chd 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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/)).