imranUSK Posted Sunday at 12:48 AM Posted Sunday at 12:48 AM (edited) I tried to extract the files from the file but I could not because of my little experience. I hope someone can help me extract the files. I will be happy to help you. RESARC.rar Edited Sunday at 12:58 AM by imranUSK
wq223 Posted Sunday at 06:37 AM Posted Sunday at 06:37 AM 5 hours ago, imranUSK said: I tried to extract the files from the file but I could not because of my little experience. I hope someone can help me extract the files. I will be happy to help you. RESARC.rar 1.71 MB · 9 downloads The file you provide is composed of two parts Index + file data The index stores the offset and length of all files and their original names I originally wanted to do a thorough parse through the index part to ensure the complete file extraction, but I encountered a problem when parsing the index 1. There are several file blocks that will have a few more bytes than other blocks The content that caused the exception to be read 2. After jumping to the file location through the offset, there is a portion of the extra data, and these extra data are also different. Some have eight bytes of extra data before compressing the data, some have only four, and some will have more. The file data is ordinary zlib compression, and the entire file should be compressed in this way There is no way, I can only switch ideas and use a brute force extraction method If you want to attach the recovery file name, you may need to thoroughly parse the structure of the index part to ensure that there are no strange phenomena. import os import zlib import struct import sys def find_magic_bytes(file_handle, start_pos=0, magic=b'\x78\xDA'): file_handle.seek(start_pos) data = file_handle.read() return data.find(magic) def process_file(input_file, output_dir): if not os.path.exists(output_dir): os.makedirs(output_dir) with open(input_file, 'rb') as f: current_pos = 0 file_size = os.path.getsize(input_file) while current_pos < file_size: magic_pos = find_magic_bytes(f, current_pos) if magic_pos == -1: break actual_pos = current_pos + magic_pos print(f"\nFound 78DA at position: {actual_pos} (0x{actual_pos:X})") f.seek(actual_pos - 4) length_bytes = f.read(4) if len(length_bytes) != 4: print("Error: Could not read 4 bytes before 78DA") current_pos = actual_pos + 2 continue length = struct.unpack('<I', length_bytes)[0] print(f"Compressed data length: {length} (0x{length:X})") f.seek(actual_pos) compressed_data = f.read(length) try: decompressed_data = zlib.decompress(compressed_data) output_filename = f"{actual_pos:X}.dat" output_path = os.path.join(output_dir, output_filename) with open(output_path, 'wb') as out_file: out_file.write(decompressed_data) print(f"Success! Saved to {output_path}") current_pos = actual_pos + length except zlib.error as e: print(f"Decompression failed: {e}") current_pos = actual_pos + 2 print("\nFinished processing file.") if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: python jrz.py <input_file> <output_dir>") sys.exit(1) input_file = sys.argv[1] output_dir = sys.argv[2] process_file(input_file, output_dir)
Engineers Rabatini Posted Sunday at 07:21 AM Engineers Posted Sunday at 07:21 AM (edited) Basically you can treat the whole archive as a big blob, look for every zlib header (0x78 0xDA), and assume each one marks the start of a compressed file. Before the first header, you scan that “index” region with a simple regex to pull out any names like FOO.DAT or BAR.BIN. Then for each header you hand the rest of the data to zlib to decompress — if it works, you save it; if zlib chokes with error ‑3 (“incorrect header check”), we skip that spot. Whenever you have a matching filename from the index,you use it; otherwise you just name the file by its offset (e.g. 6FC9.bin). This brute‑force trick skips all the headaches of parsing every little variable-length entry in the index, but still recovers most of the original names. import os import sys import re import zlib def find_magic_positions(data, magic=b'\x78\xDA'): """ Return a list of offsets where the 2-byte zlib header appears. Note: brute‑force may find false positives inside compressed data. """ return [i for i in range(len(data)-1) if data[i:i+2] == magic] def parse_filenames(data, first_magic_pos): """ Extract printable filenames from the index region (everything before the first compressed block). Assumes names look like NAME.EXT. """ idx_region = data[:first_magic_pos] # map non‑printable bytes to newlines so regex won't span them text = ''.join(chr(b) if 32 <= b < 127 else '\n' for b in idx_region) # simple regex for basename + dot + extension raw = re.findall(r'[A-Za-z0-9_]+\.[A-Za-z0-9]+', text) # dedupe while preserving order seen = set() names = [] for nm in raw: if nm not in seen: seen.add(nm) names.append(nm) return names def process_file(input_file, output_dir): if not os.path.exists(output_dir): os.makedirs(output_dir) with open(input_file, 'rb') as f: data = f.read() magic_positions = find_magic_positions(data) if not magic_positions: print("No zlib blocks found.") return filenames = parse_filenames(data, magic_positions[0]) print(f"Found {len(magic_positions)} compressed blocks.") print(f"Parsed {len(filenames)} filenames from index.\n") for idx, pos in enumerate(magic_positions): print(f"[{idx+1}/{len(magic_positions)}] at offset 0x{pos:X}") decomp = zlib.decompressobj() tail = data[pos:] try: out = decomp.decompress(tail) out += decomp.flush() except zlib.error as e: # e.args[0] often contains error code and message print(f" Decompression failed (zlib error: {e.args[0]!r}). Skipping.") continue # choose parsed name or fallback if idx < len(filenames): name = filenames[idx] else: name = f"{pos:X}.bin" out_path = os.path.join(output_dir, name) with open(out_path, 'wb') as of: of.write(out) print(f" Saved as: {name} ({len(out)} bytes)") print("\nDone.") if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: python jrz_extract.py <input_file> <output_dir>") sys.exit(1) process_file(sys.argv[1], sys.argv[2]) That should extract all files with "filename", and the rest that don't have it. Edited Sunday at 07:23 AM by Rabatini
wq223 Posted Sunday at 07:49 AM Posted Sunday at 07:49 AM I've performed a resolution test on the index part. If there are no special cases in some parts, it should be normal to restore the file name There is basically no problem with the first two indexes. The problem is in the third index, and there is a block that adds some extra data. 160686,1650824,495561,texture/acttex 87397,856280,495561,texture/character 18393,263222,495561,texture/item.bmp 6229,66614,513954,texture/npc.bmp 38673,263222,520183,texture/player.bmp 24102,263222,558856,texture/weapon.bmp 73289,794544,582958,texture/tutorial 2954,66614,582958,texture/op01_b.bmp 5133,196662,585912,texture/op02_b.bmp 9737,66614,591045,texture/op_chara.bmp 11285,66612,600782,texture/test0.bmp 7158,66612,612067,texture/test1.bmp 3988,65860,619225,texture/test2.bmp 7773,66612,623213,texture/test3.bmp 3109,65728,630986,texture/test4.bmp 9085,66614,634095,texture/test5.bmp 13067,66616,643180,texture/test6.bmp 511710,6997981,656247,texture/commontex 39217,289846,656247,texture/effect 20056,263222,656247,texture/effect.bmp 19161,26624,676303,texture/thumbs.db 43423,270215,695464,texture/font 41175,263222,695464,texture/font_line.bmp 2248,6993,736639,texture/ql.txt 217901,4580208,738887,texture/mapchips 3712,66614,738887,texture/gimic01.bmp 7214,263222,742599,texture/ground01.bmp 5080,262288,749813,texture/ground01_512.bmp 10406,263222,754893,texture/ground02.bmp 14198,263222,765299,texture/ground03.bmp 8984,263222,779497,texture/ground04.bmp 8385,263172,788481,texture/ground05.bmp 11110,263222,796866,texture/ground06.bmp 9368,263222,807976,texture/ground07.bmp 13627,263222,817344,texture/ground08.bmp 13539,263222,830971,texture/ground09.bmp 21793,263024,844510,texture/ground10.bmp 8794,262428,866303,texture/groundop.bmp 5293,263222,875097,texture/groundtown01.bmp 5435,262376,880390,texture/groundtown02.bmp 53,196,885825,texture/objectl.bmp 3536,66614,885878,texture/objectltexture01.bmp 3716,66614,889414,texture/objectltexture02.bmp 4620,66614,893130,texture/objectltexture03.bmp 3767,66614,897750,texture/objectltexture04.bmp 3797,66614,901517,texture/objectltexture05.bmp 5680,66614,905314,texture/objectltexture06.bmp 3484,66614,910994,texture/objectltexture07.bmp 5667,66614,914478,texture/objectltexture08.bmp 3877,66614,920145,texture/objectltexture09.bmp 5275,66614,924022,texture/objectltexture10.bmp 19115,31744,929297,texture/thumbs.db 5533,66614,948412,texture/towntexture01.bmp 2843,66614,953945,texture/towntexture02.bmp 195806,1836644,956788,texture/sys_cg 13068,66614,956788,texture/adhoc.bmp 5697,66614,969856,texture/e_button00.bmp 4996,66120,975553,texture/e_button01.bmp 5459,66120,980549,texture/e_button02.bmp 5364,66120,986008,texture/e_button03.bmp 5461,66120,991372,texture/e_button04.bmp 5668,66120,996833,texture/e_button05.bmp 5496,66120,1002501,texture/e_button06.bmp 5585,66120,1007997,texture/e_button07.bmp 5082,66120,1013582,texture/e_button08.bmp 5116,66614,1018664,texture/e_button09.bmp 6372,66116,1023780,texture/e_button10.bmp 4767,66614,1030152,texture/e_time00.bmp 4496,65848,1034919,texture/e_time01.bmp 4918,65848,1039415,texture/e_time02.bmp 4951,65848,1044333,texture/e_time03.bmp 4964,65848,1049284,texture/e_time04.bmp 4894,65848,1054248,texture/e_time05.bmp 4256,65848,1059142,texture/e_time06.bmp 4661,65848,1063398,texture/e_time07.bmp 5011,65848,1068059,texture/e_time08.bmp 4460,65848,1073070,texture/e_time09.bmp 5378,65848,1077530,texture/e_time10.bmp 14363,66614,1082908,texture/stage_select.bmp 10001,66614,1097271,texture/sys.bmp 4544,66614,1107272,texture/sys02.bmp 6561,66614,1111816,texture/sys03.bmp 34217,50176,1118377,texture/thumbs.db 15363,21068,1152594,texture/util 53,196,1152594,texture/daisi_yushagauge.bmp 53,196,1152647,texture/systemui_base.bmp 53,196,1152700,texture/systemui_base02.bmp 15204,20480,1152753,texture/thumbs.db 273337,1285628,1167957,texture/op_tex 103270,263224,1167957,texture/ed00.bmp 7103,263222,1271227,texture/op00.bmp 2556,262324,1278330,texture/op01.bmp 112675,263224,1280886,texture/op02.bmp 520,66614,1393561,texture/op03.bmp 9619,66614,1394081,texture/op_npc.bmp 10803,66614,1403700,texture/party.bmp 26791,33792,1414503,texture/thumbs.db 3572,7680,1441294,texture/thumbs.db 191655,2108760,1444866,texture/titletex 9028,263222,1444866,texture/calvin_title.bmp 5267,8192,1453894,texture/thumbs.db 20525,263148,1459161,texture/title.bmp 27147,524488,1479686,texture/title.gim 4191,263222,1506833,texture/title_sky.bmp 125497,786488,1511024,texture/rs[ ` calvin_title.bmp 936,263222,1636521,texture/white.bmp 241924,3126550,1637457,texture/yushatex 241924,3126550,1637457,texture/monsters 9900,66614,1637457,texture/boss01.bmp 26540,263222,1647357,texture/boss02.bmp 11436,262420,1673897,texture/boss03.bmp 14306,263222,1685333,texture/boss04.bmp 11124,263222,1699639,texture/boss05.bmp 10084,263222,1710763,texture/boss06.bmp 17491,263222,1720847,texture/boss07.bmp 11365,263222,1738338,texture/boss08.bmp 11062,263222,1749703,texture/boss09.bmp 26389,263222,1760765,texture/boss10.bmp 3827,66614,1787154,texture/enemy01.bmp 6069,66614,1790981,texture/enemy02.bmp 7985,66614,1797050,texture/enemy03.bmp 8307,66614,1805035,texture/enemy04.bmp 6152,66614,1813342,texture/enemy05.bmp 9480,66614,1819494,texture/enemy06.bmp 5785,66614,1828974,texture/enemy07.bmp 8341,66614,1834759,texture/enemy08.bmp 9403,66614,1843100,texture/enemy09.bmp 7649,66614,1852503,texture/enemy10.bmp 19229,25600,1860152,texture/thumbs.db
wq223 Posted Monday at 05:38 AM Posted Monday at 05:38 AM On 2025/4/20 at AM8点48分, imranUSK said: 我尝试从文件中提取文件,但由于经验不足,无法成功。希望有人能帮我提取文件。我很乐意为您提供帮助。 研究成果.rar 1.71 MB · 9 downloads This script should be better handled import struct import zlib import sys from pathlib import Path def parse_index(input_path): index_data = [] current_path = [] with open(input_path, 'rb') as f: f.seek(44) while True: pos = f.tell() entry_type = struct.unpack('<H', f.read(2))[0] name_length = struct.unpack('<H', f.read(2))[0] if entry_type == 1: f.read(24) folder_name = f.read(name_length).decode('latin-1').rstrip('\x00') current_path = [folder_name] elif entry_type == 0: f.read(8) file_size = struct.unpack('<I', f.read(4))[0] uncompressed_size = struct.unpack('<I', f.read(4))[0] f.read(4) offset = struct.unpack('<I', f.read(4))[0] filename = f.read(name_length).decode('latin-1').rstrip('\x00') full_path = '/'.join(current_path + [filename]) index_data.append({ 'path': full_path, 'offset': offset, 'size': file_size, 'uncompressed_size': uncompressed_size }) else: break if f.tell() >= os.path.getsize(input_path): break return index_data def extract_files(input_path, output_dir, index_data): header_patterns = { b'\x04\x00\x00\x00': 8, b'\x08\x00\x00\x00': 12, b'\x0C\x00\x00\x00': 16, b'\x10\x00\x00\x00': 20, b'\x14\x00\x00\x00': 24, b'\x24\x00\x00\x00': 40, b'\x34\x00\x00\x00': 56 } with open(input_path, 'rb') as f: for entry in index_data: print(f"{entry['offset']} {entry['size']} {entry['path']}") output_path = Path(output_dir) / entry['path'] output_path.parent.mkdir(parents=True, exist_ok=True) f.seek(entry['offset']) full_data = f.read(entry['size']) skip_bytes = 0 for pattern, length in header_patterns.items(): if full_data.startswith(pattern): skip_bytes = length break compressed_data = full_data[skip_bytes:] try: decompressed = zlib.decompress(compressed_data) with open(output_path, 'wb') as out: out.write(decompressed) except Exception: pass if __name__ == '__main__': if len(sys.argv) != 3: print("Usage: python jrz.py <input_file> <output_dir>") sys.exit(1) input_file = sys.argv[1] output_dir = sys.argv[2] import os index_data = parse_index(input_file) extract_files(input_file, output_dir, index_data) jrz.py 2
imranUSK Posted Monday at 11:29 AM Author Posted Monday at 11:29 AM Can the file be returned to its original state after modifying it?
wq223 Posted Monday at 12:36 PM Posted Monday at 12:36 PM (edited) 1 hour ago, imranUSK said: Can the file be returned to its original state after modifying it? Some files have returned to their original state, but for those unknown files, you may need to consult more game information to determine what they represent, whether they are model, texture or audio or encrypted, to determine if there is a research direction. If there is no problem, you can tag the solution Edited Monday at 12:38 PM by wq223
imranUSK Posted Monday at 01:12 PM Author Posted Monday at 01:12 PM 31 minutes ago, wq223 said: Some files have returned to their original state, but for those unknown files, you may need to consult more game information to determine what they represent, whether they are model, texture or audio or encrypted, to determine if there is a research direction. If there is no problem, you can tag the solution I asked you because I'm working on a project to translate the game into Arabic. So I'll only replace the textures and text. I don't need the models or sounds.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now