Last active
May 18, 2024 05:54
-
-
Save xchewtoyx/ab04f1148995e76a0719f5ffb06ec7e5 to your computer and use it in GitHub Desktop.
Module to load Rocksmith+ IDA files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import parseida | |
barracuda = '421bdc20-4dcd-4752-b035-12c79cfebc38' | |
ida = skill_loader(barracuda) | |
print(f"Loaded {ida.filename}") | |
for arrangement_key in ida.arrangement_keys(): | |
print(f"Difficulty({arrangement_key}): {ida.song_difficulty[arrangement_key][0]['rank_name']}") | |
print(f"Techniques({arrangement_key}): {ida.technique_list(arrangement_key)}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loaded /mnt/c/Users/russell/AppData/Local/My Games/Rocksmith+/DLC/Skill/v43/421bdc20-4dcd-4752-b035-12c79cfebc38_v36.ida | |
Difficulty(ps:ad:top): basic | |
Techniques(ps:ad:top): ['tech:Guitar:CHD_BMM:x,1,3,3,3,x', 'tech:Guitar:CHD_OCB:x,0,2,2,2,0', 'tech:Guitar:CHD_P3:1,3,3,x,x,x', 'tech:Guitar:CHD_P3:x,1,3,3,x,x', 'tech:Guitar:CHD_PO:0,2,2,x,x,x', 'tech:Guitar:CHD_PO:x,0,2,2,x,x'] | |
Difficulty(ps:cc:bc): basic | |
Techniques(ps:cc:bc): ['tech:Guitar:CHD_OCB:x,0,2,2,2,0', 'tech:Guitar:CHD_P3:1,3,3,x,x,x', 'tech:Guitar:CHD_P3:x,1,3,3,x,x', 'tech:Guitar:CHD_PO:0,2,2,x,x,x', 'tech:Guitar:CHD_PO:x,0,2,2,x,x'] | |
Difficulty(ps:cc:fc): basic | |
Techniques(ps:cc:fc): ['tech:Guitar:CHD_BMM:x,1,3,3,3,x', 'tech:Guitar:CHD_OCB:x,0,2,2,2,0', 'tech:Guitar:CHD_P3:1,3,3,x,x,x', 'tech:Guitar:CHD_P3:x,1,3,3,x,x', 'tech:Guitar:CHD_PO:0,2,2,x,x,x', 'tech:Guitar:CHD_PO:x,0,2,2,x,x'] | |
Difficulty(ps:cc:pc): basic | |
Techniques(ps:cc:pc): ['tech:Guitar:CHD_DBE:0,2,x,x,x,x', 'tech:Guitar:CHD_DBE:1,3,x,x,x,x', 'tech:Guitar:CHD_DBE:x,0,2,x,x,x', 'tech:Guitar:CHD_DBE:x,1,3,x,x,x', 'tech:Guitar:CHD_P2A:x,1,3,x,x,x', 'tech:Guitar:CHD_P2E:1,3,x,x,x,x', 'tech:Guitar:CHD_PO:0,2,x,x,x,x', 'tech:Guitar:CHD_PO:x,0,2,x,x,x'] | |
Difficulty(ps:cc:sn): basic | |
Techniques(ps:cc:sn): [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import glob | |
import json | |
import os | |
import re | |
import zlib | |
# IDA file header is: | |
# 4 bytes - "ADI " | |
# 4 bytes - i32 1 (file version?) | |
# 4 bytes - i32 compressed data length | |
# 4 bytes - i32 uncompressed size | |
# 4 bytes - i32 number of files in archive | |
# 20 bytes - string ISO timestamp (e.g. "2024-02-08T00:00:00") | |
# 8 bytes - 0xffffffffffffffff (i64 -1) | |
from struct import unpack_from | |
DLC_BASE="/mnt/c/Users/russell/AppData/Local/My Games/Rocksmith+/DLC" | |
class IDAReader(): | |
def __init__(self, filename): | |
self.filename = filename | |
with open(filename, 'rb') as file: | |
self.file_content = file.read() | |
# Read file count from file header (u32 at offset 0x10) | |
file_count = unpack_from('I', self.file_content, 0x10)[0] | |
# Identify the position of zlib signature (x\x9c) | |
# Header is 48 bytes, so this should be at 0x30 | |
compressed_data_offset = self.file_content.find(b'x\x9c') | |
# Extract the compressed data starting from the zlib signature | |
compressed_data = self.file_content[compressed_data_offset:] | |
# Decompress the data using zlib | |
self.decompressed_data = zlib.decompress(compressed_data) | |
self.files = {} | |
self.data_offset = -1 | |
self._parse_ida_index(file_count) | |
# File index entry format | |
# start - i32 offset into where file entry starts | |
# length - i32 length in bytes of file | |
# crc32 - u32 CRC32 of file (use zlib.crc32 to verify) | |
# filename | |
# length - i32 length of filename | |
# path - bytes[] byte array containing path and name of the file | |
def _parse_ida_index(self, file_count): | |
progress = 0 | |
for _ in range(file_count): | |
header = self.decompressed_data[progress:progress+16] | |
progress += 16 | |
start, length, crc32, flen = unpack('iiIi', header) | |
filename = self.decompressed_data[progress:progress+flen].decode('utf-8') | |
self.files[filename] = dict(start=start, length=length, data=data) | |
progress += flen | |
# Progress indicator is now pointing to the start of the data | |
self.data_offset = progress | |
def extract_json_data(self, filename): | |
attrs = self.files[filename] | |
offset = self.data_offset + attrs["start"] | |
end = offset+attrs["length"] | |
return json.loads( | |
self.decompressed_data[offset:end] | |
) | |
class SkillIDA(IDAReader): | |
def __init__(self, *args): | |
super().__init__(*args) | |
self.song_difficulty = self.extract_json_data(next(key for key in self.files.keys() if 'curriculum_song_difficulty' in key)) | |
self.song_nexus_aegis = self.extract_json_data(next(key for key in self.files.keys() if 'curriculum_song_nexus_aegis' in key)) | |
def arrangement_keys(self): | |
return self.song_difficulty.keys() | |
def technique_list(self, arrangement_key): | |
return [f"{tech['key_branch']}:{tech['key_nexus']}" for tech in self.song_nexus_aegis.get(arrangement_key, [])] | |
def skill_loader(arrangementid): | |
skills_path = os.path.join(DLC_BASE, 'Skill') | |
candidates = glob.glob(f'{skills_path}/**/{arrangementid}_v*.ida') | |
# In the case of duplicates, return the filename that sorts highest | |
return SkillIDA(max(candidates)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment