chd 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +30 -0
  3. data/chd.gemspec +29 -0
  4. data/ext/chd.c +1008 -0
  5. data/ext/extconf.rb +60 -0
  6. data/lib/chd/cd.rb +272 -0
  7. data/lib/chd/metadata.rb +196 -0
  8. data/lib/chd/version.rb +4 -0
  9. data/lib/chd.rb +21 -0
  10. data/libchdr/CMakeLists.txt +104 -0
  11. data/libchdr/LICENSE.txt +24 -0
  12. data/libchdr/README.md +7 -0
  13. data/libchdr/deps/lzma-19.00/CMakeLists.txt +33 -0
  14. data/libchdr/deps/lzma-19.00/LICENSE +3 -0
  15. data/libchdr/deps/lzma-19.00/include/7zTypes.h +375 -0
  16. data/libchdr/deps/lzma-19.00/include/Alloc.h +51 -0
  17. data/libchdr/deps/lzma-19.00/include/Bra.h +64 -0
  18. data/libchdr/deps/lzma-19.00/include/Compiler.h +33 -0
  19. data/libchdr/deps/lzma-19.00/include/CpuArch.h +336 -0
  20. data/libchdr/deps/lzma-19.00/include/Delta.h +19 -0
  21. data/libchdr/deps/lzma-19.00/include/LzFind.h +121 -0
  22. data/libchdr/deps/lzma-19.00/include/LzHash.h +57 -0
  23. data/libchdr/deps/lzma-19.00/include/Lzma86.h +111 -0
  24. data/libchdr/deps/lzma-19.00/include/LzmaDec.h +234 -0
  25. data/libchdr/deps/lzma-19.00/include/LzmaEnc.h +76 -0
  26. data/libchdr/deps/lzma-19.00/include/LzmaLib.h +131 -0
  27. data/libchdr/deps/lzma-19.00/include/Precomp.h +10 -0
  28. data/libchdr/deps/lzma-19.00/include/Sort.h +18 -0
  29. data/libchdr/deps/lzma-19.00/lzma-history.txt +446 -0
  30. data/libchdr/deps/lzma-19.00/lzma.txt +328 -0
  31. data/libchdr/deps/lzma-19.00/lzma.vcxproj +543 -0
  32. data/libchdr/deps/lzma-19.00/lzma.vcxproj.filters +17 -0
  33. data/libchdr/deps/lzma-19.00/src/Alloc.c +455 -0
  34. data/libchdr/deps/lzma-19.00/src/Bra86.c +82 -0
  35. data/libchdr/deps/lzma-19.00/src/BraIA64.c +53 -0
  36. data/libchdr/deps/lzma-19.00/src/CpuArch.c +218 -0
  37. data/libchdr/deps/lzma-19.00/src/Delta.c +64 -0
  38. data/libchdr/deps/lzma-19.00/src/LzFind.c +1127 -0
  39. data/libchdr/deps/lzma-19.00/src/Lzma86Dec.c +54 -0
  40. data/libchdr/deps/lzma-19.00/src/LzmaDec.c +1185 -0
  41. data/libchdr/deps/lzma-19.00/src/LzmaEnc.c +1330 -0
  42. data/libchdr/deps/lzma-19.00/src/Sort.c +141 -0
  43. data/libchdr/deps/zlib-1.2.11/CMakeLists.txt +29 -0
  44. data/libchdr/deps/zlib-1.2.11/ChangeLog +1515 -0
  45. data/libchdr/deps/zlib-1.2.11/FAQ +368 -0
  46. data/libchdr/deps/zlib-1.2.11/INDEX +68 -0
  47. data/libchdr/deps/zlib-1.2.11/Makefile +5 -0
  48. data/libchdr/deps/zlib-1.2.11/Makefile.in +410 -0
  49. data/libchdr/deps/zlib-1.2.11/README +115 -0
  50. data/libchdr/deps/zlib-1.2.11/adler32.c +186 -0
  51. data/libchdr/deps/zlib-1.2.11/compress.c +86 -0
  52. data/libchdr/deps/zlib-1.2.11/configure +921 -0
  53. data/libchdr/deps/zlib-1.2.11/crc32.c +442 -0
  54. data/libchdr/deps/zlib-1.2.11/crc32.h +441 -0
  55. data/libchdr/deps/zlib-1.2.11/deflate.c +2163 -0
  56. data/libchdr/deps/zlib-1.2.11/deflate.h +349 -0
  57. data/libchdr/deps/zlib-1.2.11/doc/algorithm.txt +209 -0
  58. data/libchdr/deps/zlib-1.2.11/doc/rfc1950.txt +619 -0
  59. data/libchdr/deps/zlib-1.2.11/doc/rfc1951.txt +955 -0
  60. data/libchdr/deps/zlib-1.2.11/doc/rfc1952.txt +675 -0
  61. data/libchdr/deps/zlib-1.2.11/doc/txtvsbin.txt +107 -0
  62. data/libchdr/deps/zlib-1.2.11/gzclose.c +25 -0
  63. data/libchdr/deps/zlib-1.2.11/gzguts.h +218 -0
  64. data/libchdr/deps/zlib-1.2.11/gzlib.c +637 -0
  65. data/libchdr/deps/zlib-1.2.11/gzread.c +654 -0
  66. data/libchdr/deps/zlib-1.2.11/gzwrite.c +665 -0
  67. data/libchdr/deps/zlib-1.2.11/infback.c +640 -0
  68. data/libchdr/deps/zlib-1.2.11/inffast.c +323 -0
  69. data/libchdr/deps/zlib-1.2.11/inffast.h +11 -0
  70. data/libchdr/deps/zlib-1.2.11/inffixed.h +94 -0
  71. data/libchdr/deps/zlib-1.2.11/inflate.c +1561 -0
  72. data/libchdr/deps/zlib-1.2.11/inflate.h +125 -0
  73. data/libchdr/deps/zlib-1.2.11/inftrees.c +304 -0
  74. data/libchdr/deps/zlib-1.2.11/inftrees.h +62 -0
  75. data/libchdr/deps/zlib-1.2.11/make_vms.com +867 -0
  76. data/libchdr/deps/zlib-1.2.11/treebuild.xml +116 -0
  77. data/libchdr/deps/zlib-1.2.11/trees.c +1203 -0
  78. data/libchdr/deps/zlib-1.2.11/trees.h +128 -0
  79. data/libchdr/deps/zlib-1.2.11/uncompr.c +93 -0
  80. data/libchdr/deps/zlib-1.2.11/zconf.h +534 -0
  81. data/libchdr/deps/zlib-1.2.11/zconf.h.cmakein +536 -0
  82. data/libchdr/deps/zlib-1.2.11/zconf.h.in +534 -0
  83. data/libchdr/deps/zlib-1.2.11/zlib.3 +149 -0
  84. data/libchdr/deps/zlib-1.2.11/zlib.3.pdf +0 -0
  85. data/libchdr/deps/zlib-1.2.11/zlib.h +1912 -0
  86. data/libchdr/deps/zlib-1.2.11/zlib.map +94 -0
  87. data/libchdr/deps/zlib-1.2.11/zlib.pc.cmakein +13 -0
  88. data/libchdr/deps/zlib-1.2.11/zlib.pc.in +13 -0
  89. data/libchdr/deps/zlib-1.2.11/zlib2ansi +152 -0
  90. data/libchdr/deps/zlib-1.2.11/zutil.c +325 -0
  91. data/libchdr/deps/zlib-1.2.11/zutil.h +271 -0
  92. data/libchdr/include/dr_libs/dr_flac.h +12280 -0
  93. data/libchdr/include/libchdr/bitstream.h +43 -0
  94. data/libchdr/include/libchdr/cdrom.h +110 -0
  95. data/libchdr/include/libchdr/chd.h +427 -0
  96. data/libchdr/include/libchdr/chdconfig.h +10 -0
  97. data/libchdr/include/libchdr/coretypes.h +60 -0
  98. data/libchdr/include/libchdr/flac.h +50 -0
  99. data/libchdr/include/libchdr/huffman.h +90 -0
  100. data/libchdr/pkg-config.pc.in +10 -0
  101. data/libchdr/src/libchdr_bitstream.c +125 -0
  102. data/libchdr/src/libchdr_cdrom.c +415 -0
  103. data/libchdr/src/libchdr_chd.c +2744 -0
  104. data/libchdr/src/libchdr_flac.c +302 -0
  105. data/libchdr/src/libchdr_huffman.c +545 -0
  106. data/libchdr/src/link.T +5 -0
  107. data/libchdr/tests/CMakeLists.txt +2 -0
  108. data/libchdr/tests/benchmark.c +52 -0
  109. metadata +183 -0
