From 7702069032974240d5a13aab63669933285dbd2b Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 18 Jul 2023 16:49:11 +1000 Subject: [PATCH] Add cue and ccd parsers Switch bchunk.py to use the cue parser in cue.py Signed-off-by: Ronnie Sahlberg --- README | 13 +-- bchunk.py | 153 ++++---------------------- cue.py | 292 ++++++++++++++++++++++++++++++++++++++++++++++++++ pop-fe-ps3.py | 17 ++- pop-fe-psp.py | 17 ++- pop-fe.py | 47 +++++--- 6 files changed, 368 insertions(+), 171 deletions(-) create mode 100755 cue.py diff --git a/README b/README index 35678fb..88fedb7 100644 --- a/README +++ b/README @@ -13,11 +13,11 @@ TL;DR For Linux and windows. Connect PSP to your Linux/Windows box via USB, then run: - $ ./pop-fe.py --psp-dir=auto /psx/Grandia1of2.img /psx/Grandia2of2.img + $ ./pop-fe.py --psp-dir=auto /psx/Grandia1of2.cue /psx/Grandia2of2.cue Or create a PS3 PKG: - $ ./pop-fe.py --ps3-pkg=Grandia.pkg /psx/Grandia1of2.img /psx/Grandia2of2.img + $ ./pop-fe.py --ps3-pkg=Grandia.pkg /psx/Grandia1of2.ccd /psx/Grandia2of2.ccd or ... @@ -48,6 +48,7 @@ formats: from the content of the cue file. If the file-name found inside the cue is a relative path it is assumed that the bin/img file is stored in the same directory as the cue file. +.ccd : CCD file. Will be converted to a temporary CUE file before it is used. .bin : BIN/IMG files. In this case a temporary .cue file will be created .img in the local directory and used for the conversion. This cue file will assume that the bin/img file is just one single track of type @@ -289,11 +290,11 @@ Please send patches to populate these fields in gamedb.py with more entries. Examples: - $ ./pop-fe.py --ps3-pkg=alundra.pkg Alundra.img --snd0='https://www.youtube.com/watch?v=wKmZTw7bmI0&list=PL2S7vVonTF8UZrKK17J8FBksvYbnqyHz-&index=1' + $ ./pop-fe.py --ps3-pkg=alundra.pkg Alundra.cue --snd0='https://www.youtube.com/watch?v=wKmZTw7bmI0&list=PL2S7vVonTF8UZrKK17J8FBksvYbnqyHz-&index=1' - $ ./pop-fe.py --ps3-pkg=alundra.pkg Alundra.img --snd0=Alundra.WAV + $ ./pop-fe.py --ps3-pkg=alundra.pkg Alundra.cue --snd0=Alundra.WAV - $ ./pop-fe.py --ps3-pkg=alundra.pkg Alundra.img --snd0=auto + $ ./pop-fe.py --ps3-pkg=alundra.pkg Alundra.cue --snd0=auto If specified as a file it must be WAVE/16bit/stereo/44100Hz. @@ -403,7 +404,7 @@ $ cd ../../.. Then to use it just run it as -$ ./pop-fe.py --ps3-pkg=Grandia.pkg Grandia_d1.img Grandia_d2.img +$ ./pop-fe.py --ps3-pkg=Grandia.pkg Grandia_d1.cue Grandia_d2.cue LIBCRYPT diff --git a/bchunk.py b/bchunk.py index 48397d9..dc3915d 100755 --- a/bchunk.py +++ b/bchunk.py @@ -8,139 +8,30 @@ import struct import sys +from cue import parse_cue class bchunk(object): SECTLEN = 2352 WAV_FORMAT_HLEN = 24 WAV_DATA_HLEN = 8 - def get_file_name(self, line): - # strip off leading 'FILE ' - pos = line.lower().index('file ') - line = line[pos + 5:] - # strip off leading 'FILE ' - pos = line.lower().index(' binary') - line = line[:pos+1] - #strip off leading ' ' - while line[0] == ' ': - line = line[1:] - #strip off trailing ' ' - while line[-1] == ' ': - line = line[:-1] - # remove double quotes - if line[0] == '"': - line = line[1:-1] - # remove single quotes - if line[0] == '\'': - line = line[1:-1] - return line - - def get_mode(self, track, line): - if line.lower().find('mode1/2352') >= 0: - track['extension'] = 'iso' - track['bstart'] = 16 - track['bsize'] = 2048 - return track - if line.lower().find('mode2/2352') >= 0: - track['extension'] = 'iso' - if self.raw: - track['bstart'] = 0 - track['bsize'] = 2352 - return track - if self.psxtruncate: - track['bstart'] = 0 - track['bsize'] = 2336 - return track - track['bstart'] = 24 - track['bsize'] = 2048 - return track - if line.lower().find('mode2/2336') >= 0: - track['extension'] = 'iso' - track['bstart'] = 16 - track['bsize'] = 2336 - return track - if line.lower().find('audio') >= 0: - track['bstart'] = 0 - track['bsize'] = 2352 - track['audio'] = True - if self.towav: - track['extension'] = 'wav' - else: - track['extension'] = 'cdr' - return track - raise Exception('Can not handle this mode yet, check bchunk docs') - - def parse_cue(self, cue): - print('Cue:', cue) if self._verbose else None - with open(cue, 'r') as c: - lines = c.readlines() - tracks = [] - - for line in lines: - # FILE - if re.search('^\s*FILE', line): - f = self.get_file_name(line) - if f[0] != '/': - s = cue.split('/') - if len(s) > 1: - f = '/'.join(s[:-1]) + '/' + f - if self._file: - if len(tracks) >= 1: - tracks[-1]['stop'] = os.stat(tracks[-1]['bin']).st_size - tracks[-1]['stopsect'] = int(tracks[-1]['stop'] / self.SECTLEN) - - self._file = f - continue - # TRACK - pos = line.lower().find('track ') - if pos >= 0: - track = {'audio': False, 'bsize': -1, 'bstart': -1, - 'startsect': -1, 'stopsect': -1, - 'bin': self._file} - line = line[pos + 6:] - while line[0] == ' ': - line = line[1:] - pos = line.index(' ') - track['num'] = int(line[:pos]) - line = line[pos:] - while line[0] == ' ': - line = line[1:] - track = self.get_mode(track, line) - tracks.append(track) - # INDEX - pos = line.lower().find('index ') - if pos >= 0: - line = line[pos + 6:] - while line[0] == ' ': - line = line[1:] - pos = line.index(' ') - line = line[pos:] - while line[0] == ' ': - line = line[1:] - mins, secs, frames = line.split(':') - - tracks[-1]['startsect'] = 75 * ( int(mins) * 60 + int(secs) ) + int(frames) - tracks[-1]['start'] = int(tracks[-1]['startsect'] * self.SECTLEN) - if len(tracks) > 1: - if tracks[-2]['stopsect'] < 0: - tracks[-2]['stopsect'] = tracks[-1]['startsect'] - tracks[-2]['stop'] = tracks[-1]['start'] - 1 - if len(tracks) >= 1: - tracks[-1]['stop'] = os.stat(tracks[-1]['bin']).st_size - tracks[-1]['stopsect'] = int(tracks[-1]['stop'] / self.SECTLEN) - self.tracks = tracks - def writetrack(self, idx, fn): t = self.tracks[idx] - fn = fn + "%02d" % t['num'] + '.' + t['extension'] - print('Read bin', idx, self.tracks[idx]['bin']) if self._verbose else None - with open(self.tracks[idx]['bin'], "rb") as i: - i.seek(t['start']) + print('Read file', idx, self.tracks[idx]['FILE']) if self._verbose else None + + with open(t['FILE'], "rb") as i: + # Find the last index for this track. Assume this is the + # data track + index = None + for _i in t['INDEX']: + index = t['INDEX'][_i] + + i.seek(index['STARTSECT'] * self.SECTLEN) with open(fn, "wb") as o: print('Write track', fn) if self._verbose else None - reallen = int((t['stopsect'] - t['startsect'] + 1) * t['bsize']) + reallen = int((index['STOPSECT'] - index['STARTSECT'] + 1) * t['BSIZE']) - if t['audio'] and self.towav: + if t['MODE'] == 'AUDIO' and self.towav: buf = bytearray(44) buf[0:4] = bytes('RIFF', encoding='utf-8') struct.pack_into('H', struct.unpack('=0 and _i >= 0: + cue['TRACKS'][_t]['INDEX'][_i]['STOPSECT'] = cue['TRACKS'][track]['INDEX'][idx]['STARTSECT'] + + _t = track + _i = idx + + mode = cue['TRACKS'][track]['MODE'] + if mode == 'MODE1/2352': + cue['TRACKS'][track]['BSTART'] = 16 + cue['TRACKS'][track]['BSIZE'] = 2048 + if mode == 'MODE2/2352': + cue['TRACKS'][track]['BSTART'] = 24 + cue['TRACKS'][track]['BSIZE'] = 2048 + if raw: + cue['TRACKS'][track]['BSTART'] = 0 + cue['TRACKS'][track]['BSIZE'] = 2352 + if psxtruncate: + cue['TRACKS'][track]['BSIZE'] = 2336 + if mode == 'MODE2/2336': + cue['TRACKS'][track]['BSTART'] = 16 + cue['TRACKS'][track]['BSIZE'] = 2336 + if mode == 'AUDIO': + cue['TRACKS'][track]['BSTART'] = 0 + cue['TRACKS'][track]['BSIZE'] = 2352 + + +def parse_cue(cuefile, raw=False, psxtruncate=False): + + def strip_line(line): + # strip leading/trailing whitespace and ignore blank lines + while line and line[0] and line[0] in [' ', '\t', '\r', '\n', '"']: + line = line[1:] + while line and line[-1] and line[-1] in [' ', '\t', '\r', '\n', '"']: + line = line[:-1] + return line + + section = None + cue = {} + file = None + filesize = 0 + cue['TRACKS'] = {} + track = None + with open(cuefile, 'r') as f: + for line in f.readlines(): + line = strip_line(line) + if not line: + continue + + if line.upper()[:5] == 'FILE ': + file = strip_line(line[5:line.rindex(' ')]) + if file[0] != '/' and cuefile.rindex('/') >= 0: + file = cuefile[:cuefile.rindex('/') + 1] + file + + if line.upper()[:6] == 'TRACK ': + track = int(strip_line(line[6:line.rindex(' ')])) + mode = strip_line(line[line.rindex(' '):]).upper() + cue['TRACKS'][track] = {} + cue['TRACKS'][track]['MODE'] = mode + cue['TRACKS'][track]['FILE'] = file + cue['TRACKS'][track]['INDEX'] = {} + + if line.upper()[:6] == 'INDEX ': + idx = int(strip_line(line[6:line.rindex(' ')])) + min, sec, frame = strip_line(line[line.rindex(' '):]).split(':') + min = int(min) + sec = int(sec) + frame = int(frame) + + sector = 75 * ( int(min) * 60 + int(sec) ) + int(frame) + if idx == 1 and cue['TRACKS'][track]['MODE'] == 'AUDIO' and len(cue['TRACKS'][track]['INDEX']) == 0: + cue['TRACKS'][track]['INDEX'][0] = {} + cue['TRACKS'][track]['INDEX'][0]['STARTSECT'] = sector - 150 + cue['TRACKS'][track]['INDEX'][idx] = {} + cue['TRACKS'][track]['INDEX'][idx]['STARTSECT'] = sector + + if idx > 1: + raise Exception('Can not handle TRACK INDEX > 1 yet') + + fixup_cue(cue, raw=raw, psxtruncate=psxtruncate) + + return cue + +def write_cue(cue, file): + with open(file, 'w') as o: + file = None + _t = -1 + _i = -1 + + for track in range(1, len(cue['TRACKS']) +1): + if file != cue['TRACKS'][track]['FILE']: + file = cue['TRACKS'][track]['FILE'] + _t = -1 + _i = -1 + o.write("FILE \"%s\" BINARY\n" % file) + o.write(" TRACK %d %s\n" % (track, cue['TRACKS'][track]['MODE'])) + if cue['TRACKS'][track]['MODE'] == 'AUDIO' and 0 not in cue['TRACKS'][track]['INDEX']: + # add preamble + sector = cue['TRACKS'][track]['INDEX'][idx]['STARTSECT'] - 150 + frames = int(sector % 75) + sector = sector - frames + secs = int((sector / 75) % 60) + sector = sector - secs * 75 + mins = int(sector / 75 / 60) + o.write(" INDEX 00 %02d:%02d:%02d\n" % (mins, secs, frames)) + for idx in cue['TRACKS'][track]['INDEX']: + sector = cue['TRACKS'][track]['INDEX'][idx]['STARTSECT'] + frames = int(sector % 75) + sector = sector - frames + secs = int((sector / 75) % 60) + sector = sector - secs * 75 + mins = int(sector / 75 / 60) + o.write(" INDEX %02d %02d:%02d:%02d\n" % (idx, mins, secs, frames)) + + +def parse_ccd(ccdfile): + f = open(ccdfile, 'r') + + section = None + ccd = {} + ccd['FILE'] = os.path.abspath(ccdfile)[:-4] + '.img' + + for line in f.readlines(): + # strip leading/trailing whitespace and ignore blank lines + while line and line[0] and line[0] in [' ', '\t', '\r', '\n']: + line = line[1:] + while line and line[-1] and line[-1] in [' ', '\t', '\r', '\n']: + line = line[:-1] + if not line: + continue + + if line[0] == '[': + section = line[1:-1] + ccd[section] = {} + continue + + # Read individual entries, we must be in a section by now + if not section: + print('ERROR: key=value outside of section') + return None + + kv = line.split('=') + try: + ccd[section][kv[0]] = int(kv[1], base=16) if kv[1][:2] == '0x' else int(kv[1]) + except: + ccd[section][kv[0]] = kv[1] + + # Fixup the TRACKS + ccd['TRACKS'] = {} + _r = [] + for k in ccd: + if k[:6].upper() == 'TRACK ': + idx = int(k[6:]) + ccd['TRACKS'][idx] = ccd[k] + _r.append(k) + for i in _r: + del ccd[i] + _r = [] + for track in ccd['TRACKS']: + ccd['TRACKS'][track]['INDEX'] = {} + for k in ccd['TRACKS'][track]: + if k[:6].upper() == 'INDEX ': + idx = int(k[6:]) + ccd['TRACKS'][track]['INDEX'][idx] = ccd['TRACKS'][track][k] + _r.append((track,k)) + for i in _r: + del ccd['TRACKS'][i[0]][i[1]] + + # Fixup the ENTRIES + ccd['ENTRIES'] = {} + _r = [] + for k in ccd: + if k[:6].upper() == 'ENTRY ': + idx = int(k[6:]) + ccd['ENTRIES'][idx] = ccd[k] + _r.append(k) + for i in _r: + del ccd[i] + + return ccd + + +def ccd2cue(ccd): + cue = {} + + if 'CATALOG' in ccd: + cue['CATALOG'] = ccd['CATALOG'] + + cue['TRACKS'] = {} + for track in ccd['TRACKS']: + cue['TRACKS'][track] = {} + cue['TRACKS'][track]['FILE'] = ccd['FILE'] + if ccd['TRACKS'][track]['MODE'] == 0: + cue['TRACKS'][track]['MODE'] = 'AUDIO' + if ccd['TRACKS'][track]['MODE'] == 1: + cue['TRACKS'][track]['MODE'] = 'MODE1/2352' + if ccd['TRACKS'][track]['MODE'] == 2: + cue['TRACKS'][track]['MODE'] = 'MODE2/2352' + if 'MODE' not in cue['TRACKS'][track]: + raise Exception('Can not handle MODE %d yet' % ccd['TRACKS'][track]['MODE']) + + if 'FLAGS' in ccd['TRACKS'][track]: + cue['TRACKS'][track]['FLAGS'] = ccd['TRACKS'][track]['FLAGS'] + + if 'ISRC' in ccd['TRACKS'][track]: + cue['TRACKS'][track]['ISRC'] = ccd['TRACKS'][track]['ISRC'] + + cue['TRACKS'][track]['INDEX'] = {} + for idx in ccd['TRACKS'][track]['INDEX']: + cue['TRACKS'][track]['INDEX'][idx] = {} + cue['TRACKS'][track]['INDEX'][idx]['STARTSECT'] = ccd['TRACKS'][track]['INDEX'][idx] + + #print(track, cue['TRACKS'][track]) + + fixup_cue(cue) + return cue + + +#CUE { +# CUE +# 'TRACKS': {1: {'MODE': 'MODE2/2352', 'FILE': 'tmp/PSX ...', +# 'INDEX': {1: {'STARTSECT': 0, 'STOPSECT': 127658}}, +# 'BSTART': 24, 'BSIZE': 2048}, +# 2: {'MODE': 'AUDIO', 'FILE': 'tmp/PSX ...', +# 'INDEX': {0: {'STARTSECT': 127658, 'STOPSECT': 127808}, +# 1: {'STARTSECT': 127808, 'STOPSECT': 139329}}, +# 'BSTART': 0, 'BSIZE': 2352}, +# +# CCD +# 'FILE': 'tmp/PSX ...', +# 'CloneCD': {'Version': 3}, +# 'Disc': {'TocEntries': 21, 'Sessions': 1, +# 'DataTracksScrambled': 0, 'CDTextLength': 0}, +# 'Session 1': {'PreGapMode': 2, 'PreGapSubC': 0}, +# 'TRACKS': {1: {'MODE': 2, 'INDEX': {1: 0}}, +# 2: {'MODE': 0, 'INDEX': {1: 127808}}, +# 3: {'MODE': 0, 'INDEX': {1: 139479}}, +# ... +# 'ENTRIES': {0: {'Session': 1, 'Point': 160, 'ADR': 1, 'Control': 4, +# 'TrackNo': 0, 'AMin': 0, 'ASec': 0, 'AFrame': 0, +# 'ALBA': -150, 'Zero': 0, 'PMin': 1, 'PSec': 32, +# 'PFrame': 0, 'PLBA': 6750}, +# 1: {'Session': 1, 'Point': 161, 'ADR': 1, 'Control': 0, +# 'TrackNo': 0, 'AMin': 0, 'ASec': 0, 'AFrame': 0, +# 'ALBA': -150, 'Zero': 0, 'PMin': 18, 'PSec': 0, +# 'PFrame': 0, 'PLBA': 80850}, +# ... +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('-v', action='store_true', help='Verbose') + parser.add_argument('ccd', help='CCD file') + parser.add_argument('cue', help='CUE file') + args = parser.parse_args() + + if args.v: + verbose = True + + ccd = parse_ccd(args.ccd) + cue = ccd2cue(ccd) + write_cue(cue, args.cue) + print('Wrote', args.cue) + diff --git a/pop-fe-ps3.py b/pop-fe-ps3.py index a12975d..ed19fef 100644 --- a/pop-fe-ps3.py +++ b/pop-fe-ps3.py @@ -39,9 +39,9 @@ def get_disc_id(cue, tmp): bc = bchunk() bc.verbose = False bc.open(cue) - bc.writetrack(0, tmp) + bc.writetrack(1, tmp) - gid = popfe.get_gameid_from_iso(tmp + '01.iso') + gid = popfe.get_gameid_from_iso(tmp) return gid @@ -357,9 +357,8 @@ def on_path_changed(self, event): raise Exception('Bad dump CUE, can not convert to CU2') print('Scanning for Game ID') if verbose else None - tmp = 'pop-fe-ps3-work/TMP' + tmp = 'pop-fe-ps3-work/TMP01.iso' disc_id = get_disc_id(cue_file, tmp) - temp_files.append(tmp + '01.iso') self.builder.get_variable('disci%s_variable' % (disc)).set(disc_id) @@ -673,13 +672,11 @@ def on_create_pkg(self): bc = bchunk() bc.towav = True bc.open(self.cue_files[d]) - for i in range(1, len(bc.cue)): - if not bc.cue[i]['audio']: + for i in range(2, len(bc.cue) + 1): + if bc.cue[i]['MODE'] != 'AUDIO': continue - f = 'pop-fe-ps3-work/TRACK_%d_' % (d) - bc.writetrack(i, f) - wav_file = f + '%02d.wav' % (bc.cue[i]['num']) - temp_files.append(wav_file) + wav_file = 'pop-fe-ps3-work/TRACK_%d_%02d.wav' % (d, i) + bc.writetrack(i, wav_file) aea_file = wav_file[:-3] + 'aea' temp_files.append(aea_file) print('Converting', wav_file, 'to', aea_file) diff --git a/pop-fe-psp.py b/pop-fe-psp.py index bc50b51..369db27 100644 --- a/pop-fe-psp.py +++ b/pop-fe-psp.py @@ -34,9 +34,9 @@ def get_disc_id(cue, tmp): bc = bchunk() bc.verbose = False bc.open(cue) - bc.writetrack(0, tmp) + bc.writetrack(1, tmp) - gid = popfe.get_gameid_from_iso(tmp + '01.iso') + gid = popfe.get_gameid_from_iso(tmp) return gid @@ -212,10 +212,10 @@ def on_path_changed(self, event): subprocess.call(['cue2cu2.exe', '-n', cu2_file, '--size', str(os.stat(img_file).st_size), cue_file]) temp_files.append(cu2_file) print('Scanning for Game ID') if verbose else None - tmp = 'pop-fe-psp-work/TMP' + tmp = 'pop-fe-psp-work/TMP01.iso' disc_id = get_disc_id(cue_file, tmp) print('ID', disc_id) - temp_files.append(tmp + '01.iso') + temp_files.append(tmp) self.builder.get_variable('disci%s_variable' % (disc)).set(disc_id) @@ -447,12 +447,11 @@ def on_create_eboot(self): bc = bchunk() bc.towav = True bc.open(self.cue_files[d]) - for i in range(1, len(bc.cue)): - if not bc.cue[i]['audio']: + for i in range(2, len(bc.cue) + 1): + if bc.cue[i]['MODE'] != 'AUDIO': continue - f = 'pop-fe-psp-work/TRACK_%d_' % (d) - bc.writetrack(i, f) - wav_file = f + '%02d.wav' % (bc.cue[i]['num']) + wav_file = 'pop-fe-psp-work/TRACK_%d_%02d.wav' % (d, i) + bc.writetrack(i, wav_file) temp_files.append(wav_file) aea_file = wav_file[:-3] + 'aea' temp_files.append(aea_file) diff --git a/pop-fe.py b/pop-fe.py index e000fd3..37846e5 100755 --- a/pop-fe.py +++ b/pop-fe.py @@ -60,6 +60,7 @@ from make_isoedat import pack except: True +from cue import parse_ccd, ccd2cue, write_cue from popstation import popstation, GenerateSFO from ppf import ApplyPPF from riff import copy_riff, create_riff, parse_riff @@ -780,6 +781,7 @@ def bcd(i): def generate_pbp(dest_file, disc_ids, game_title, icon0, pic0, pic1, cue_files, cu2_files, img_files, aea_files, snd0=None, whole_disk=True, subchannels=[]): print('Create PBP file for', game_title) if verbose else None + SECTLEN = 2352 p = popstation() p.verbose = verbose p.disc_ids = disc_ids @@ -808,7 +810,7 @@ def generate_pbp(dest_file, disc_ids, game_title, icon0, pic0, pic1, cue_files, bc.towav = True bc.open(cue_files[i]) # store how big the data track is - p.add_track0_size(bc.tracks[0]['stop']) + p.add_track0_size(bc.tracks[1]['INDEX'][1]['STOPSECT'] * SECTLEN) p.striptracks = True p.eboot = dest_file @@ -936,6 +938,7 @@ def create_psc(dest, disc_ids, game_title, icon0, pic1, cue_files, cu2_files, im def create_ps3(dest, disc_ids, game_title, icon0, pic0, pic1, cue_files, cu2_files, img_files, mem_cards, aea_files, magic_word, resolution, subdir = './', snd0=None, whole_disk=True, subchannels=[]): print('Create PS3 PKG for', game_title) if verbose else None + SECTLEN = 2352 p = popstation() p.verbose = verbose p.disc_ids = disc_ids @@ -961,7 +964,7 @@ def create_ps3(dest, disc_ids, game_title, icon0, pic0, pic1, cue_files, cu2_fil bc.towav = True bc.open(cue_files[i]) # store how big the data track is - p.add_track0_size(bc.tracks[0]['stop']) + p.add_track0_size(bc.tracks[1]['INDEX'][1]['STOPSECT'] * SECTLEN) # create directory structure f = subdir + disc_ids[0] @@ -1455,13 +1458,14 @@ def create_ps2(dest, disc_ids, game_title, icon0, pic1, cue_files, cu2_files, im def get_disc_ids(cue_files, subdir='./'): disc_ids = [] for idx in range(len(cue_files)): - print('Convert CUE to a normal style ISO') if verbose else None + print('Convert CUE to a normal style ISO', cue_files[idx]) if verbose else None bc = bchunk() bc.verbose = args.v bc.open(cue_files[idx]) - bc.writetrack(0, subdir + 'ISO%02x' % idx) - temp_files.append(subdir + 'ISO%02x01.iso' % idx) - gid = get_gameid_from_iso(subdir + 'ISO%02x01.iso' % idx) + fn = subdir + 'ISO%02x01.iso' % idx + bc.writetrack(1, fn) + temp_files.append(fn) + gid = get_gameid_from_iso(fn) disc_ids.append(gid) return disc_ids @@ -1972,7 +1976,6 @@ def create_manual(source, gameid, subdir='./pop-fe-work/'): print('Found CUE file', f) if verbose else None cue_file = f - tmpcue = None if cue_file[-3:] == 'img' or cue_file[-3:] == 'bin': tmpcue = subdir + 'TMP%d.cue' % (0 if not idx else idx[0]) print('IMG or BIN file. Create a temporary cue file for it', tmpcue) if verbose else None @@ -1984,6 +1987,19 @@ def create_manual(source, gameid, subdir='./pop-fe-work/'): cue_file = tmpcue + if cue_file[-3:] == 'ccd': + tmpcue = subdir + 'TMP%d.cue' % (0 if not idx else idx[0]) + print('CCD file. Create a temporary cue file for it', tmpcue) if verbose else None + temp_files.append(tmpcue) + ccd = parse_ccd(cue_file) + cue = ccd2cue(ccd) + write_cue(cue, tmpcue) + + cue_file = tmpcue + # we didn't actually have a CUE file to start with so just + # replace the "real" cue filename with our temporary one + real_cue_files[-1] = cue_file + if cue_file[-3:] != 'cue': print('%s is not a CUE file. Skipping' % cue_file) if verbose else None continue @@ -2031,23 +2047,22 @@ def create_manual(source, gameid, subdir='./pop-fe-work/'): bc.towav = True bc.open(cue_file) aea_files[0 if not idx else idx[0] - 1] = [] - for i in range(1, len(bc.cue)): - if not bc.cue[i]['audio']: + for i in range(2, len(bc.cue) + 1): + if bc.cue[i]['MODE'] != 'AUDIO': print('WARNING disc contains multiple data tracks. Forcing --whole-disk') args.whole_disk = True continue - f = subdir + 'TRACK_%d_' % (0 if not idx else idx[0]) + f = subdir + 'TRACK_%d_%02d.wav' % (0 if not idx else idx[0], i) bc.writetrack(i, f) - wav_file = f + '%02d.wav' % (bc.cue[i]['num']) - temp_files.append(wav_file) - aea_file = wav_file[:-3] + 'aea' + temp_files.append(f) + aea_file = f[:-3] + 'aea' temp_files.append(aea_file) - print('Converting', wav_file, 'to', aea_file) + print('Converting', f, 'to', aea_file) try: if os.name == 'posix': - subprocess.run(['./atracdenc/src/atracdenc', '--encode=atrac3', '-i', wav_file, '-o', aea_file], check=True) + subprocess.run(['./atracdenc/src/atracdenc', '--encode=atrac3', '-i', f, '-o', aea_file], check=True) else: - subprocess.run(['atracdenc/src/atracdenc', '--encode=atrac3', '-i', wav_file, '-o', aea_file], check=True) + subprocess.run(['atracdenc/src/atracdenc', '--encode=atrac3', '-i', f, '-o', aea_file], check=True) except: print('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\natracdenc not found.\nCan not convert CDDA tracks.\nCreating EBOOT.PBP without support for CDDA audio.\nPlease see README file for how to install atracdenc\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') break