The Hero of Time Posted May 11, 2015 Share Posted May 11, 2015 (edited) So i was wondering how you could extract the cache to obtain music tracks, and the code below is for runescape and i think it works, but obviously i want it for oldschool, i changed the path from C:\Users\x\jagexcache\runescape\LIVE To: C:\Users\x\jagexcache\oldschool\LIVE and i changed main_file_cache.idx To: main_file_cache.idx0 as that was inside the folder, but it doesn't seem to work.. Could anyone help me with this? i would be very happy rsmusicextract.py #!/usr/bin/python3 # # Copyright 2015 Johannes 'josch' Schauer <j.schauer@email.de> # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. import os, struct, io, zlib, sys def bytes_to_int(data): return sum([b << ((len(data) - i - 1) * 8) for i, b in enumerate(data)]) def unpack_file(cache_dir, archive_idx, file_idx): idx_fname = os.path.join(cache_dir, "main_file_cache.idx0" + str(archive_idx)) with open(idx_fname, "rb") as idx_file: idx_file.seek(file_idx*6) fsize = bytes_to_int(idx_file.read(3)) curr_chunk_offs = bytes_to_int(idx_file.read(3)) * 520 write_offs, chunk_idx, fbuf = 0, 0, b"" with open(os.path.join(cache_dir, "main_file_cache.dat2"), "rb") as cache_file: while curr_chunk_offs != 0: cache_file.seek(curr_chunk_offs) if file_idx >= 65536: assert file_idx == struct.unpack(">I", cache_file.read(4))[0] chunk_size = min(510, fsize - write_offs) else: assert file_idx == struct.unpack(">H", cache_file.read(2))[0] chunk_size = min(512, fsize - write_offs) assert chunk_idx == struct.unpack(">H", cache_file.read(2))[0] curr_chunk_offs = bytes_to_int(cache_file.read(3)) * 520 assert archive_idx == struct.unpack("B", cache_file.read(1))[0] chunk_idx += 1 write_offs += chunk_size fbuf += cache_file.read(chunk_size) assert len(fbuf) == fsize if fbuf[9:11] == b"\x1f\x8b": return zlib.decompress(fbuf[19:], -zlib.MAX_WBITS) elif fbuf[4:10] == b"\x31\x41\x59\x26\x53\x59": raise Exception("bzip2 decompression not implemented") else: return fbuf[5:] def get_tname_dict(inf): track_id2name = dict() music_num, = struct.unpack(">H", inf.read(2)) for i in range(music_num): track_id, = struct.unpack(">H", inf.read(2)) s = b"" while True: b = inf.read(1) if b == b"\x00": break s += b if s in [b'', b' ', b' ', b' ']: continue track_id2name[track_id] = s.decode("utf8") return track_id2name def get_tid_dict(inf, track_id2name): file_id2track = dict() file_num, = struct.unpack(">H", inf.read(2)) for i in range(file_num): track_id, file_id = struct.unpack(">HI", inf.read(6)) if track_id not in track_id2name: continue file_id2track[file_id] = track_id return file_id2track def main(cache_dir, out_dir, process_incomplete): # archive 15, file 5 stores the track names resolve = unpack_file(cache_dir, 17, 5) # this is a gross hack because I don't know a better way to find the right # sections in the archive names = resolve.index(b"\x00\x66\x24\x07") files = resolve.index(b"\x00\x66\x0b\x08") assert names != -1 and files != -1 track_id2name = get_tname_dict(io.BytesIO(resolve[names+6:])) file_id2track = get_tid_dict(io.BytesIO(resolve[files+6:]), track_id2name.keys()) # go through all track ids and get the associated file from the archive tracklist = [] for i,(file_id, track_id) in enumerate(sorted(file_id2track.items())): jaga = unpack_file(cache_dir, 40, file_id) if jaga is None or jaga[:4] != b"JAGA": continue jaga, incomplete, ogg_chunks = io.BytesIO(jaga[32:]), False, [] while jaga.read(4) != b"OggS": file_id, = struct.unpack(">I", jaga.read(4)) ogg = unpack_file(cache_dir, 40, file_id) if ogg is None or ogg[:4] != b"OggS": incomplete = True break ogg_chunks.append(ogg) print("%f %%"%((i*100)/len(file_id2track)), end='\r', file=sys.stderr) if incomplete and not process_incomplete: continue if not out_dir: tracklist.append(track_id2name[track_id]) continue ogg_chunks = [ b"OggS" + jaga.read() ] + ogg_chunks if incomplete: outdir = os.path.join(out_dir, "incomplete", track_id2name[track_id]) else: outdir = os.path.join(out_dir, track_id2name[track_id]) os.makedirs(outdir, exist_ok=True) for i,chunk in enumerate(ogg_chunks): with open(os.path.join(outdir, "%03d.ogg"%i), "wb") as f: f.write(chunk) if not out_dir: print("\n".join(sorted(tracklist))) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="Extract music tracks from runescape cache file.") parser.add_argument("cache", help="The runescape cache directory (probably ends with jagexcache/runescape/LIVE/).") parser.add_argument("out", nargs="?", help="Output directory for extracted music. If this is not supplied, the available music is simply listed.") parser.add_argument("-i", "--incomplete", action="store_true", help="Also process incomplete music.") args = parser.parse_args() main(args.cache, args.out, args.incomplete) extract.bat @echo off py --version 2>NUL if errorlevel 1 ( echo You need to install Python for the extractor to work. echo Now go do that. pause exit ) if not exist rsmusicextract.py ( bitsadmin.exe /transfer "josch/rsmusicextract" https://raw.githubusercontent.com/josch/rsmusicextract/master/rsmusicextract.py %CD%/rsmusicextract.py echo rsmusicextract.py not found, downloaded it instead echo https://github.com/josch/rsmusicextract echo. ) echo --- RS Music Extractor --- echo Script by josch, merger by Vazkii echo. echo Extracting files from the Jagex Cache echo This will only extract songs that exist in your Cache. echo If you've never played the song ingame it won't be here. rsmusicextract.py %userprofile%/jagexcache/oldschool/LIVE %CD%/extract mkdir output echo # Runescape Soundtrack > output/_playlist.m3u echo Done. Merging songs cd extract for /D %%N in (*) do ( cd %%N echo Merging %%N.ogg echo %%N.ogg >> ../../output/_playlist.m3u copy /b *.ogg "%%N.ogg" >NUL move "%%N.ogg" ../../output >NUL cd .. ) echo Done! You may delete the 'extract' folder now if you please. echo Check the 'output' folder for your music. A playlist file has also been created. echo Note that due to the way the files were stored, some players won't be able to play the tracks. pause exit Edited May 11, 2015 by The Hero of Time Quote Link to comment Share on other sites More sharing options...
Botre Posted May 11, 2015 Share Posted May 11, 2015 osrs uses midi extensively, I don't think they store complete audio renders in the cache. Quote Link to comment Share on other sites More sharing options...
The Hero of Time Posted May 11, 2015 Author Share Posted May 11, 2015 osrs uses midi extensively, I don't think they store complete audio renders in the cache. So.. it's not possible? Quote Link to comment Share on other sites More sharing options...
FrostBug Posted May 11, 2015 Share Posted May 11, 2015 op is noob Quote Link to comment Share on other sites More sharing options...
Botre Posted May 11, 2015 Share Posted May 11, 2015 So.. it's not possible? It's probably possible, but it's not just going to be a matter of opening the cache. Might not be easy to force render tracks or stuff like that though, because their midi engine use licensed libraries so they might have tried a bit harder a securing that part of the code :p Quote Link to comment Share on other sites More sharing options...
The Hero of Time Posted May 11, 2015 Author Share Posted May 11, 2015 It's probably possible, but it's not just going to be a matter of opening the cache. Might not be easy to force render tracks or stuff like that though, because their midi engine use licensed libraries so they might have tried a bit harder a securing that part of the code do you have the power of aiding this poor soul, mighty botre? Quote Link to comment Share on other sites More sharing options...
Botre Posted May 11, 2015 Share Posted May 11, 2015 do you have the power of aiding this poor soul, mighty botre? I wish, but I don't, you should try a private server pro who is familiar with the cache and stoof like that. Quote Link to comment Share on other sites More sharing options...