chd 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +30 -0
- data/chd.gemspec +29 -0
- data/ext/chd.c +1008 -0
- data/ext/extconf.rb +60 -0
- data/lib/chd/cd.rb +272 -0
- data/lib/chd/metadata.rb +196 -0
- data/lib/chd/version.rb +4 -0
- data/lib/chd.rb +21 -0
- data/libchdr/CMakeLists.txt +104 -0
- data/libchdr/LICENSE.txt +24 -0
- data/libchdr/README.md +7 -0
- data/libchdr/deps/lzma-19.00/CMakeLists.txt +33 -0
- data/libchdr/deps/lzma-19.00/LICENSE +3 -0
- data/libchdr/deps/lzma-19.00/include/7zTypes.h +375 -0
- data/libchdr/deps/lzma-19.00/include/Alloc.h +51 -0
- data/libchdr/deps/lzma-19.00/include/Bra.h +64 -0
- data/libchdr/deps/lzma-19.00/include/Compiler.h +33 -0
- data/libchdr/deps/lzma-19.00/include/CpuArch.h +336 -0
- data/libchdr/deps/lzma-19.00/include/Delta.h +19 -0
- data/libchdr/deps/lzma-19.00/include/LzFind.h +121 -0
- data/libchdr/deps/lzma-19.00/include/LzHash.h +57 -0
- data/libchdr/deps/lzma-19.00/include/Lzma86.h +111 -0
- data/libchdr/deps/lzma-19.00/include/LzmaDec.h +234 -0
- data/libchdr/deps/lzma-19.00/include/LzmaEnc.h +76 -0
- data/libchdr/deps/lzma-19.00/include/LzmaLib.h +131 -0
- data/libchdr/deps/lzma-19.00/include/Precomp.h +10 -0
- data/libchdr/deps/lzma-19.00/include/Sort.h +18 -0
- data/libchdr/deps/lzma-19.00/lzma-history.txt +446 -0
- data/libchdr/deps/lzma-19.00/lzma.txt +328 -0
- data/libchdr/deps/lzma-19.00/lzma.vcxproj +543 -0
- data/libchdr/deps/lzma-19.00/lzma.vcxproj.filters +17 -0
- data/libchdr/deps/lzma-19.00/src/Alloc.c +455 -0
- data/libchdr/deps/lzma-19.00/src/Bra86.c +82 -0
- data/libchdr/deps/lzma-19.00/src/BraIA64.c +53 -0
- data/libchdr/deps/lzma-19.00/src/CpuArch.c +218 -0
- data/libchdr/deps/lzma-19.00/src/Delta.c +64 -0
- data/libchdr/deps/lzma-19.00/src/LzFind.c +1127 -0
- data/libchdr/deps/lzma-19.00/src/Lzma86Dec.c +54 -0
- data/libchdr/deps/lzma-19.00/src/LzmaDec.c +1185 -0
- data/libchdr/deps/lzma-19.00/src/LzmaEnc.c +1330 -0
- data/libchdr/deps/lzma-19.00/src/Sort.c +141 -0
- data/libchdr/deps/zlib-1.2.11/CMakeLists.txt +29 -0
- data/libchdr/deps/zlib-1.2.11/ChangeLog +1515 -0
- data/libchdr/deps/zlib-1.2.11/FAQ +368 -0
- data/libchdr/deps/zlib-1.2.11/INDEX +68 -0
- data/libchdr/deps/zlib-1.2.11/Makefile +5 -0
- data/libchdr/deps/zlib-1.2.11/Makefile.in +410 -0
- data/libchdr/deps/zlib-1.2.11/README +115 -0
- data/libchdr/deps/zlib-1.2.11/adler32.c +186 -0
- data/libchdr/deps/zlib-1.2.11/compress.c +86 -0
- data/libchdr/deps/zlib-1.2.11/configure +921 -0
- data/libchdr/deps/zlib-1.2.11/crc32.c +442 -0
- data/libchdr/deps/zlib-1.2.11/crc32.h +441 -0
- data/libchdr/deps/zlib-1.2.11/deflate.c +2163 -0
- data/libchdr/deps/zlib-1.2.11/deflate.h +349 -0
- data/libchdr/deps/zlib-1.2.11/doc/algorithm.txt +209 -0
- data/libchdr/deps/zlib-1.2.11/doc/rfc1950.txt +619 -0
- data/libchdr/deps/zlib-1.2.11/doc/rfc1951.txt +955 -0
- data/libchdr/deps/zlib-1.2.11/doc/rfc1952.txt +675 -0
- data/libchdr/deps/zlib-1.2.11/doc/txtvsbin.txt +107 -0
- data/libchdr/deps/zlib-1.2.11/gzclose.c +25 -0
- data/libchdr/deps/zlib-1.2.11/gzguts.h +218 -0
- data/libchdr/deps/zlib-1.2.11/gzlib.c +637 -0
- data/libchdr/deps/zlib-1.2.11/gzread.c +654 -0
- data/libchdr/deps/zlib-1.2.11/gzwrite.c +665 -0
- data/libchdr/deps/zlib-1.2.11/infback.c +640 -0
- data/libchdr/deps/zlib-1.2.11/inffast.c +323 -0
- data/libchdr/deps/zlib-1.2.11/inffast.h +11 -0
- data/libchdr/deps/zlib-1.2.11/inffixed.h +94 -0
- data/libchdr/deps/zlib-1.2.11/inflate.c +1561 -0
- data/libchdr/deps/zlib-1.2.11/inflate.h +125 -0
- data/libchdr/deps/zlib-1.2.11/inftrees.c +304 -0
- data/libchdr/deps/zlib-1.2.11/inftrees.h +62 -0
- data/libchdr/deps/zlib-1.2.11/make_vms.com +867 -0
- data/libchdr/deps/zlib-1.2.11/treebuild.xml +116 -0
- data/libchdr/deps/zlib-1.2.11/trees.c +1203 -0
- data/libchdr/deps/zlib-1.2.11/trees.h +128 -0
- data/libchdr/deps/zlib-1.2.11/uncompr.c +93 -0
- data/libchdr/deps/zlib-1.2.11/zconf.h +534 -0
- data/libchdr/deps/zlib-1.2.11/zconf.h.cmakein +536 -0
- data/libchdr/deps/zlib-1.2.11/zconf.h.in +534 -0
- data/libchdr/deps/zlib-1.2.11/zlib.3 +149 -0
- data/libchdr/deps/zlib-1.2.11/zlib.3.pdf +0 -0
- data/libchdr/deps/zlib-1.2.11/zlib.h +1912 -0
- data/libchdr/deps/zlib-1.2.11/zlib.map +94 -0
- data/libchdr/deps/zlib-1.2.11/zlib.pc.cmakein +13 -0
- data/libchdr/deps/zlib-1.2.11/zlib.pc.in +13 -0
- data/libchdr/deps/zlib-1.2.11/zlib2ansi +152 -0
- data/libchdr/deps/zlib-1.2.11/zutil.c +325 -0
- data/libchdr/deps/zlib-1.2.11/zutil.h +271 -0
- data/libchdr/include/dr_libs/dr_flac.h +12280 -0
- data/libchdr/include/libchdr/bitstream.h +43 -0
- data/libchdr/include/libchdr/cdrom.h +110 -0
- data/libchdr/include/libchdr/chd.h +427 -0
- data/libchdr/include/libchdr/chdconfig.h +10 -0
- data/libchdr/include/libchdr/coretypes.h +60 -0
- data/libchdr/include/libchdr/flac.h +50 -0
- data/libchdr/include/libchdr/huffman.h +90 -0
- data/libchdr/pkg-config.pc.in +10 -0
- data/libchdr/src/libchdr_bitstream.c +125 -0
- data/libchdr/src/libchdr_cdrom.c +415 -0
- data/libchdr/src/libchdr_chd.c +2744 -0
- data/libchdr/src/libchdr_flac.c +302 -0
- data/libchdr/src/libchdr_huffman.c +545 -0
- data/libchdr/src/link.T +5 -0
- data/libchdr/tests/CMakeLists.txt +2 -0
- data/libchdr/tests/benchmark.c +52 -0
- 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
|
+
|
data/lib/chd/metadata.rb
ADDED
@@ -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
|
data/lib/chd/version.rb
ADDED
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)
|
data/libchdr/LICENSE.txt
ADDED
@@ -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/)).
|