|
1 | 1 | #!/usr/local/bin/python3
|
2 | 2 |
|
3 | 3 | import struct
|
| 4 | +import PIL.Image |
4 | 5 |
|
5 |
| -FILENAME = "Intro.bin" |
| 6 | +FILENAME = "Idle.bin" |
6 | 7 |
|
7 |
| -storyfile = open(FILENAME, "rb") |
| 8 | +storyfile = open(FILENAME, "rb+") |
8 | 9 |
|
9 | 10 | SNXROM = {}
|
10 | 11 |
|
|
32 | 33 | assetTablePointers = [0] * SNXROM['assetTableLengthWords']
|
33 | 34 | for i in range(SNXROM['assetTableLengthWords']):
|
34 | 35 | assetTablePointers[i] = struct.unpack('I', storyfile.read(4))[0]
|
35 |
| -#print("Asset table pointers:", [hex(i) for i in assetTablePointers]) |
| 36 | +print("Asset table pointers:", [hex(i) for i in assetTablePointers]) |
36 | 37 |
|
37 | 38 |
|
38 | 39 |
|
39 | 40 | # Go to the metadata table
|
40 | 41 | SNXROM['metaOffset'] = assetTablePointers[0]
|
41 | 42 | storyfile.seek(SNXROM['metaOffset'])
|
42 | 43 |
|
43 |
| -if 0x0000 != struct.unpack('h', storyfile.read(2))[0]: |
44 |
| - raise RuntimeError("Didn't find 0x0000 magic bytes") |
45 |
| -SNXROM['storyId'] = struct.unpack('h', storyfile.read(2))[0] |
46 |
| -SNXROM['numberOfEyeAnimations'] = struct.unpack('h', storyfile.read(2))[0] |
47 |
| -SNXROM['numberOfEyeBitmaps'] = struct.unpack('h', storyfile.read(2))[0] |
48 |
| -SNXROM['numberOfVideoSequences'] = struct.unpack('h', storyfile.read(2))[0] |
49 |
| -SNXROM['numberOfAudioBlocks'] = struct.unpack('h', storyfile.read(2))[0] |
50 |
| -print("Story ID: ", SNXROM['storyId']) |
51 |
| -print("Eye animations:", SNXROM['numberOfEyeAnimations']) |
52 |
| -print("Eye bitmaps:", SNXROM['numberOfEyeBitmaps']) |
53 |
| -print("Video seqs:", SNXROM['numberOfVideoSequences']) |
54 |
| -print("Audio blocks:", SNXROM['numberOfAudioBlocks']) |
| 44 | +if 0x0000 == struct.unpack('h', storyfile.read(2))[0]: |
| 45 | + SNXROM['storyId'] = struct.unpack('h', storyfile.read(2))[0] |
| 46 | + SNXROM['numberOfEyeAnimations'] = struct.unpack('h', storyfile.read(2))[0] |
| 47 | + SNXROM['numberOfEyeBitmaps'] = struct.unpack('h', storyfile.read(2))[0] |
| 48 | + SNXROM['numberOfVideoSequences'] = struct.unpack('h', storyfile.read(2))[0] |
| 49 | + SNXROM['numberOfAudioBlocks'] = struct.unpack('h', storyfile.read(2))[0] |
| 50 | + print("Story ID: ", SNXROM['storyId']) |
| 51 | + print("Eye animations:", SNXROM['numberOfEyeAnimations']) |
| 52 | + print("Eye bitmaps:", SNXROM['numberOfEyeBitmaps']) |
| 53 | + print("Video seqs:", SNXROM['numberOfVideoSequences']) |
| 54 | + print("Audio blocks:", SNXROM['numberOfAudioBlocks']) |
| 55 | +else: |
| 56 | + print("Didn't find 0x0000 magic bytes, skipping meta!") |
| 57 | + SNXROM['metaOffset'] = 0 |
| 58 | + |
55 | 59 |
|
56 | 60 | SNXROM['ROMfilesize'] = struct.unpack('i', storyfile.read(4))[0]
|
57 | 61 | print(SNXROM['ROMfilesize'])
|
58 | 62 |
|
59 | 63 | ##############
|
60 | 64 |
|
61 | 65 | SNXROM['audioOffset'] = assetTablePointers[-1]
|
62 |
| - |
| 66 | +SNXROM['marktable'] = [] |
| 67 | + |
63 | 68 | # verify audio offset
|
64 | 69 | print("Looking for audio at", hex(SNXROM['audioOffset']))
|
65 | 70 | storyfile.seek(SNXROM['audioOffset'])
|
66 | 71 |
|
67 |
| -if "AU" != storyfile.read(2).decode('utf-8'): |
68 |
| - raise RuntimeError("Didn't find AU at beginning of audio") |
69 |
| -print("Found beginning of audio") |
70 |
| -(samplerate, bitrate, channels, frame_count, file_len, mark_flag, silence_flag, \ |
71 |
| - mbf, pcs, rec, header_len) = \ |
72 |
| - struct.unpack('<HHHIIHHHHHH', storyfile.read(26)) |
73 |
| - |
74 |
| -print("Header Size (16b words):", hex(header_len)) |
75 |
| - |
76 |
| -print("Samplerate:", samplerate) |
77 |
| -print("Bitrate:", bitrate) |
78 |
| -print("Channels:", channels) |
79 |
| -print("Frame count:", frame_count) |
80 |
| -print("File len (16b words):", file_len) |
81 |
| -if (file_len * 2) / 80 != frame_count: |
82 |
| - print("Should be %d frames * 80 bytes per frame = %d total size" % (frame_count, file_len*2)) |
83 |
| -print("Mark flag:", mark_flag) |
84 |
| -print("Silence flag:", silence_flag) |
85 |
| -print("Header Size (16b words):", header_len) |
| 72 | +if b'AU' == storyfile.read(2): |
| 73 | + print("Found beginning of audio") |
| 74 | + (samplerate, bitrate, channels, frame_count, file_len, mark_flag, silence_flag, \ |
| 75 | + mbf, pcs, rec, header_len) = \ |
| 76 | + struct.unpack('<HHHIIHHHHHH', storyfile.read(26)) |
| 77 | + |
| 78 | + print("Header Size (16b words):", hex(header_len)) |
| 79 | + |
| 80 | + print("Samplerate:", samplerate) |
| 81 | + print("Bitrate:", bitrate) |
| 82 | + print("Channels:", channels) |
| 83 | + print("Frame count:", frame_count) |
| 84 | + print("File len (16b words):", file_len) |
| 85 | + if (file_len * 2) / 80 != frame_count: |
| 86 | + print("Should be %d frames * 80 bytes per frame = %d total size" % (frame_count, file_len*2)) |
| 87 | + print("Mark flag:", mark_flag) |
| 88 | + print("Silence flag:", silence_flag) |
| 89 | + print("Header Size (16b words):", header_len) |
| 90 | + |
| 91 | + # toss 0xFFFFFFFF |
| 92 | + storyfile.read(4) |
| 93 | + |
| 94 | + table_size = struct.unpack('<H', storyfile.read(2))[0] |
| 95 | + #print(table_size * 2) |
| 96 | + mark_entries = header_len*2 - (storyfile.tell() - SNXROM['audioOffset']) - 4 |
| 97 | + print("entries:", mark_entries) |
| 98 | + |
| 99 | + SNXROM['marktable'] = [] |
| 100 | + for i in range(mark_entries // 2): |
| 101 | + SNXROM['marktable'].append(struct.unpack('<H', storyfile.read(2))[0]) |
| 102 | + totaltime = 0; |
| 103 | + for i in range(len(SNXROM['marktable']) // 2): |
| 104 | + totaltime+= SNXROM['marktable'][2*i+0] |
| 105 | + print( SNXROM['marktable'][2*i+0], SNXROM['marktable'][2*i+1]) |
| 106 | + print("total length of motion mark table (s):", totaltime / 1000.0) |
| 107 | + |
| 108 | + print("Actual audio data starts at", hex(SNXROM['audioOffset'] + header_len*2)) |
| 109 | + print("and ends at ", hex(SNXROM['audioOffset'] + header_len*2 + file_len*2)) |
| 110 | + |
| 111 | +else: |
| 112 | + print("Didn't find AU at beginning of audio, no mark table!") |
| 113 | + |
| 114 | +# The rest are eyeball frames, extract them! |
| 115 | + |
| 116 | +for i,pointer in enumerate(assetTablePointers): |
| 117 | + if SNXROM['metaOffset'] and (i == 0): |
| 118 | + continue # skip metadata table if it exists |
| 119 | + if SNXROM['marktable'] and (i == len(assetTablePointers)-1): |
| 120 | + continue # skip audio table if it exists |
| 121 | + print(i,pointer) |
| 122 | + storyfile.seek(pointer) |
| 123 | + image_data = storyfile.read(128*128*2) |
| 124 | + |
| 125 | + print(len(image_data)) |
| 126 | + PIL.Image.frombytes('RGB', (128,128), image_data, 'raw', 'BGR;16').save(f'eye{i}.png') |
| 127 | + continue |
| 128 | + |
| 129 | + logobytes = bytearray(b'') |
| 130 | + for pix in PIL.Image.open("logo.png").convert('RGB').getdata(): |
| 131 | + r = (pix[0] >> 3) & 0x1F |
| 132 | + g = (pix[1] >> 2) & 0x3F |
| 133 | + b = (pix[2] >> 3) & 0x1F |
| 134 | + logobytes = logobytes + struct.pack('H', (r << 11) + (g << 5) + b) |
| 135 | + #print(len(logobytes)) |
| 136 | + storyfile.write(logobytes) |
| 137 | + |
86 | 138 |
|
87 | 139 | #print(storyfile.tell() - SNXROM['audioOffset'])
|
88 | 140 |
|
89 |
| -# toss 0xFFFFFFFF |
90 |
| -storyfile.read(4) |
91 |
| - |
92 |
| -table_size = struct.unpack('<H', storyfile.read(2))[0] |
93 |
| -#print(table_size * 2) |
94 |
| -mark_entries = header_len*2 - (storyfile.tell() - SNXROM['audioOffset']) - 4 |
95 |
| -print("entries:", mark_entries) |
96 |
| - |
97 |
| -marktable = [] |
98 |
| -for i in range(mark_entries // 2): |
99 |
| - marktable.append(struct.unpack('<H', storyfile.read(2))[0]) |
100 |
| -totaltime = 0; |
101 |
| -for i in range(len(marktable) // 2): |
102 |
| - totaltime+=marktable[2*i+0] |
103 |
| - print(marktable[2*i+0], marktable[2*i+1]) |
104 |
| -print("total length of motion mark table (s):", totaltime / 1000.0) |
105 |
| - |
106 |
| -print("Actual audio data starts at", hex(SNXROM['audioOffset'] + header_len*2)) |
107 |
| -print("and ends at ", hex(SNXROM['audioOffset'] + header_len*2 + file_len*2)) |
108 | 141 | """
|
109 | 142 |
|
110 |
| -
|
| 143 | +seen.add(i) |
| 144 | + asset = (c_uint16 * (128*128)).from_buffer(content, assetTablePointers[i]) |
| 145 | + PIL.Image.frombytes('RGB', (128,128), string_at(asset, 128*128*2), 'raw', 'RGB;16').save(f'eye{i}.png') |
111 | 146 |
|
112 | 147 | SNXROM['leftEyeOffset'] = struct.unpack('I', storyfile.read(4))[0]
|
113 | 148 | SNXROM['rightEyeOffset'] = struct.unpack('I', storyfile.read(4))[0]
|
|
122 | 157 | print(storyfile.tell())
|
123 | 158 |
|
124 | 159 | """
|
| 160 | + |
| 161 | +storyfile.close() |
0 commit comments