forked from ReScrap/ScrapHacks
		
	Lots of changes, expand to read
- Add notes folder with MDBook documentation (the NOTES.md file was getting kind of large) - Add rz_analyze.py, does the same a r2_analyze.py just with Rizin instead of radare2 so the project can be loaded in Cutter (*and* it's faster) - Add Scrap.rzdb, Rizin database for the Scrap.exe executable - Add Scrapper_rs, Rust version of .packed extractor and repacker - replace helplib.txt with helplib.md - add Py_Docs folder which contains generated documentation for the binary python modules built into Scrap.exe
This commit is contained in:
		
							parent
							
								
									43c01e81d2
								
							
						
					
					
						commit
						7afdfb5869
					
				
					 50 changed files with 483086 additions and 1709 deletions
				
			
		|  | @ -8,10 +8,13 @@ import string | |||
| import re | ||||
| from binascii import hexlify | ||||
| 
 | ||||
| 
 | ||||
| def gen(): | ||||
|     with open(sys.argv[1], "rb") as fh: | ||||
|         size = os.stat(sys.argv[1]).st_size | ||||
|         progbar = tqdm(total=size, unit="bytes", unit_scale=True, unit_divisor=1024) | ||||
|         progbar = tqdm( | ||||
|             total=size, unit="bytes", unit_scale=True, unit_divisor=1024, leave=False | ||||
|         ) | ||||
|         pos = 0 | ||||
|         for entry in mp.Unpacker(fh, raw=True): | ||||
|             progbar.update(fh.tell() - pos) | ||||
|  | @ -27,14 +30,10 @@ def gen(): | |||
|             yield entry | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def strdump(data): | ||||
|     printable_chars = set(bytes(string.printable, "ascii")) - set(b"\n\r\t\x0b\x0c") | ||||
|     return "".join(chr(c) if c in printable_chars else "." for c in data) | ||||
| 
 | ||||
| def tohex(data): | ||||
|     return str(hexlify(data), "utf8").upper() | ||||
| 
 | ||||
| 
 | ||||
| # best=sorted(tqdm(gen(),ascii=True),key=lambda v:len(v['data']),reverse=True) | ||||
| 
 | ||||
|  | @ -53,16 +52,24 @@ def tohex(data): | |||
| #                 entry['infos'][fmt]=[v[0] for v in struct.iter_unpack(fmt,data)] | ||||
| #     return entry | ||||
| 
 | ||||
| filters=[re.compile(s) for s in sys.argv[2:]] | ||||
| 
 | ||||
| filters = sys.argv[2:] | ||||
| with open("all.log", "w") as of: | ||||
|     for entry in gen(): | ||||
|         fm=[(f.match(entry['filename']) is not None) for f in filters] | ||||
|         if filters and not any(fm): | ||||
|         fm = any( | ||||
|             all(s.lower() in entry["filename"].lower() for s in f.split("|")) | ||||
|             for f in filters | ||||
|         ) | ||||
|         if filters and not fm: | ||||
|             continue | ||||
|         is_magic = bytes(entry["block_id"], "utf8").ljust(4, b"\0") == entry["data"] | ||||
|         entry["data_len"] = len(entry["data"]) | ||||
|         entry["str"] = strdump(entry["data"]) | ||||
|         entry["data"] = tohex(entry["data"]) | ||||
|         entry["data"] = entry["data"].hex().upper() | ||||
|         if is_magic: | ||||
|             print("#" * 50, file=of) | ||||
|         print( | ||||
|             "{timestamp} {block_id} {filename} {data_len:08X} {data} {str}".format(**entry), file=of | ||||
|             "{timestamp} {block_id} {filename} {data_len:08X} {data} {str}".format( | ||||
|                 **entry | ||||
|             ), | ||||
|             file=of, | ||||
|         ) | ||||
|  |  | |||
|  | @ -38,12 +38,12 @@ def seek_to(fh, offset, pos=None): | |||
|     yield | ||||
|     fh.seek(pos) | ||||
| 
 | ||||
| def read_array(s,fh): | ||||
|     ret=[] | ||||
| 
 | ||||
| def read_array(s, fh): | ||||
|     ret = [] | ||||
|     count = read_struct("<I", fh)[0] | ||||
|     size = struct.calcsize(s) | ||||
|     for _ in range(count): | ||||
|         ret.append(read_struct(s,fh)) | ||||
|         ret.append(read_struct(s, fh)) | ||||
|     return ret | ||||
| 
 | ||||
| 
 | ||||
|  | @ -93,7 +93,10 @@ class Parser: | |||
|         print("{}[{}] {} bytes".format("  " * self.depth, magic, len(data))) | ||||
|         self.depth += 1 | ||||
|         fh = BytesIO(data) | ||||
|         ret = getattr(self, magic, lambda fh: self._default(magic, fh))(fh) | ||||
|         if len(data) != 0: | ||||
|             ret = getattr(self, magic, lambda fh: self._default(magic, fh))(fh) | ||||
|         else: | ||||
|             ret = None | ||||
|         pos = fh.tell() | ||||
|         leftover = len(fh.read()) | ||||
|         fh.seek(pos) | ||||
|  | @ -198,17 +201,27 @@ class Parser: | |||
|             dum = {} | ||||
|             dum["name"] = read_str(fh) | ||||
|             dum["pos"] = read_struct("<fff", fh) | ||||
|             dum["rot"] = read_struct("<fff", fh) | ||||
|             dum["ang"] = read_struct("<fff", fh) | ||||
|             dum["has_ini"] = read_struct("<I", fh)[0] | ||||
|             if dum["has_ini"]: | ||||
|                 dum['ini']=self.parse_block(fh) | ||||
|             dum["has_next"] = read_struct("<I", fh)[0] | ||||
|                 dum["ini"] = self.parse_block(fh) | ||||
|             dum["unk_1"] = read_struct("<I", fh)[0] | ||||
|             ret["dummies"].append(dum) | ||||
|         assert fh.read() == b"", "Leftover Data" | ||||
|         return ret | ||||
| 
 | ||||
|     # def AMC(self, fh): | ||||
|     #     return len(fh.read()) | ||||
|     def AMC(self, fh): | ||||
|         ret = {} | ||||
|         ret["unk_1"] = read_struct("<I", fh)[0] | ||||
|         ret["unk_2"] = read_struct("<I", fh)[0] | ||||
|         ret["floats"] = read_struct("<16f", fh)  # ??? | ||||
|         ret["cmsh_1"] = [self.parse_block(fh) for _ in range(2)] | ||||
|         ret["num_submeshes"] = read_struct("<I", fh)[0] | ||||
|         ret["cmsh_2"] = [self.parse_block(fh) for _ in range(ret["num_submeshes"])] | ||||
|         return ret | ||||
| 
 | ||||
|     def CMSH(self, fh): | ||||
|         return {"ret": len(fh.read())} | ||||
| 
 | ||||
|     # def EMI(self, fh): | ||||
|     #     return len(fh.read()) | ||||
|  | @ -218,30 +231,14 @@ class Parser: | |||
| 
 | ||||
| basedir = r"D:/Games/Deep Silver/Scrapland/extracted/Data.packed" | ||||
| 
 | ||||
| files = [ | ||||
|     r"Models/Chars/Dtritus/Dtritus.sm3", | ||||
|     r"Models/Elements/AnilloEstructuraA/AnilloEstructuraA.SM3", | ||||
|     r"models/elements/antenaa/antenaa.lod1.sm3", | ||||
|     # r"models/elements/abshield/anm/loop.cm3", | ||||
|     # r"levels/fake/map/map3d.amc", | ||||
|     # r"levels/shipedit/map/map3d.dum", | ||||
|     # r"levels/menu/map/map3d.emi", | ||||
|     r"Models/Skies/Menu/Sky.SM3", | ||||
|     r"Levels/Menu/Map/Map3D.SM3", | ||||
|     r"Models/Elements/AnilloEstructuraD/AnilloEstructuraD.LOD1.SM3", | ||||
|     # r"levels/menu/map/map3d.amc", | ||||
|     # r"levels/menu/map/map3d.dum", | ||||
|     # r"levels/menu/map/scenecamera/anm/loop.cm3", | ||||
|     r"models/chars/boss/boss.sm3", | ||||
|     # r"models/chars/boss/anm/boss_walk.cm3", | ||||
| ] | ||||
| 
 | ||||
| filt = [s.lower() for s in sys.argv[1:]] | ||||
| 
 | ||||
| for root, folders, files in os.walk(basedir): | ||||
| for root, _, files in os.walk(basedir): | ||||
|     for file in files: | ||||
|         path = os.path.join(root, file).replace("\\","/") | ||||
|         if not path.lower().endswith(".dum".lower()): | ||||
|         path = os.path.join(root, file).replace("\\", "/") | ||||
|         if not path.lower().endswith(".amc".lower()): | ||||
|             continue | ||||
|         if "menu" not in path.lower(): | ||||
|             continue | ||||
|         print("Parsing", path) | ||||
|         p = Parser(debug=True) | ||||
|  | @ -252,4 +249,3 @@ for root, folders, files in os.walk(basedir): | |||
|                     break | ||||
|                 pprint(parsed, compact=False, indent=4) | ||||
|         print("#" * 50) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										21
									
								
								tools/parse_save.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tools/parse_save.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| import sys | ||||
| from construct import * | ||||
| from pprint import pprint | ||||
| 
 | ||||
| ScrapSaveVar = Struct( | ||||
|     "name" / PascalString(Int32ul, encoding="utf-8"), | ||||
|     "data" / PascalString(Int32ul, encoding="utf-8"), | ||||
| ) | ||||
| ScrapSave = "ScarpSaveGame" / Struct( | ||||
|     "title" / PascalString(Int32ul, encoding="utf-8"), | ||||
|     "id" / PascalString(Int32ul, encoding="utf-8"), | ||||
|     "data" / PrefixedArray(Int32ul, ScrapSaveVar), | ||||
|     Terminated, | ||||
| ) | ||||
| with open(sys.argv[1], "rb") as sav_file: | ||||
|     save = ScrapSave.parse_stream(sav_file) | ||||
|     print("ID:", save.id) | ||||
|     print("Title:", save.title) | ||||
|     for var in save.data: | ||||
|         print("   {}: {}".format(var.name, var.data)) | ||||
| 
 | ||||
|  | @ -3,29 +3,18 @@ from collections import OrderedDict | |||
| import glob | ||||
| import os | ||||
| import shutil | ||||
| from construct import ( | ||||
|     Struct, | ||||
|     PascalString, | ||||
|     Int32ul, | ||||
|     Lazy, | ||||
|     Pointer, | ||||
|     Bytes, | ||||
|     this, | ||||
|     PrefixedArray, | ||||
|     Const, | ||||
|     Debugger | ||||
| ) | ||||
| from construct import * | ||||
| from tqdm import tqdm | ||||
| 
 | ||||
| setglobalstringencoding(None) | ||||
| 
 | ||||
| ScrapFile = Struct( | ||||
|     "path" / PascalString(Int32ul, encoding="ascii"), | ||||
|     "path" / PascalString(Int32ul), | ||||
|     "size" / Int32ul, | ||||
|     "offset" / Int32ul, | ||||
|     "data" / Lazy(Pointer(this.offset, Bytes(this.size))), | ||||
| ) | ||||
| DummyFile = Struct( | ||||
|     "path" / PascalString(Int32ul, encoding="u8"), "size" / Int32ul, "offset" / Int32ul | ||||
|     "data" / OnDemandPointer(this.offset, Bytes(this.size)), | ||||
| ) | ||||
| DummyFile = Struct("path" / PascalString(Int32ul), "size" / Int32ul, "offset" / Int32ul) | ||||
| 
 | ||||
| PackedHeader = Struct( | ||||
|     Const(b"BFPK"), Const(b"\0\0\0\0"), "files" / PrefixedArray(Int32ul, ScrapFile) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue