Skip to content

Commit 8e3ebf4

Browse files
authored
Merge pull request #150 from RevoSucks/extract_assets_2
decompress Yay0s and extract PRESJPEGs
2 parents f83ef11 + 12d06db commit 8e3ebf4

File tree

5 files changed

+259
-5
lines changed

5 files changed

+259
-5
lines changed

src/3FB0.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ typedef struct PERSSZP {
2323

2424
typedef struct PRESJPEG {
2525
/* 0x00 */ char magic[0x8]; // PRESJPEG
26-
/* 0x08 */ u32 unk_08;
27-
/* 0x0C */ u32 unk_0C;
26+
/* 0x08 */ u32 unk_08; // header size? always 0x10
27+
/* 0x0C */ u32 unk_0C;
2828
} PRESJPEG; // size = 0x10
2929

3030
typedef struct Yay0 {

tools/decompress_persszp.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import re
5+
import os
6+
from pathlib import Path
7+
import crunch64
8+
9+
# PERS-SZP decompressor v0.1 (WIP)
10+
# Usage: decompress_persszp.py [path to PERS-SZP file]
11+
12+
# Read a big endian 32-bit value from a bytearray file with a given offset.
13+
def read_32_be_value(file_arr, i):
14+
return (file_arr[i + 0] << 24) + (file_arr[i + 1] << 16) \
15+
+ (file_arr[i + 2] << 8) + (file_arr[i + 3])
16+
17+
assets_path = "assets/us/"
18+
19+
filepath = Path(sys.argv[1])
20+
filename = os.path.splitext(os.path.basename(filepath))[0]
21+
22+
file = open(filepath, 'rb')
23+
file_header = bytearray(file.read(0xC)) # First, we need to fetch the header size. This is right after the magic part of the header.
24+
25+
# Check for 50 45 52 53 2D 53 5A 50, which is "PERS-SZP"
26+
if file_header[0] != 0x50 or file_header[1] != 0x45 or file_header[2] != 0x52 or file_header[3] != 0x53 or file_header[4] != 0x2D or file_header[5] != 0x53 or file_header[6] != 0x5A or file_header[7] != 0x50:
27+
print(filepath, "File magic is NOT 'PERS-SZP'. Please pass a valid file.")
28+
sys.exit(1)
29+
30+
header_size = read_32_be_value(file_header, 8)
31+
# Now that we have the header size, seek past the header.
32+
file.seek(header_size, os.SEEK_SET)
33+
yay0_file = file.read()
34+
35+
decompressed = crunch64.yay0.decompress(yay0_file)
36+
file.close()
37+
file = open(filepath, 'wb')
38+
file.write(decompressed)

tools/extract_assets.sh

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
# TODO: The packed bins do not depend on their extracted contents yet so modifying these files will do nothing. Add dependencies
66

7+
# Unpack/move any multi or single asset bins.
78
tools/unpack_asset.py assets/us/n64_logo_texture.bin
89
tools/unpack_asset.py assets/us/fonts.bin
910
tools/unpack_asset.py assets/us/backgrounds.bin
@@ -52,3 +53,180 @@ tools/unpack_asset.py assets/us/textdata.bin
5253
tools/unpack_asset.py assets/us/798CD0.bin
5354
tools/unpack_asset.py assets/us/snap_mode_ui.bin
5455
tools/unpack_asset.py assets/us/copyright.bin
56+
57+
# Next, decompress all the PERS-SZP files with a Yay0. TODO: Roll these up
58+
# into recursive calls. All of the files within an asset bin SHOULD be the
59+
# same type, but it in theory it isnt guaranteed to be. Does the game ensure
60+
# that? If so, we should be able to safely roll these up.
61+
tools/decompress_persszp.py assets/us/n64_logo_texture/0/file.bin
62+
tools/decompress_persszp.py assets/us/fonts/0/file.bin
63+
tools/decompress_persszp.py assets/us/fonts/1/file.bin
64+
tools/decompress_persszp.py assets/us/fonts/2/file.bin
65+
tools/decompress_persszp.py assets/us/fonts/3/file.bin
66+
tools/decompress_persszp.py assets/us/fonts/4/file.bin
67+
tools/decompress_persszp.py assets/us/fonts/5/file.bin
68+
tools/decompress_persszp.py assets/us/battle_headers/0/file.bin
69+
tools/decompress_persszp.py assets/us/battle_headers/1/file.bin
70+
tools/decompress_persszp.py assets/us/battle_headers/2/file.bin
71+
tools/decompress_persszp.py assets/us/battle_headers/3/file.bin
72+
tools/decompress_persszp.py assets/us/battle_headers/4/file.bin
73+
tools/decompress_persszp.py assets/us/battle_headers/5/file.bin
74+
tools/decompress_persszp.py assets/us/battle_headers/6/file.bin
75+
tools/decompress_persszp.py assets/us/battle_headers/7/file.bin
76+
tools/decompress_persszp.py assets/us/battle_headers/8/file.bin
77+
tools/decompress_persszp.py assets/us/battle_headers/9/file.bin
78+
tools/decompress_persszp.py assets/us/battle_headers/10/file.bin
79+
tools/decompress_persszp.py assets/us/battle_headers/11/file.bin
80+
tools/decompress_persszp.py assets/us/battle_headers/12/file.bin
81+
tools/decompress_persszp.py assets/us/common_menu0_ui/0/file.bin
82+
tools/decompress_persszp.py assets/us/common_menu1_ui/0/file.bin
83+
tools/decompress_persszp.py assets/us/common_menu2_ui/0/file.bin
84+
tools/decompress_persszp.py assets/us/area_select_ui/0/file.bin
85+
tools/decompress_persszp.py assets/us/menu_select_ui/0/file.bin
86+
tools/decompress_persszp.py assets/us/stadium_select_ui/0/file.bin
87+
tools/decompress_persszp.py assets/us/title_ui/0/file.bin
88+
tools/decompress_persszp.py assets/us/52F6D0/0/file.bin
89+
tools/decompress_persszp.py assets/us/battle_portraits/0/file.bin
90+
tools/decompress_persszp.py assets/us/battle_portraits/1/file.bin
91+
tools/decompress_persszp.py assets/us/battle_portraits/2/file.bin
92+
tools/decompress_persszp.py assets/us/battle_portraits/3/file.bin
93+
tools/decompress_persszp.py assets/us/battle_portraits/4/file.bin
94+
tools/decompress_persszp.py assets/us/battle_portraits/5/file.bin
95+
tools/decompress_persszp.py assets/us/battle_portraits/6/file.bin
96+
tools/decompress_persszp.py assets/us/battle_portraits/7/file.bin
97+
tools/decompress_persszp.py assets/us/battle_portraits/8/file.bin
98+
tools/decompress_persszp.py assets/us/battle_portraits/9/file.bin
99+
tools/decompress_persszp.py assets/us/battle_portraits/10/file.bin
100+
tools/decompress_persszp.py assets/us/battle_portraits/11/file.bin
101+
tools/decompress_persszp.py assets/us/battle_portraits/12/file.bin
102+
tools/decompress_persszp.py assets/us/battle_portraits/13/file.bin
103+
tools/decompress_persszp.py assets/us/battle_portraits/14/file.bin
104+
tools/decompress_persszp.py assets/us/battle_portraits/15/file.bin
105+
tools/decompress_persszp.py assets/us/battle_portraits/16/file.bin
106+
tools/decompress_persszp.py assets/us/battle_portraits/17/file.bin
107+
tools/decompress_persszp.py assets/us/battle_portraits/18/file.bin
108+
tools/decompress_persszp.py assets/us/battle_portraits/19/file.bin
109+
tools/decompress_persszp.py assets/us/battle_portraits/20/file.bin
110+
tools/decompress_persszp.py assets/us/battle_portraits/21/file.bin
111+
tools/decompress_persszp.py assets/us/battle_portraits/22/file.bin
112+
tools/decompress_persszp.py assets/us/battle_portraits/23/file.bin
113+
tools/decompress_persszp.py assets/us/battle_portraits/24/file.bin
114+
tools/decompress_persszp.py assets/us/battle_portraits/25/file.bin
115+
tools/decompress_persszp.py assets/us/battle_portraits/26/file.bin
116+
tools/decompress_persszp.py assets/us/battle_portraits/27/file.bin
117+
tools/decompress_persszp.py assets/us/battle_portraits/28/file.bin
118+
tools/decompress_persszp.py assets/us/battle_portraits/29/file.bin
119+
tools/decompress_persszp.py assets/us/battle_portraits/30/file.bin
120+
tools/decompress_persszp.py assets/us/battle_portraits/31/file.bin
121+
tools/decompress_persszp.py assets/us/battle_portraits/32/file.bin
122+
tools/decompress_persszp.py assets/us/battle_portraits/33/file.bin
123+
tools/decompress_persszp.py assets/us/battle_portraits/34/file.bin
124+
tools/decompress_persszp.py assets/us/battle_portraits/35/file.bin
125+
tools/decompress_persszp.py assets/us/battle_portraits/36/file.bin
126+
tools/decompress_persszp.py assets/us/battle_portraits/37/file.bin
127+
tools/decompress_persszp.py assets/us/battle_portraits/38/file.bin
128+
tools/decompress_persszp.py assets/us/battle_portraits/39/file.bin
129+
tools/decompress_persszp.py assets/us/battle_portraits/40/file.bin
130+
tools/decompress_persszp.py assets/us/battle_portraits/41/file.bin
131+
tools/decompress_persszp.py assets/us/battle_portraits/42/file.bin
132+
tools/decompress_persszp.py assets/us/battle_portraits/43/file.bin
133+
tools/decompress_persszp.py assets/us/battle_portraits/44/file.bin
134+
tools/decompress_persszp.py assets/us/battle_portraits/45/file.bin
135+
tools/decompress_persszp.py assets/us/battle_portraits/46/file.bin
136+
tools/decompress_persszp.py assets/us/battle_portraits/47/file.bin
137+
tools/decompress_persszp.py assets/us/battle_portraits/48/file.bin
138+
tools/decompress_persszp.py assets/us/battle_portraits/49/file.bin
139+
tools/decompress_persszp.py assets/us/battle_portraits/50/file.bin
140+
tools/decompress_persszp.py assets/us/battle_portraits/51/file.bin
141+
tools/decompress_persszp.py assets/us/battle_portraits/52/file.bin
142+
tools/decompress_persszp.py assets/us/battle_portraits/53/file.bin
143+
tools/decompress_persszp.py assets/us/stadium_models/0/file.bin
144+
tools/decompress_persszp.py assets/us/stadium_models/1/file.bin
145+
tools/decompress_persszp.py assets/us/stadium_models/2/file.bin
146+
tools/decompress_persszp.py assets/us/stadium_models/3/file.bin
147+
tools/decompress_persszp.py assets/us/stadium_models/4/file.bin
148+
tools/decompress_persszp.py assets/us/stadium_models/5/file.bin
149+
tools/decompress_persszp.py assets/us/stadium_models/6/file.bin
150+
tools/decompress_persszp.py assets/us/stadium_models/7/file.bin
151+
tools/decompress_persszp.py assets/us/stadium_models/8/file.bin
152+
tools/decompress_persszp.py assets/us/stadium_models/9/file.bin
153+
tools/decompress_persszp.py assets/us/stadium_models/10/file.bin
154+
tools/decompress_persszp.py assets/us/stadium_models/11/file.bin
155+
tools/decompress_persszp.py assets/us/stadium_models/12/file.bin
156+
tools/decompress_persszp.py assets/us/stadium_models/13/file.bin
157+
tools/decompress_persszp.py assets/us/stadium_models/14/file.bin
158+
tools/decompress_persszp.py assets/us/stadium_models/15/file.bin
159+
tools/decompress_persszp.py assets/us/stadium_models/16/file.bin
160+
tools/decompress_persszp.py assets/us/stadium_models/17/file.bin
161+
tools/decompress_persszp.py assets/us/5C7A70/0/file.bin
162+
tools/decompress_persszp.py assets/us/pokedex_area_model/0/file.bin
163+
tools/decompress_persszp.py assets/us/sushi_go_round/0/file.bin
164+
tools/decompress_persszp.py assets/us/order_select_ui/0/file.bin
165+
tools/decompress_persszp.py assets/us/cup_ball_select_ui/0/file.bin
166+
tools/decompress_persszp.py assets/us/trade_select_ui/0/file.bin
167+
tools/decompress_persszp.py assets/us/kids_club_select_ui/0/file.bin
168+
tools/decompress_persszp.py assets/us/gym_leader_castle_clear_gfx/0/file.bin
169+
tools/decompress_persszp.py assets/us/rental_rules_ui/0/file.bin
170+
tools/decompress_persszp.py assets/us/sushi_go_round_sprites/0/file.bin
171+
tools/decompress_persszp.py assets/us/kids_club_game_ui/0/file.bin
172+
tools/decompress_persszp.py assets/us/gallery_ui/0/file.bin
173+
tools/decompress_persszp.py assets/us/album_ui/0/file.bin
174+
tools/decompress_persszp.py assets/us/6A9750/0/file.bin
175+
tools/decompress_persszp.py assets/us/snap_select_ui/0/file.bin
176+
tools/decompress_persszp.py assets/us/cup_clear_ui/0/file.bin
177+
tools/decompress_persszp.py assets/us/gb_pak_select_ui/0/file.bin
178+
tools/decompress_persszp.py assets/us/victory_palace_plate_text/0/file.bin
179+
tools/decompress_persszp.py assets/us/run_rattata_run_flag_gfx/0/file.bin
180+
tools/decompress_persszp.py assets/us/kanto_gb_map/0/file.bin
181+
tools/decompress_persszp.py assets/us/6E2F90/0/file.bin
182+
tools/decompress_persszp.py assets/us/battle_ui/0/file.bin
183+
tools/decompress_persszp.py assets/us/transfer_pak_error_ui/0/file.bin
184+
tools/decompress_persszp.py assets/us/6EB340/0/file.bin
185+
tools/decompress_persszp.py assets/us/6EB340/1/file.bin
186+
tools/decompress_persszp.py assets/us/6EB340/2/file.bin
187+
tools/decompress_persszp.py assets/us/6EB340/3/file.bin
188+
tools/decompress_persszp.py assets/us/6EC4D0/0/file.bin
189+
tools/decompress_persszp.py assets/us/badge_ui/0/file.bin
190+
tools/decompress_persszp.py assets/us/gym_leader_castle_ui/0/file.bin
191+
tools/decompress_persszp.py assets/us/798CD0/0/file.bin
192+
tools/decompress_persszp.py assets/us/snap_mode_ui/0/file.bin
193+
tools/decompress_persszp.py assets/us/copyright/0/file.bin
194+
195+
# Extract all PRESJPEG files.
196+
tools/extract_presjpeg.py assets/us/backgrounds/0/file.bin
197+
tools/extract_presjpeg.py assets/us/backgrounds/1/file.bin
198+
tools/extract_presjpeg.py assets/us/backgrounds/2/file.bin
199+
tools/extract_presjpeg.py assets/us/backgrounds/3/file.bin
200+
tools/extract_presjpeg.py assets/us/backgrounds/4/file.bin
201+
tools/extract_presjpeg.py assets/us/backgrounds/5/file.bin
202+
tools/extract_presjpeg.py assets/us/backgrounds/6/file.bin
203+
tools/extract_presjpeg.py assets/us/backgrounds/7/file.bin
204+
tools/extract_presjpeg.py assets/us/backgrounds/8/file.bin
205+
tools/extract_presjpeg.py assets/us/backgrounds/9/file.bin
206+
tools/extract_presjpeg.py assets/us/backgrounds/10/file.bin
207+
tools/extract_presjpeg.py assets/us/backgrounds/11/file.bin
208+
tools/extract_presjpeg.py assets/us/backgrounds/12/file.bin
209+
tools/extract_presjpeg.py assets/us/backgrounds/13/file.bin
210+
tools/extract_presjpeg.py assets/us/backgrounds/14/file.bin
211+
tools/extract_presjpeg.py assets/us/backgrounds/15/file.bin
212+
tools/extract_presjpeg.py assets/us/backgrounds/16/file.bin
213+
tools/extract_presjpeg.py assets/us/backgrounds/17/file.bin
214+
tools/extract_presjpeg.py assets/us/backgrounds/18/file.bin
215+
tools/extract_presjpeg.py assets/us/backgrounds/19/file.bin
216+
tools/extract_presjpeg.py assets/us/backgrounds/20/file.bin
217+
tools/extract_presjpeg.py assets/us/6CA730/0/file.bin
218+
tools/extract_presjpeg.py assets/us/6CA730/1/file.bin
219+
tools/extract_presjpeg.py assets/us/6CA730/2/file.bin
220+
tools/extract_presjpeg.py assets/us/6CA730/3/file.bin
221+
tools/extract_presjpeg.py assets/us/6CA730/4/file.bin
222+
tools/extract_presjpeg.py assets/us/6CA730/5/file.bin
223+
tools/extract_presjpeg.py assets/us/6CA730/6/file.bin
224+
tools/extract_presjpeg.py assets/us/6CA730/7/file.bin
225+
tools/extract_presjpeg.py assets/us/6CA730/8/file.bin
226+
tools/extract_presjpeg.py assets/us/6CA730/9/file.bin
227+
tools/extract_presjpeg.py assets/us/6CA730/10/file.bin
228+
tools/extract_presjpeg.py assets/us/6CA730/11/file.bin
229+
tools/extract_presjpeg.py assets/us/6CA730/12/file.bin
230+
tools/extract_presjpeg.py assets/us/6CA730/13/file.bin
231+
tools/extract_presjpeg.py assets/us/6CA730/14/file.bin
232+
tools/extract_presjpeg.py assets/us/6CA730/15/file.bin

tools/extract_presjpeg.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import re
5+
import os
6+
from pathlib import Path
7+
import crunch64
8+
9+
# PRESJPEG extractor
10+
# Usage: decompress_presjpeg.py [path to PRESJPEG file]
11+
12+
# Read a big endian 32-bit value from a bytearray file with a given offset.
13+
def read_32_be_value(file_arr, i):
14+
return (file_arr[i + 0] << 24) + (file_arr[i + 1] << 16) \
15+
+ (file_arr[i + 2] << 8) + (file_arr[i + 3])
16+
17+
assets_path = "assets/us/"
18+
19+
filepath = Path(sys.argv[1])
20+
filename = os.path.splitext(os.path.basename(filepath))[0]
21+
22+
file = open(filepath, 'rb')
23+
file_header = bytearray(file.read(0xC)) # First, we need to fetch the header size. This is right after the magic part of the header.
24+
25+
# Check for 50 52 45 53 4A 50 45 47, which is "PRESJPEG"
26+
if file_header[0] != 0x50 or file_header[1] != 0x52 or file_header[2] != 0x45 or file_header[3] != 0x53 or file_header[4] != 0x4A or file_header[5] != 0x50 or file_header[6] != 0x45 or file_header[7] != 0x47:
27+
print(filepath, "File magic is NOT 'PRESJPEG'. Please pass a valid file.")
28+
sys.exit(1)
29+
30+
header_size = read_32_be_value(file_header, 8)
31+
file.seek(header_size, os.SEEK_SET)
32+
jpeg_file = bytearray(file.read())
33+
file.close()
34+
file = open(filepath, 'wb')
35+
file.write(jpeg_file)
36+
filepath.rename(filepath.with_suffix('.jpeg'))

tools/unpack_asset.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ def read_32_be_value(file_arr, i):
2929
#print("[DEBUG] File name:", filename)
3030

3131
# Magic check for multi asset bin. If no match, treat as a single asset bin, and just copy it to the expected folder.
32-
if file_header[0] != 0x00 or file_header[1] != 0x00 or file_header[2] != 0x00 or file_header[3] != 0x00 or file_header[4] != 0x00 or file_header[5] != 0x00 or file_header[6] != 0x00 or file_header[7] != 0x00:
33-
file_path_to_write = assets_path + filename + "/0/0.bin"
32+
# For some reason, stadium_models.bin breaks this pattern and uses this for something. (HACK: Workaround by just not checking the 4th byte.)
33+
# TODO: Properly handle this
34+
if file_header[0] != 0x00 or file_header[1] != 0x00 or file_header[2] != 0x00 or file_header[4] != 0x00 or file_header[5] != 0x00 or file_header[6] != 0x00 or file_header[7] != 0x00:
35+
file_path_to_write = assets_path + filename + "/0/file.bin"
3436
os.makedirs(os.path.dirname(file_path_to_write), exist_ok=True)
3537
with open(file_path_to_write, 'wb') as f:
3638
fin = open(filepath, 'rb')
@@ -59,7 +61,7 @@ def read_32_be_value(file_arr, i):
5961
# Seek to the file offset.
6062
file.seek(bin_offset, os.SEEK_SET)
6163
sub_file_bytes = bytearray(file.read(bin_size))
62-
file_path_to_write = assets_path + filename + "/" + str(file_num) + "/" + str(file_num) + ".bin"
64+
file_path_to_write = assets_path + filename + "/" + str(file_num) + "/file.bin"
6365
#print("[DEBUG] Path to write:", file_path_to_write)
6466
os.makedirs(os.path.dirname(file_path_to_write), exist_ok=True)
6567
file_to_write = open(file_path_to_write, 'wb')

0 commit comments

Comments
 (0)