data/ext/extconf.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'mkmf'
2
+ require 'set'
3
+
4
+ ROOT = File.join(__dir__, '..')
5
+
6
+ def with_success(&block)
7
+ state = [ $CFLAGS, $LDFLAGS, $libs ]
8
+ block.call.tap {|success|
9
+ $CFLAGS, $LDFLAGS, $libs = state unless success
10
+ }
11
+ end
12
+
13
+
14
+ build = Set[]
15
+
16
+ cmake = find_executable('cmake')
17
+
18
+ unless pkg_config('libchdr').tap {|found|
19
+ puts "found pkg-config for libchdr" if found
20
+ } ||
21
+ with_success do
22
+ find_library('chdr', 'chd_open') & find_header('libchdr/chd.h')
23
+ end
24
+
25
+ $CFLAGS += " -I " + File.join(ROOT, 'libchdr', 'include')
26
+
27
+ $libs += [ 'deps/zlib-1.2.11/libzlib.a',
28
+ 'deps/lzma-19.00/liblzma.a',
29
+ 'CMakeFiles/chdr.dir/src/libchdr_chd.c.o',
30
+ 'CMakeFiles/chdr.dir/src/libchdr_flac.c.o',
31
+ 'CMakeFiles/chdr.dir/src/libchdr_bitstream.c.o',
32
+ 'CMakeFiles/chdr.dir/src/libchdr_huffman.c.o',
33
+ 'CMakeFiles/chdr.dir/src/libchdr_cdrom.c.o',
34
+ ].map {|l|
35
+ ' ' + File.join(ROOT, 'build', l)
36
+ }.join
37
+
38
+ # $libs += [ 'libchdr-static.a', 'liblzma.a', 'libzlib.a' ].map {|l|
39
+ # ' ' + File.join(ROOT, 'stage', 'lib', l)
40
+ # }.join(' ')
41
+
42
+ build.add(:libchdr)
43
+ end
44
+
45
+ create_makefile('chd/core')
46
+
47
+
48
+ if build.include?(:libchdr)
49
+ puts "Building bundled libchdr"
50
+
51
+ Dir.chdir(ROOT) do
52
+ unless cmake &&
53
+ system(cmake, '-DINSTALL_STATIC_LIBS=on',
54
+ '-S', 'libchdr', '-B', 'build') &&
55
+ system(cmake, '--build', 'build')
56
+ abort "failed building libchdr"
57
+ end
58
+ end
59
+ end
60
+
data/lib/chd/cd.rb ADDED
@@ -0,0 +1,272 @@
1
+ require 'set'
2
+ require 'digest'
3
+
4
+ class CHD
5
+
6
+ #
7
+ # Access a CD-ROM / GD-ROM in Mame CHD format
8
+ #
9
+ class CD
10
+ # Maximum number of tracks in a CD-ROM
11
+ MAX_TRACKS = 99
12
+
13
+ # Maximum sector size
14
+ MAX_SECTOR_DATASIZE = 2352
15
+
16
+ # Maximum subcode size
17
+ MAX_SUBCODE_DATASIZE = 96
18
+
19
+ # Maximum frame size
20
+ FRAME_SIZE = MAX_SUBCODE_DATASIZE + MAX_SECTOR_DATASIZE
21
+
22
+ # @!visibility private
23
+ TRACK_PADDING = 4
24
+
25
+ # Various sector data size according to track type
26
+ TRACK_TYPE_DATASIZE = {
27
+ :MODE1 => 2048,
28
+ :MODE1_RAW => 2352,
29
+ :MODE2 => 2336,
30
+ :MODE2_FORM1 => 2048,
31
+ :MODE2_FORM2 => 2324,
32
+ :MODE2_FORM_MIX => 2336,
33
+ :MODE2_RAW => 2352,
34
+ :AUDIO => 2352,
35
+ }.freeze
36
+
37
+ # Various subcode data size according to track type
38
+ TRACK_SUBTYPE_DATASIZE = {
39
+ :NONE => 0,
40
+ :NORMAL => 96,
41
+ :RAW => 96,
42
+ }.freeze
43
+
44
+ private
45
+
46
+ SYNCBYTES = [ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
47
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
48
+ ].pack('C*').freeze
49
+
50
+ public
51
+
52
+ # Read the TOC, and returns it's information in a parsed form.
53
+ #
54
+ # @param chd [CHD] a chd opened file
55
+ #
56
+ # @return [Array<Hash{Symbol => Object}>] Table of Content
57
+ # @return [nil] if the CHD file is not of a CD-ROM / GD-ROM type
58
+ #
59
+ def self.read_toc(chd)
60
+ return nil if chd.hunk_bytes % FRAME_SIZE != 0 ||
61
+ chd.unit_bytes != FRAME_SIZE
62
+
63
+ flags = Set.new
64
+ tracks = []
65
+ while (idx = tracks.size) < MAX_TRACKS do
66
+ if md = chd.get_metadata(idx, Metadata::CDROM_TRACK, )
67
+ elsif md = chd.get_metadata(idx, Metadata::CDROM_TRACK_PREGAP)
68
+ elsif md = chd.get_metadata(idx, Metadata::GDROM_OLD, )
69
+ raise NotSupported, "upgrade your CHD to a more recent version"
70
+ elsif md = chd.get_metadata(idx, Metadata::GDROM_TRACK, )
71
+ flags << :GDROM
72
+ else
73
+ break
74
+ end
75
+ tracks << Metadata.parse(*md)
76
+ end
77
+
78
+ if ! tracks.empty?
79
+ unless tracks.each_with_index.all? {|trackinfo, index|
80
+ trackinfo[:track] == index + 1
81
+ }
82
+ raise ParsingError, "unordered tracks"
83
+ end
84
+ [ tracks, flags ]
85
+ elsif chd.get_metadata(0, Metadata::CDROM_OLD)
86
+ raise NotSupported, "upgrade your CHD to a more recent version"
87
+ else
88
+ raise NotFoundError, "provided CHD is not a CD-ROM"
89
+ end
90
+ end
91
+
92
+
93
+ def initialize(chd)
94
+ @chd = chd
95
+ @toc, @flags = CD.read_toc(chd)
96
+
97
+ # Build mapping
98
+ chdofs = physofs = logofs = 0
99
+ @mapping = @toc.map {|trackinfo|
100
+ { :physframeofs => physofs,
101
+ :chdframeofs => chdofs,
102
+ :logframeofs => trackinfo[:pregap] + logofs,
103
+ :logframes => trackinfo[:frames] - trackinfo[:pregap],
104
+ }.tap {
105
+ logofs += trackinfo[:pregap] if trackinfo[:pgdatasize].zero?
106
+ logofs += trackinfo[:frames] + trackinfo[:postgap]
107
+ physofs += trackinfo[:frames]
108
+ chdofs += trackinfo[:frames] + trackinfo[:extraframes]
109
+ }
110
+ }
111
+ @mapping << { :physframeofs => physofs,
112
+ :logframeofs => logofs,
113
+ :chdframeofs => chdofs,
114
+ :logframes => 0,
115
+ }
116
+ end
117
+
118
+
119
+ # Table of Content
120
+ #
121
+ # @return [Array<Hash{Symbol => Object}>]
122
+ #
123
+ attr_reader :toc
124
+
125
+ # Get the frame number that the track starts at.
126
+ #
127
+ # @param track [Integer] track number (start at 1)
128
+ #
129
+ # @return [Integer] frame number
130
+ #
131
+ def track_start(track, phys = false)
132
+ frame_ofs_type = phys ? :physframeofs : :logframeofs
133
+
134
+ # handle lead-out specially
135
+ if track == 0xAA
136
+ @mapping.last[frame_ofs_type]
137
+ elsif ! (1 .. @toc.size).include?(track)
138
+ raise RangeError, "track must be in 1..#{@toc.size}"
139
+ else
140
+ @mappging.dig(track - 1, frame_ofs_type)
141
+ end
142
+ end
143
+
144
+ # Read one or more sectors from a CD-ROM
145
+ #
146
+ # @param lbasector [Integer] sector number
147
+ # @param datatype [Symbol] type of data
148
+ #
149
+ # @return [String]
150
+ #
151
+ def read_sector(lbasector, datatype = nil, phys = false)
152
+ # Compute CHD sector and track index
153
+ frame_ofs_type = phys ? :physframeofs : :logframeofs
154
+ chdsector = lbasector
155
+ trackidx = 0
156
+ @mapping.each_cons(2).with_index do |(cur, nxt), idx|
157
+ if lbasector < nxt[frame_ofs_type]
158
+ chdsector = lbasector - cur[frame_ofs_type] + cur[:chdframeofs]
159
+ trackidx = idx
160
+ break
161
+ end
162
+ end
163
+
164
+
165
+ trackinfo = @toc[trackidx];
166
+ tracktype = trackinfo[:trktype]
167
+
168
+ offset, length, header =
169
+ # return same type or don't care
170
+ if (datatype == tracktype) || datatype.nil?
171
+ [ 0, trackinfo[:datasize] ]
172
+
173
+ # return 2048 bytes of MODE1 data
174
+ # from a 2352 byte MODE1 RAW sector
175
+ elsif (datatype == :MODE1 ) &&
176
+ (tracktype == :MODE1_RAW)
177
+ [ 16, 2048 ]
178
+
179
+ # return 2352 byte MODE1 RAW sector
180
+ # from 2048 bytes of MODE1 data
181
+ elsif (datatype == :MODE1_RAW) &&
182
+ (tracktype == :MODE1 )
183
+ warn "promotion of MODE1/FORM1 sector to MODE1 RAW is incomplete"
184
+ m, sf = lba.divmod(60 * 75);
185
+ s, f = sf.divmod(75)
186
+ hdr = SYNCBYTES + [
187
+ ((m / 10) << 4) | ((m % 10) << 0), # M
188
+ ((s / 10) << 4) | ((s % 10) << 0), # S
189
+ ((f / 10) << 4) | ((f % 10) << 0), # F
190
+ 1 # MODE1
191
+ ].pack('C*') # MSF + MODE1
192
+
193
+ [ 0, 2048, hdr ]
194
+
195
+ # return 2048 bytes of MODE1 data
196
+ # from a MODE2 FORM1 or RAW sector
197
+ elsif (datatype == :MODE1 ) &&
198
+ ((tracktype == :MODE2_FORM1) || (tracktype == :MODE2_RAW ))
199
+ [ 24, 2048 ]
200
+
201
+ # return 2048 bytes of MODE1 data
202
+ # from a MODE2 FORM2 or XA sector
203
+ elsif (datatype == :MODE1 ) &&
204
+ (tracktype == :MODE2_FORM_MIX)
205
+ [ 8, 2048 ]
206
+
207
+ # return MODE2 2336 byte data
208
+ # from a 2352 byte MODE1 or MODE2 RAW sector (skip the header)
209
+ elsif (datatype == :MODE2) &&
210
+ ((tracktype == :MODE1_RAW) || (tracktype == :MODE2_RAW))
211
+ [ 16, 2336 ]
212
+
213
+ # Not supported
214
+ else
215
+ raise NotSupported,
216
+ "conversion from type %s to type %s not supported" % [
217
+ tracktype, datatype ]
218
+ end
219
+
220
+ # Read data
221
+ unless phys
222
+ if ! trackinfo[:pgdatasize].zero?
223
+ # chdman (phys=true) relies on chdframeofs to point
224
+ # to index 0 instead of index 1 for extractcd.
225
+ # Actually playing CDs requires it to point
226
+ # to index 1 instead of index 0,
227
+ # so adjust the offset when phys=false.
228
+ chdsector += trackinfo[:pregap]
229
+ elsif lbasector < trackinfo[:logframeofs]
230
+ # if this is pregap info that isn't actually in the file,
231
+ # just return blank data
232
+ return '\0' * length
233
+ end
234
+ end
235
+
236
+ data = @chd.read_bytes(chdsector * FRAME_SIZE + offset, length)
237
+ data = header + data if header
238
+ data
239
+ end
240
+
241
+
242
+ private
243
+
244
+
245
+ def _enhance_toc(toc)
246
+ chd_offset = physical_offset = logical_offset = 0
247
+
248
+ toc.each do |trackinfo|
249
+ trackinfo[:logframeofs] = 0
250
+
251
+ if trackinfo[:pgdatasize].zero?
252
+ logical_offset += trackinfo[:pregap]
253
+ else
254
+ trackinfo[:logframeofs] = trackinfo[:pregap]
255
+ end
256
+
257
+ trackinfo[:physframeofs] = physical_offset
258
+ trackinfo[:chdframeofs ] = chd_offset
259
+ trackinfo[:logframeofs ] += logical_offset
260
+ trackinfo[:logframes ] = trackinfo[:frames] - trackinfo[:pregap]
261
+
262
+ logical_offset += trackinfo[:frames] + trackinfo[:postgap]
263
+ physical_offset += trackinfo[:frames]
264
+ chd_offset += trackinfo[:frames] + trackinfo[:extraframes]
265
+ end
266
+
267
+ toc
268
+ end
269
+
270
+ end
271
+ end
272
+
@@ -0,0 +1,196 @@
1
+ require 'set'
2
+
3
+ class CHD
4
+
5
+ #
6
+ # Parsing of the metadata failed.
7
+ #
8
+ class ParsingError < Error
9
+ end
10
+
11
+ #
12
+ #
13
+ # | Type | Associated scanf string |
14
+ # |------|----------------------------------------------------------------------------------------|
15
+ # | GDDD | "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d" |
16
+ # | CHTR | "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d" |
17
+ # | CHT2 | "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" |
18
+ # | CHGD | "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" |
19
+ # | AVAV | "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d" |
20
+ #
21
+ module Metadata
22
+ HARD_DISK = :'GDDD' # Hard disk
23
+ HARD_DISK_IDENT = :'IDNT' # Hard disk identity
24
+ HARD_DISK_KEY = :'KEY ' # Hard disk key
25
+ PCMCIA_CIS = :'CIS ' # PCMCIA CIS
26
+ CDROM_TRACK = :'CHTR' # CDROM Track
27
+ CDROM_TRACK_PREGAP = :'CHT2' # CDROM Track with pregap
28
+ GDROM_TRACK = :'CHGD' # GDROM Track (SEGA Genesis)
29
+ AV = :'AVAV' # Audio/Video
30
+ AV_LD = :'AVLD' # Audio/Video Laser Disk
31
+ CDROM_OLD = :'CHCD' # CDROM Track (old CHD version)
32
+ GDROM_OLD = :'CHGT' # GDROM Track (old CHD version)
33
+
34
+ private
35
+
36
+ HARD_DISK_REGEX = /\A CYLS: (?<cyls>%d) ,
37
+ HEADS: (?<heads>%d) ,
38
+ SECS: (?<secs>%d) ,
39
+ BPS: (?<bps>)%d
40
+ \z /x
41
+ CDROM_TRACK_REGEX = /\A TRACK: (?<track>\d+) \s+
42
+ TYPE: (?<trktype>\w+) \s+
43
+ SUBTYPE: (?<subtype>\w+) \s+
44
+ FRAMES: (?<frames>\d+)
45
+ \z /x
46
+ CDROM_TRACK_PREGAP_REGEX = /\A TRACK: (?<track>\d+) \s+
47
+ TYPE: (?<trktype>\w+) \s+
48
+ SUBTYPE: (?<subtype>\w+) \s+
49
+ FRAMES: (?<frames>\d+) \s+
50
+ PREGAP: (?<pregap>\d+) \s+
51
+ PGTYPE: V?(?<pgtype>\w+) \s+
52
+ PGSUB: (?<pgsub>\w+) \s+
53
+ POSTGAP: (?<postgap>\d+)
54
+ \z /x
55
+ GDROM_TRACK_REGEX = /\A TRACK: (?<track>\d+) \s+
56
+ TYPE: (?<trktype>\w+) \s+
57
+ SUBTYPE: (?<subtype>\w+) \s+
58
+ FRAMES: (?<frames>\d+) \s+
59
+ PAD: (?<padframes>\d+) \s+
60
+ PREGAP: (?<pregap>\d+) \s+
61
+ PGTYPE: V?(?<pgtype>\w+) \s+
62
+ PGSUB: (?<pgsub>\w+) \s+
63
+ POSTGAP: (?<postgap>\d+)
64
+ \z /x
65
+ AV_REGEX = /\A FPS: (?<fps>\d+\.\d+) \s+
66
+ WIDTH: (?<width>\d+) \s+
67
+ HEIGHT: (?<height>\d+) \s+
68
+ INTERLACED:(?<interlaced>\d+) \s+
69
+ CHANNELS: (?<channels>\d+) \s+
70
+ SAMPLERATE:(?<samplerate>\d+)
71
+ \z /x
72
+
73
+
74
+ CD_TRACK_TYPES = {
75
+ 'MODE1' => :MODE1,
76
+ 'MODE1/2048' => :MODE1,
77
+ 'MODE1_RAW' => :MODE1_RAW,
78
+ 'MODE1/2352' => :MODE1_RAW,
79
+ 'MODE2' => :MODE2,
80
+ 'MODE2/2336' => :MODE2,
81
+ 'MODE2_FORM1' => :MODE2_FORM1,
82
+ 'MODE2/2048' => :MODE2_FORM1,
83
+ 'MODE2_FORM2' => :MODE2_FORM2,
84
+ 'MODE2/2324' => :MODE2_FORM2,
85
+ 'MODE2_FORM_MIX' => :MODE2_FORM_MIX,
86
+ 'MODE2/2336' => :MODE2_FORM_MIX,
87
+ 'MODE2_RAW' => :MODE2_RAW,
88
+ 'MODE2/2352' => :MODE2_RAW,
89
+ 'AUDIO' => :AUDIO,
90
+ }
91
+
92
+ CD_TRACK_SUBTYPES = {
93
+ 'NONE' => :NONE,
94
+ 'RW' => :NORMAL,
95
+ 'RW_RAW' => :RAW,
96
+ }
97
+
98
+
99
+
100
+ public
101
+
102
+ # Parse metadata returned by {CHD#get_metadata}
103
+ #
104
+ # @param data [String]
105
+ # @param flags [Integer]
106
+ # @param type [Symbol]
107
+ #
108
+ # @return [Hash{Symbol => Object}]
109
+ #
110
+ # @example
111
+ # chd = CHD.new('file.chd')
112
+ # puts CHD::Metadata.parse(*chd.get_metadata(0)).inspect
113
+ #
114
+ def self.parse(data, flags, type)
115
+ # Flags
116
+ unless (flags & ~(METADATA_FLAG_CHECKSUM)).zero?
117
+ raise ParsingError,
118
+ "unsupported flag (0x#{flags.to_s(16)}) (fill bug report)"
119
+ end
120
+ set_flags = Set.new
121
+ set_flags << :checksum unless (flags & METADATA_FLAG_CHECKSUM).zero?
122
+
123
+ # Data
124
+ case type
125
+ when CDROM_TRACK then parse_cdrom_track(data, CDROM_TRACK_REGEX)
126
+ when CDROM_TRACK_PREGAP then parse_cdrom_track(data, CDROM_TRACK_PREGAP_REGEX)
127
+ when GDROM_TRACK then parse_cdrom_track(data, GDROM_TRACK_REGEX)
128
+ when HARD_DISK then parse_hard_disk(data, HARD_DISK_REGEX)
129
+ else raise "not implemented yet"
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ def self.parse_cdrom_track(data, regex)
136
+ md = parse_using_regex(data, regex, :trktype => CD_TRACK_TYPES,
137
+ :subtype => CD_TRACK_SUBTYPES,
138
+ :pgtype => CD_TRACK_TYPES,
139
+ :pgsub => CD_TRACK_SUBTYPES)
140
+ dflt = { :track => nil,
141
+ :trktype => nil,
142
+ :subtype => nil,
143
+ :frames => nil,
144
+ :padframes => 0,
145
+ :pregap => 0,
146
+ :pgtype => :MODE1,
147
+ :pgsub => :NONE,
148
+ :postgap => 0,
149
+ }
150
+
151
+ md = dflt.merge(md)
152
+
153
+ if (md[:track] < 0) || (md[:track] > CD::MAX_TRACKS)
154
+ raise ParsingError, "track number out of range"
155
+ end
156
+
157
+ md.merge(:extraframes => CD::TRACK_PADDING -
158
+ md[:frames] % CD::TRACK_PADDING,
159
+ :datasize => CD::TRACK_TYPE_DATASIZE[ md[:trktype]],
160
+ :subsize => CD::TRACK_SUBTYPE_DATASIZE[md[:subtype]],
161
+ :pgdatasize => CD::TRACK_TYPE_DATASIZE[ md[:pgtype ]],
162
+ :pgsubsize => CD::TRACK_SUBTYPE_DATASIZE[md[:pgsub ]],
163
+ )
164
+ end
165
+
166
+ def self.parse_hard_disk(data, regex)
167
+ parse_using_regex(data, regex)
168
+ end
169
+
170
+ def self.parse_using_regex(data, regex, mapping = nil, &block)
171
+ unless md = regex.match(data)&.named_captures
172
+ raise ParsingError, "failed to parse track"
173
+ end
174
+
175
+ md.transform_keys!(&:to_sym)
176
+ md.transform_values! do |v|
177
+ case v
178
+ when /^\d+$/ then Integer(v)
179
+ when /^\d+\.\d+$/ then Float(v)
180
+ else v
181
+ end
182
+ end
183
+
184
+ (mapping || {}).each do |key, map|
185
+ md[key] = map.fetch(md[key]) if md.include?(key)
186
+ end
187
+
188
+ block&.call(md)
189
+
190
+ md
191
+ rescue KeyError
192
+ raise ParsingError, "unable to decode track description"
193
+ end
194
+
195
+ end
196
+ end
@@ -0,0 +1,4 @@
1
+ class CHD
2
+ # Current version of this gem/library
3
+ VERSION = '0.1.1'
4
+ end
data/lib/chd.rb ADDED
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+ require 'chd/core'
3
+ require 'chd/metadata'
4
+ require 'chd/cd'
5
+
6
+ class CHD
7
+
8
+ # Returns a string representation of the number of frames using
9
+ # minutes:seconds:frames format.
10
+ #
11
+ # @param frames [Integer]
12
+ #
13
+ # @return [String]
14
+ #
15
+ def self.msf(frames)
16
+ "%02d:%02d:%02d" % [ frames / (75 * 60), (frames / 75) % 60, frames % 75 ]
17
+ end
18
+ end
19
+
20
+
21
+
@@ -0,0 +1,104 @@
1
+ cmake_minimum_required(VERSION 3.9)
2
+
3
+ project(chdr VERSION 0.1 LANGUAGES C)
4
+
5
+ if(CMAKE_PROJECT_NAME STREQUAL "chdr")
6
+ option(BUILD_SHARED_LIBS "Build libchdr also as a shared library" ON)
7
+ endif()
8
+ option(INSTALL_STATIC_LIBS "Install static libraries" OFF)
9
+ option(WITH_SYSTEM_ZLIB "Use system provided zlib library" OFF)
10
+
11
+ option(BUILD_LTO "Compile libchdr with link-time optimization if supported" OFF)
12
+ if(BUILD_LTO)
13
+ include(CheckIPOSupported)
14
+ check_ipo_supported(RESULT HAVE_IPO)
15
+ if(HAVE_IPO)
16
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
17
+ endif()
18
+ endif()
19
+
20
+ include(GNUInstallDirs)
21
+
22
+ #--------------------------------------------------
23
+ # dependencies
24
+ #--------------------------------------------------
25
+
26
+
27
+ # lzma
28
+ add_subdirectory(deps/lzma-19.00 EXCLUDE_FROM_ALL)
29
+ list(APPEND CHDR_LIBS lzma)
30
+ list(APPEND CHDR_INCLUDES lzma)
31
+
32
+ # zlib
33
+ if (WITH_SYSTEM_ZLIB)
34
+ find_package(ZLIB REQUIRED)
35
+ list(APPEND PLATFORM_LIBS ZLIB::ZLIB)
36
+ else()
37
+ add_subdirectory(deps/zlib-1.2.11 EXCLUDE_FROM_ALL)
38
+ list(APPEND CHDR_LIBS zlib)
39
+ list(APPEND CHDR_INCLUDES zlib)
40
+ endif()
41
+
42
+ #--------------------------------------------------
43
+ # chdr
44
+ #--------------------------------------------------
45
+
46
+ set(CHDR_SOURCES
47
+ src/libchdr_bitstream.c
48
+ src/libchdr_cdrom.c
49
+ src/libchdr_chd.c
50
+ src/libchdr_flac.c
51
+ src/libchdr_huffman.c
52
+ )
53
+
54
+ list(APPEND CHDR_INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/include)
55
+
56
+ add_library(chdr-static STATIC ${CHDR_SOURCES})
57
+ target_include_directories(chdr-static PRIVATE ${CHDR_INCLUDES} PUBLIC include)
58
+ target_link_libraries(chdr-static PRIVATE ${CHDR_LIBS} ${PLATFORM_LIBS})
59
+
60
+ if(MSVC)
61
+ target_compile_definitions(chdr-static PRIVATE _CRT_SECURE_NO_WARNINGS)
62
+ endif()
63
+
64
+ if (INSTALL_STATIC_LIBS)
65
+ install(TARGETS chdr-static ${CHDR_LIBS}
66
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
67
+ )
68
+ endif()
69
+
70
+ if (BUILD_SHARED_LIBS)
71
+ add_library(chdr SHARED ${CHDR_SOURCES})
72
+ target_include_directories(chdr PRIVATE ${CHDR_INCLUDES} PUBLIC include)
73
+ target_link_libraries(chdr PRIVATE ${CHDR_LIBS} ${PLATFORM_LIBS})
74
+
75
+ if(MSVC)
76
+ target_compile_definitions(chdr PUBLIC "CHD_DLL")
77
+ target_compile_definitions(chdr PRIVATE "CHD_DLL_EXPORTS")
78
+ target_compile_definitions(chdr PRIVATE _CRT_SECURE_NO_WARNINGS)
79
+ elseif(APPLE)
80
+ target_link_libraries(chdr PRIVATE -Wl,-dead_strip -Wl,-exported_symbol,_chd_*)
81
+ else()
82
+ target_link_libraries(chdr PRIVATE -Wl,--version-script ${CMAKE_CURRENT_SOURCE_DIR}/src/link.T -Wl,--no-undefined)
83
+ endif()
84
+
85
+ set_target_properties(chdr PROPERTIES C_VISIBILITY_PRESET hidden)
86
+ set_target_properties(chdr PROPERTIES VISIBILITY_INLINES_HIDDEN 1)
87
+ set_target_properties(chdr PROPERTIES PUBLIC_HEADER "include/libchdr/bitstream.h;include/libchdr/cdrom.h;include/libchdr/chd.h;include/libchdr/chdconfig.h;include/libchdr/coretypes.h;include/libchdr/flac.h;include/libchdr/huffman.h")
88
+ set_target_properties(chdr PROPERTIES VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" SOVERSION ${PROJECT_VERSION_MAJOR})
89
+
90
+ if (CMAKE_BUILD_TYPE MATCHES Release)
91
+ #add_custom_command(TARGET chdr POST_BUILD COMMAND ${CMAKE_STRIP} libchdr.so)
92
+ endif (CMAKE_BUILD_TYPE MATCHES Release)
93
+
94
+ install(TARGETS chdr
95
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
96
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
97
+ PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libchdr"
98
+ )
99
+
100
+ configure_file(pkg-config.pc.in ${CMAKE_BINARY_DIR}/libchdr.pc @ONLY)
101
+ install(FILES ${CMAKE_BINARY_DIR}/libchdr.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
102
+ endif()
103
+
104
+ add_subdirectory(tests)
@@ -0,0 +1,24 @@
1
+ Copyright Romain Tisserand
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of the <organization> nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/libchdr/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # libchdr
2
+
3
+ libchdr is a standalone library for reading MAME's CHDv1-v5 formats.
4
+
5
+ The code is based off of MAME's old C codebase which read up to CHDv4 with OS-dependent features removed, and CHDv5 support backported from MAME's current C++ codebase.
6
+
7
+ libchdr is licensed under the BSD 3-Clause (see [LICENSE.txt](LICENSE.txt)) and uses third party libraries that are each distributed under their own terms (see each library's license in [deps/](deps/)).