-
Notifications
You must be signed in to change notification settings - Fork 1
/
golden sun tutorial in progress.txt
835 lines (702 loc) · 48 KB
/
golden sun tutorial in progress.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
press start is placed in oam tiles, first tile at 06010000
breakpoint works at 08004016 stmia r3!, {r0-r2} some kind of DMA transer, source address:
r0 = 020367bc, and our tile data is placed exactly there.
break at this address 03003628 strb r3, [r1], #0x1. looks like some kind of custom unpacking routine. and this byte was previously loaded from [r0] 080F38BD (f38bd in ROM)
need to disasm this. that's lz77 (which is technically LZSS, as it has bit flags for defining block type):
Flag data (8bit)
Bit 0-7 Type Flags for next 8 Blocks, MSB first
Block Type 0 - Uncompressed - Copy 1 Byte from Source to Dest
Bit 0-7 One data byte to be copied to dest
Block Type 1 - Compressed - Copy X bytes Y bytes back
XX YY
no sign of size data, cause it is destroyed by inline character
code analys shows that control bytes are XY ZZ, where Y is 4bit length(zero-based), XZZ - 24 bit distance.
if length == 0, then it's expanded length mode: X0 ZZ LL, where length = LL + 0x10, distance = xZZ
if length == 0, distance == 0; end of compressed stream
These are very important for lz tool programming, as it was close to impossible to catch that imperical.
Bytes of code, we've found in iwram, are found in one location of ROM: around 0x1B78 (function starts at 0x8001B70), so code was copied to iwram from this location and it's more comfortable to disassemble code from actual ROM.
tracing back from iwram code is impossible - breaks don't work in vbasdlh and no$gba, so jump to the end of subroutine, code retruns to
08005414 BL unpackLZ
set a bp for execution
Breakpoint 0 reached
R00=08324be8 R04=030077ff R08=08324be8 R12=000000e0
R01=02010000 R05=00000096 R09=00000001 R13=03007e50
R02=84000096 R06=0300355c R10=02010000 R14=080053fd
R03=040000d4 R07=00000005 R11=03007e76 R15=08005416
CPSR=2000003f (..C...T Mode: 1f)
in R0 is another pointer. but for second time we will have our pointer. make a tracelog:
debugger> trace once off
debugger> trace file log.txt
debugger> trace start
debugger> c
Continuing after breakpoint
Breakpoint 0 reached
R00=080f38bc R04=030077ff R08=080f38bc R12=08005419
R01=020367bc R05=00000096 R09=00000001 R13=03007e50
R02=84000096 R06=0300355c R10=020367bc R14=080053fd
R03=040000d4 R07=00000014 R11=03007e76 R15=08005416
CPSR=2000003f (..C...T Mode: 1f)
08005412 4651 mov r1, r10
> 08005414 f001 bl $080072fc
debugger> trace stop
08004904 6820 ldr r0, [r4, #0x0] R00=00000000 R01=00000400 R02=00000400 R03=00000100 R04=03001e50
seek up:
080F2A0A 481e ldr r0, [$080f2a84] (=$080f38bc)
80f2a84 - is a ptr table. 80f38bc - pointer for first flag byte
30037a8 from ida is exit. R0 will have 8F39AB: last byte of compressed chunk to check our decompressor (F0 bytes for whole compressed gfx piece)
one can think, that uncompressed size is (20 tiles 8x8 pixels)/2 (as it's 4bpp) = 0x280 bytes. But remember, what we saw: gfx was transfered via DMA. and If we see at outPtr in the beginning and end: 2036abc - 20367bc = 0x300 bytes. What is actually encoded in compressed stream.
40 FF 0101: means FF and then distance is 1 and length is 2. length > distance, which copies bytes, which were copied before, but in the same LZ reference pair.
testing encode work:
*Main> findLzMatch "abcabcabc" "zzzzabczz"
(5,3)
*Main> findLzMatch "abcabcabc" "zzabzzzabczz"
(5,3)
*Main> findLzMatch "abcabcabc" "zzabczzzabczz"
(5,3)
*Main> findLzMatch "abcabcabc" "zzabczzzabzz"
(10,3)
*Main> findLzMatch "abcabcabc" "zzzzzzzzzzzabc"
(3,9)
FFFFFF00F011110FF001001FF0FFFF10F0111101F0010000FFFF0000111101
00
FFFFFF00F011110FF001001FF0FFFF10F001
you can either math it as refer (len 3, dist 8) or emit as separate symbol and then do refer (len 11, dist 20)
the last one is called LZ non-greedy parsing approach: www.cs.uvic.ca/~nigelh/Publications/LZ-non-greedy.pdf
http://www.gzip.org/algorithm.txt
After encoding new russian gfx takes 251 bytes instead of 238 - large difference due to cyrilic glyph complicated form, not because of encoder issues. Even if I delete non-used blank tiles, which are not used as sprite tiles, LZ encoded block stays the same size.
Let's put it in the end of rom: 0x7F D4 C0 is empty, copy compressed piece there. change pointer at f2a84 to C0 D4 7F 08. We could also store changed gfx at the end of ROM in plain view, but that will require to catch corresponding pointer in code (as that's the only gfx piece in plain view), and add code for plain gfx copy.
It's clear now, we need one more sprite for the last letter in "START" word to display and move whole text to the left for proper centering.
07000012 is X of left most sprite (0x50) breakpoint at it before sprite appears
debugger> bpw 0x07000010 4
Added break on write at 07000010 for 4 bytes
debugger> c
Breakpoint (on write) address 07000010 old:4050407c new:4050407c
R00=0300355c R04=00000100 R08=0300347c R12=08003651
R01=07000000 R05=03001e44 R09=03001d24 R13=03007e34
R02=84000100 R06=fffffe00 R10=03007e3c R14=08003687
R03=040000e0 R07=0300347c R11=00000001 R15=080036a8
CPSR=0000003f (......T Mode: 1f)
080036a4 c307 stmia r3!, {r0-r2}
> 080036a6 3b0c sub r3, #0xc
080036a8 4b22 ldr r3, [$08003734] (=$040000d4)
That's DMA: source in R0: 300355c, destination is R01=07000000
At 300356e is our X coord
full tracing:
R00=00000001 R04=03001e50 R08=0300347c R12=00000000
R01=03001d28 R05=03001a10 R09=03001d24 R13=03007e3c
R02=0000fffe R06=fffffe00 R10=03007e3c R14=08003e41
R03=00000000 R07=0300347c R11=00000001 R15=08003326
CPSR=2000003f (..C...T Mode: 1f)
08003324 880a ldrh r2, [r1, #0x0]
> 08003324 880a ldrh r2, [r1, #0x0]
08003326 1c03 add r3, r0, #0x0
debugger> trace once off
debugger> trace file log.txt
debugger> trace start
debugger> c
R00=00000001 R04=03001e50 R08=0300347c R12=00000000
R01=03001d28 R05=03001a10 R09=03001d24 R13=03007e3c
R02=0000fffe R06=fffffe00 R10=03007e3c R14=08003e41
R03=00000000 R07=0300347c R11=00000001 R15=08003326
CPSR=2000003f (..C...T Mode: 1f)
08003324 880a ldrh r2, [r1, #0x0]
> 08003324 880a ldrh r2, [r1, #0x0]
08003326 1c03 add r3, r0, #0x0
debugger> trace stop
debugger> bpw 07000000
bpw {address} {size} Break on write
debugger> bpw 07000000 4
Added break on write at 07000000 for 4 bytes
debugger> c
Breakpoint (on write) address 07000000 old:4090407c new:4090407c
R00=0300355c R04=00000100 R08=0300347c R12=08003651
R01=07000000 R05=03001e44 R09=03001d24 R13=03007e34
R02=84000100 R06=fffffe00 R10=03007e3c R14=08003687
R03=040000e0 R07=0300347c R11=00000001 R15=080036a8
CPSR=0000003f (......T Mode: 1f)
080036a4 c307 stmia r3!, {r0-r2}
> 080036a6 3b0c sub r3, #0xc
080036a8 4b22 ldr r3, [$08003734] (=$040000d4)
debugger> mb 0300355c
0300355c 7c 40 90 40 08 00 00 00 7c 40 70 40 04 00 00 00 |@.@....|@p@....
0300356c 7c 40 50 40 00 00 00 00 c0 00 c0 00 c0 00 c0 00 |@P@............
0300357c c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
0300358c c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
0300359c c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035ac c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035bc c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035cc c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035dc c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035ec c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035fc c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
0300360c c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
0300361c c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
0300362c c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
0300363c c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
0300364c c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
debugger> bpw 0300356c
bpw {address} {size} Break on write
debugger> bpw 0300356c 4
Added break on write at 0300356c for 4 bytes
debugger> c
Breakpoint (on write) address 0300356c old:4050407c new:4050407c
R00=03003574 R04=00000000 R08=00000000 R12=00000001
R01=0000007e R05=00000000 R09=0300355c R13=03007e14
R02=03001400 R06=00000000 R10=03007e3c R14=03003994
R03=03003514 R07=4050407c R11=00000001 R15=030039c0
CPSR=2000001f (..C.... Mode: 1f)
030039b8 e8a00180 stmia r0!, {r7,r8}
> 030039bc e2511001 subs r1, r1, #0x1
030039c0 0a000008 beq $030039e8
debugger> mb 03003514
03003514 08 35 00 03 7c 40 90 40 08 00 00 00 00 00 00 00 .5..|@.@........
03003524 00 40 00 40 0c 00 00 00 00 00 00 00 00 40 00 40 .@.@.........@.@
03003534 10 00 00 00 c0 00 c0 00 a0 20 00 40 c0 00 c0 00 ......... .@....
03003544 a0 20 00 40 c0 00 c0 00 a0 20 00 40 c0 00 c0 00 . .@..... .@....
03003554 a0 20 00 40 c0 00 c0 00 7c 40 90 40 08 00 00 00 . .@....|@.@....
03003564 7c 40 70 40 04 00 00 00 7c 40 50 40 00 00 00 00 |@p@....|@P@....
03003574 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
03003584 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
03003594 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035a4 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035b4 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035c4 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035d4 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035e4 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
030035f4 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
03003604 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 c0 00 ................
debugger> bpw 03003518 4
Added break on write at 03003518 for 4 bytes
debugger> c
Breakpoint (on write) address 07000000 old:4090407c new:4090407c
R00=0300355c R04=00000100 R08=0300347c R12=08003651
R01=07000000 R05=03001e44 R09=03001d24 R13=03007e34
R02=84000100 R06=fffffe00 R10=03007e3c R14=08003687
R03=040000e0 R07=0300347c R11=00000001 R15=080036a8
CPSR=0000003f (......T Mode: 1f)
080036a4 c307 stmia r3!, {r0-r2}
> 080036a6 3b0c sub r3, #0xc
080036a8 4b22 ldr r3, [$08003734] (=$040000d4)
debugger> c
Breakpoint (on write) address 0300351a old:4090 new:4090
R00=080f2ac7 R04=00000090 R08=0300347c R12=000000e0
R01=fffffe00 R05=03003514 R09=00000e10 R13=03007e64
R02=00000090 R06=00000002 R10=fffffe00 R14=080f2ac7
R03=00004090 R07=000000af R11=04000052 R15=080f2aba
CPSR=0000003f (......T Mode: 1f)
080f2ab6 80eb strh r3, [r5, #0x6]
> 080f2ab8 237c mov r3, #0x7c
080f2aba 712b strb r3, [r5, #0x4]
check this in IDA:
.text:080F2AA4 MOVS R4, #0x50 as immediate value for string start - code in rom, responsible for saving our coordinate
13 symbols in russian string, 30 tiles of screen width, so starting coord = (15 - 6)*8 = 0x48. change 50 to 48 and we're ready.
.text:080F2ACE CMP spriteCount, #2 ; 3 sprites on screen, change it to 4
Now for the PAUSE caption, if you press start. In the beginnign of game, right after you get control over character,
at tile viewer we can see that tiles are placed in 06012100,
place a breakpoint and press start: DMA from 0203917C.
which is buffer and it's written several times: only once with immediate write to 06012100:command at 03003548
with source address for data in r0 at 8031f84, but there's a mess instead of gfx.
Need to analyse code at IWRAM. dump it and disassemble in IDA
full log:
debugger> bpw 0x6012100 4
Added break on write at 06012100 for 4 bytes
debugger> c
Breakpoint (on write) address 06012100 old:00000000 new:00000000
R00=0203917c R04=84000000 R08=0203917c R12=03001810
R01=06012100 R05=00002100 R09=02032fbc R13=03007e1c
R02=84000080 R06=00000200 R10=ffffffe8 R14=03001b10
R03=040000e0 R07=03001b70 R11=00000003 R15=0800401a
CPSR=8000003f (N.....T Mode: 1f)
08004016 c307 stmia r3!, {r0-r2}
> 08004018 3b0c sub r3, #0xc
0800401a 0968 lsr r0, r5, #0x05
debugger> bpw 0x203917c 4
Added break on write at 0203917c for 4 bytes
debugger> c
Breakpoint (on write) address 0203917c old:00 new:00
R00=0843ef88 R04=030077ff R08=0843ef86 R12=00000000
R01=0203917d R05=00000096 R09=080367dc R13=03007e24
R02=84000096 R06=0300347c R10=0203917c R14=5d000000
R03=00000000 R07=00000015 R11=00000003 R15=03003550
CPSR=0000001f (....... Mode: 1f)
03003548 e4c13001 strb r3, [r1], #0x1
> 0300354c e1b0e08e movs lr, lr, lsl #0x01
03003550 3afffffb bcc $03003544
debugger> c
Breakpoint (on write) address 0203917d old:00 new:04
R00=0843ef89 R04=030077ff R08=0843ef86 R12=00000000
R01=0203917e R05=00000096 R09=080367dc R13=03007e24
R02=84000096 R06=0300347c R10=0203917c R14=ba000000
R03=00000004 R07=00000015 R11=00000003 R15=03003550
CPSR=8000001f (N...... Mode: 1f)
03003548 e4c13001 strb r3, [r1], #0x1
> 0300354c e1b0e08e movs lr, lr, lsl #0x01
03003550 3afffffb bcc $03003544
debugger> c
....
debugger> c
Breakpoint (on write) address 0203917c old:00 new:00
R00=0843ef88 R04=030077ff R08=0843ef86 R12=03001810
R01=0203917d R05=00000096 R09=00000000 R13=03007ddc
R02=84000096 R06=0300347c R10=0203917c R14=5d000000
R03=00000000 R07=00000015 R11=02038b54 R15=03003550
CPSR=0000001f (....... Mode: 1f)
03003548 e4c13001 strb r3, [r1], #0x1
> 0300354c e1b0e08e movs lr, lr, lsl #0x01
03003550 3afffffb bcc $03003544
debugger> dsave pauseCode.bin 0x03000000 0xdeadbeef //VBA-SDL-H bug, cut 0x8000 from dump later
debugger> mb 03003548
03003548 01 30 c1 e4 8e e0 b0 e1 fb ff ff 3a c9 ff ff 0a .0.........:....
03003558 01 c0 d0 e4 01 30 d0 e4 f0 20 0c e2 01 30 63 e0 .....0... ...0c.
03003568 02 42 43 e0 0f c0 1c e2 26 00 00 0a 0f c0 6c e2 .BC.....&.....l.
03003578 8c f1 8f e0 00 00 a0 e1 01 30 d4 e4 01 30 c1 e4 .........0...0..
03003588 01 30 d4 e4 01 30 c1 e4 01 30 d4 e4 01 30 c1 e4 .0...0...0...0..
03003598 01 30 d4 e4 01 30 c1 e4 01 30 d4 e4 01 30 c1 e4 .0...0...0...0..
030035a8 01 30 d4 e4 01 30 c1 e4 01 30 d4 e4 01 30 c1 e4 .0...0...0...0..
030035b8 01 30 d4 e4 01 30 c1 e4 01 30 d4 e4 01 30 c1 e4 .0...0...0...0..
030035c8 01 30 d4 e4 01 30 c1 e4 01 30 d4 e4 01 30 c1 e4 .0...0...0...0..
030035d8 01 30 d4 e4 01 30 c1 e4 01 30 d4 e4 01 30 c1 e4 .0...0...0...0..
030035e8 01 30 d4 e4 01 30 c1 e4 01 30 d4 e4 01 30 c1 e4 .0...0...0...0..
030035f8 01 30 d4 e4 01 30 c1 e4 8e e0 b0 e1 ce ff ff 3a .0...0.........:
03003608 9c ff ff 0a d1 ff ff ea 01 00 54 e1 2b 00 00 0a ..........T.+...
03003618 01 30 d0 e4 10 30 83 e2 0f c0 03 e2 0f c0 6c e2 .0...0........l.
03003628 8c f1 8f e0 00 00 a0 e1 01 20 d4 e4 01 20 c1 e4 ......... ... ..
03003638 01 20 d4 e4 01 20 c1 e4 01 20 d4 e4 01 20 c1 e4 . ... ... ... ..
Analysis shows, that decompression routine is the same as previous one, but code is copied from ROM to another location: 300347c instead of 300355c.
Anyhow, from tracing we've discovered, that uncompress routine is called from here:
080072FC 4730 bx r6 R00=0843f246 R01=0203917c ...
0300347C e92d40e2 stmfd sp!, {r1,r5-r7,lr} R00=0843f246 R01=0203917c ...
R00 - is an srcPtr. Let's trace pointer to pause caption. Both VBA-SDL-H and no$gba has issues with break at code in IWRAM (you need to break at the point when code is already loaded into IWRAM and then put a breakpoint on that code), so we'll use conditional breakpoints (available only at VBA-SDL-H2):
break at ROM 080072FC at the moment when it will be ready to jump to 0300347C only (R06 will have this value).
debugger> bpw 06012100
At 06012100, Break Always on Write
debugger> bx 080072fc if r6 == 0x0300347c
Added break on address 080072fc, ending with condition:
r6 == 0x0300347c
debugger> c
Breakpoint (on Thumb) address 080072fc
R00=0843ef86 R04=030077ff R08=0843ef86 R12=03001810
R01=0203917c R05=00000096 R09=00000000 R13=03007df0
R02=84000096 R06=0300347c R10=0203917c R14=08005419
R03=040000d4 R07=00000015 R11=02038b54 R15=080072fe
CPSR=2000003f (..C...T Mode: 1f)
08005416 ff72 blh $0ee4
> 080072fc 4730 bx r6
080072fe 46c0 mov r8, r8
debugger> c
Breakpoint (on Thumb) address 080072fc
R00=0843f0e6 R04=030077ff R08=0843f0e6 R12=03001810
R01=0203917c R05=00000096 R09=00000000 R13=03007df0
R02=84000096 R06=0300347c R10=0203917c R14=08005419
R03=040000d4 R07=00000016 R11=02038b54 R15=080072fe
CPSR=2000003f (..C...T Mode: 1f)
08005416 ff72 blh $0ee4
> 080072fc 4730 bx r6
080072fe 46c0 mov r8, r8
debugger> c
Breakpoint (on Thumb) address 080072fc
R00=0843f246 R04=030077ff R08=0843f246 R12=03001810
R01=0203917c R05=00000096 R09=00000000 R13=03007df0
R02=84000096 R06=0300347c R10=0203917c R14=08005419
R03=040000d4 R07=00000017 R11=02038b54 R15=080072fe
CPSR=2000003f (..C...T Mode: 1f)
08005416 ff72 blh $0ee4
> 080072fc 4730 bx r6
080072fe 46c0 mov r8, r8
debugger> c
Breakpoint (on Thumb) address 080072fc
R00=08031f82 R04=030077ff R08=08031f82 R12=03001810//R00 has pointer
R01=0203917c R05=00000096 R09=02032fbc R13=03007e1c
R02=84000096 R06=0300347c R10=0203917c R14=08005419
R03=040000d4 R07=00000018 R11=00000003 R15=080072fe
CPSR=2000003f (..C...T Mode: 1f)
08005416 ff72 blh $0ee4
> 080072fc 4730 bx r6
080072fe 46c0 mov r8, r8
debugger> c
Breakpoint (on write) address 06012100 old:00000000 new:00000000//previous write to buffer was PAUSE gfx decompress
R00=0203917c R04=84000000 R08=0203917c R12=03001810
R01=06012100 R05=00002100 R09=02032fbc R13=03007e1c
R02=84000080 R06=00000200 R10=ffffffe8 R14=03001b10
R03=040000e0 R07=03001b70 R11=00000003 R15=0800401a
CPSR=8000003f (N.....T Mode: 1f)
08004016 c307 stmia r3!, {r0-r2}
> 08004018 3b0c sub r3, #0xc
0800401a 0968 lsr r0, r5, #0x05
So, decompress with our tool from offset 0x31f82 in ROM and voila, we have our graphics
let's see how pointer to compressed block is loaded. The easiest way is to dump a tracing log, we now know our pointer to compressed block value:
debugger> bx 080072fc if r0 == 0x8031f82
Added break on address 080072fc, ending with condition:
r0 == 0x8031f82
debugger> trace once off
debugger> trace start
debugger> c //here I quickly switch to game and press start rapidly, so log file will not grow too large
Breakpoint (on Thumb) address 080072fc
R00=08031f82 R04=030077ff R08=08031f82 R12=03001810 //OK, desired point with pointer already loaded and whole process is in trace log
R01=0203917c R05=00000096 R09=02032fbc R13=03007e1c
R02=84000096 R06=0300347c R10=0203917c R14=08005419
R03=040000d4 R07=00000018 R11=00000003 R15=080072fe
CPSR=2000003f (..C...T Mode: 1f)
08005416 ff72 blh $0ee4
> 080072fc 4730 bx r6
080072fe 46c0 mov r8, r8
debugger> trace stop
In the log file we trace this address downside up and easily see where it is loaded from:
080215F0 4b0a ldr r3, [$0802161c] (=$08031864) R00=0203917c R01=080215f1 R02=0203957c R03=02040000 R04=03001e50 R05=00000006 //start address of ptr table
080215F2 00ad lsl r5, r5, #0x02 R00=0203917c R01=080215f1 R02=0203957c R03=08031864 R04=03001e50 R05=00000006 //r5 has block number, shl 2, as pointer takes 4 bytes
080215F4 1c06 add r6, r0, #0x0 R00=0203917c R01=080215f1 R02=0203957c R03=08031864 R04=03001e50 R05=00000018
080215F6 5958 ldr r0, [r3, r5] R00=0203917c R01=080215f1 R02=0203957c R03=08031864 R04=03001e50 R05=00000018 //get actual pointer to compressed block
So, in general 0x31864 has pointer table with 32 bit pointers, and as first pointer has value of 0x08031884 table is 0x20 bytes long with 8 entries in it.
-----------------------------Text-------------------------------------------
In general, it's always useful to dump all RAM and search (relative) for a particular phrase, which is now displayed by game.
In Golden Sun case, I've searched for ASCII string and relative and got nothing.
Great opportunity to find text starting from video buffer:
06010480 - upper part of P in Please dear wake up
dma from 3007d08
put a breakpoint on that and break at 08017a14,
debugger> bpw 03007d08 4
Added break on write at 03007d08 for 4 bytes
debugger> c
R00=00000001 R04=03001e50 R08=02032abc R12=00000020
R01=03001d28 R05=03001a10 R09=03001d24 R13=03007e24
R02=0000fffe R06=00000000 R10=03007e24 R14=08003e41
R03=00000000 R07=02032abc R11=00000001 R15=08003326
CPSR=2000003f (..C...T Mode: 1f)
08003324 880a ldrh r2, [r1, #0x0]
> 08003324 880a ldrh r2, [r1, #0x0]
08003326 1c03 add r3, r0, #0x0
debugger> c
Breakpoint (on write) address 03007d08 old:00000000 new:00000000
R00=03007d0c R04=00000000 R08=0000000c R12=08032bc2
R01=03007b57 R05=00000000 R09=00000001 R13=03007b54
R02=00000000 R06=00000000 R10=00000004 R14=080179ed
R03=00000000 R07=08032ba6 R11=0000000f R15=08017a16
CPSR=a000003f (N.C...T Mode: 1f)
08017a12 c004 stmia r0!, {r2}
> 08017a14 3401 add r4, #0x1
08017a16 1c03 add r3, r0, #0x0
debugger> trace once off
debugger> trace start
debugger> c
...
Breakpoint (on write) address 03007d08 old:00000000 new:00000000
R00=03007d0c R04=00000000 R08=00000003 R12=080323c2
R01=03007b57 R05=00000000 R09=00000001 R13=03007b54
R02=00000000 R06=00000000 R10=00000000 R14=08017965
R03=00000000 R07=080323a6 R11=0000000f R15=08017a16
CPSR=a000003f (N.C...T Mode: 1f)
08017a12 c004 stmia r0!, {r2}
> 08017a14 3401 add r4, #0x1
08017a16 1c03 add r3, r0, #0x0
debugger> trace stop
see how gfx is copied from rom: game has VWF, so procedure is painful to analyse - let's just look at registers:
looks like each time we break at this place new portion of drawn string is copied to VRAM, and r7 changes a little bit somewhere around ~8032ac6
The guess is that here in ROM we will have our font is true - look at this place in Tile Editor in 2bpp planar mode with width at 1 tile, and we've got ingame font along with width table starting from around ~32226.
Anyhow now we can trace command, which loads r7 between two sequental writes to 3007d08 and find text
080178F4 881b ldrh r3, [r3, #0x0] R00=00000ea4 R01=00000000 R02=00000eae R03=0203396a R04=00000000 R05=0000002c R06=02032abc R07=02032fbc
080178F6 2001 mov r0, #0x1 R00=00000ea4 R01=00000000 R02=00000eae R03=0000000f R04=00000000 R05=0000002c R06=02032abc R07=02032fbc
080178F8 4681 mov r9, r0 R00=00000001 R01=00000000 R02=00000eae R03=0000000f R04=00000000 R05=0000002c R06=02032abc R07=02032fbc
080178FA 469b mov r11, r3 R00=00000001 R01=00000000 R02=00000eae R03=0000000f R04=00000000 R05=0000002c R06=02032abc R07=02032fbc
080178FC 1c2b add r3, r5, #0x0 R00=00000001 R01=00000000 R02=00000eae R03=0000000f R04=00000000 R05=0000002c R06=02032abc R07=02032fbc //r3 = 0f is hardcoded, need to trace r5
080178FE 3b20 sub r3, #0x20 R00=00000001 R01=00000000 R02=00000eae R03=0000002c R04=00000000 R05=0000002c R06=02032abc R07=02032fbc
08017900 4a54 ldr r2, [$08017a54] (=$08032224) R00=00000001 R01=00000000 R02=00000eae R03=0000000c R04=00000000 R05=0000002c R06=02032abc R07=02032fbc //08032224 - start of font
08017902 015b lsl r3, r3, #0x05 R00=00000001 R01=00000000 R02=08032224 R03=0000000c R04=00000000 R05=0000002c R06=02032abc R07=02032fbc
08017904 189f add r7, r3, r2 R00=00000001 R01=00000000 R02=08032224 R03=00000180 R04=00000000 R05=0000002c R06=02032abc R07=02032fbc
08017906 883b ldrh r3, [r7, #0x0] R00=00000001 R01=00000000 R02=08032224 R03=00000180 R04=00000000 R05=0000002c R06=02032abc R07=080323a4
08017908 4853 ldr r0, [$08017a58] (=$00000eac) R00=00000001 R01=00000000 R02=08032224 R03=00000003 R04=00000000 R05=0000002c R06=02032abc R07=080323a4
r2 is a pointer to font start in ROM, r5 - is loaded from somewhere. Moreover, r7 at the end points to comma symbol (we can see this in tile editor) and r5 holds ASCII code for comma. Let's trace it:
after few register transfers and stack push/pops we come to
08016986 5ae7 ldsb r7, [r4, r3] R00=0000020c R01=03001ae8 R02=00000eb0 R03=00000f10 R04=02032abc R05=03001d24 R06=020330dc R07=00000000
08016988 2f1e cmp r7, #0x1e R00=0000020c R01=03001ae8 R02=00000eb0 R03=00000f10 R04=02032abc R05=03001d24 R06=020330dc R07=00000077
interesting...
debugger> mb 020339cc
020339cc 77 00 61 00 6b 00 65 00 20 00 75 00 70 00 21 00 w.a.k.e. .u.p.!.
020339dc 02 00 00 00 50 00 6c 00 65 00 61 00 73 00 65 00 ....P.l.e.a.s.e.
020339ec 2c 00 20 00 64 00 65 00 61 00 72 00 2c 00 20 00 ,. .d.e.a.r.,. .
020339fc 77 00 61 00 6b 00 65 00 20 00 75 00 70 00 21 00 w.a.k.e. .u.p.!.
02033a0c 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02033a1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02033a2c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02033a3c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02033a4c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Gotcha! Now I see that text is stored in unicode, so each character takes 2 bytes. In general we could start text hacking process with straight text search in RAM dump. This can be done in almost any hex editor, supporting unicode search. Anyway, now we know RAM buffer, which is filled by some decoding function. Let's dig deeper:
020339b0 has P letter (in "Please" word) code: 0x50. let's trace where it comes from
debugger> bpw 020339b0 2
Added break on write at 020339b0 for 2 bytes
debugger> trace start
debugger> c
Breakpoint (on write) address 020339b0 old:0000 new:0050
R00=000001ff R04=080374c4 R08=0203396c R12=00000001
R01=00000000 R05=0203396c R09=0300347c R13=03007da8
R02=000012fa R06=00000023 R10=03007de0 R14=080180cf
R03=00000044 R07=00000050 R11=00000001 R15=08018288
CPSR=4000003f (.Z....T Mode: 1f)
08018284 535f strh r7, [r3, r5]
> 08018286 4006 and r6, r0
08018288 910a str r1, [sp, #0x28]
debugger> trace stop
From tracing log it becomes obvious, that letter code is formed somewhere near
030035A0 31a01205 movcc r1, r5, lsl #0x04 R00=03007de0 R01=00000011 R02=08040e60 R03=00000004 R04=080374c4 R05=00000005 R06=00000000
030035A4 31811226 orrcc r1, r1, r6, lsr #0x04 R00=03007de0 R01=00000050 R02=08040e60 R03=00000004 R04=080374c4 R05=00000005 R06=00000000
Need to analyse this function - There are two ways for that.
a) As usual we'll do a dump of WRAM via 'dsave textCode.bin 0x03000000 0xdeadbeef' and open it with IDA.
b) We could just find bytes from function in ROM file and find this place in IDA database for whole ROM analysis. For instance, I've found char decode start at 0x15430 in ROM, which means we need to go to sub_8015430 in IDA. The code will be the same, as in RAM dump, as it uses only relative offsets and no hardcoded address values.
From log file it's also easy to see entry point of our decoding function and exit from it.
1. Trees
Analysis shows, that this each character has separate huffman tree. Actual text is a plain path in corresponding tree. Each iteration function returns a character based on a pass on previous char's huffman tree. Usually full text characters are counted for probabilities, one large tree is created and text is encoded based on that overall tree. For instance, both Battletoads games on NES has this scheme. Large tree has accordingly larger average bits per character. In case of Golden Sun, there is a bunch of small trees, which have separately smaller alphabet (script doesn't have syllable "zr", so you don't need to include char "r" in "z"'s Huffman tree). And that leads to higher probabilities of concrete chars, which now can be encoded in less bits. That leads to overall text size decrease. Of course, you'll need to store all these trees, but in case of half a megabyte script, that's acceptable: text size decrease is more than trees size increase.
So, if that's the first symbol, previous char is considered zero. let's build a tree of "no previous char" situation.
ROM:03003484 LDR treeBitStream, =0x803842C
ROM:03003488 MOV treePtr, R1,LSR#8
ROM:0300348C ADD treeBitStream, treeBitStream, treePtr,LSL#3
ROM:03003490 LDMIA treeBitStream, {treePtr,R5} ; r4 = 0x08037464 (@803842c)
ROM:03003490 ; r5= 0x08038334 (@8038430)
ROM:03003494 AND treeBitStream, R1, #0xFF ; clear high byte of previous char code
ROM:03003498 ADD treeBitStream, treeBitStream, treeBitStream ; char*2, as each offset is 16 bit
ROM:0300349C LDRH R5, [R5,treeBitStream] ; load offset of corresponding previously decoded char
ROM:030034A0 ADD treePtr, treePtr, R5 ; and position in iter bitstream
After carful code analysis, overview of all space looks like this:
37464: char-tree structures block (size = 38334-37464 = ED0)
38334: offset table of each separate tree from the start of char-tree block (size = 3842B - 38334 = F8 bytes = 7C pointers)
3842c: pointer to 37464 (size 4)
38430: pointer to 38334 (size 4)
So, 3842c in ROM contains 2 pointers: 08037464 - start address of char-trees structures table, and 08038334 - 16 bit offset table of each char tree. You can easily identify 16 bit pointer table in byte array, so visually, we have 7b entries @38334-3842b and our two main pointers are stored after it (at 3842c). So we have 123 characters. You can also note, that pointers, for, say, 0xA - 0xF tree structures are equal 0x8000 - that's obviously a dummy initial pointer - we just have zero occurences of these bytes in game's binary script.
37464 is a start offset of tree table, which ends right before offset table at 38334, so all character trees take only 0xED0 bytes. For half a megabyte script, that size is uncomparable with text size reduction. Also note, that index in this trees table is actually a char code. For instance, if previously decoded char was 'A' with ASCII code 0x41, for next character decode we'll take tree and character table number 0x41. So each ASCII entry has it's own tree with character look-up table.
From code analysis we can also see this char-tree structure: each consists of Look up table of char codes, stored in 12 bits (1,5 byte) and a binary tree right after them.
374BB is also a start address for a zero character tree structure. For zero previous character, offset, stored in table at 38334 is also a size of Look Up Table before appropriate tree structure. First offset in 38334 is 0x0057, so 0x0057*8/12 = 0x3A (dec 58) characters are stored and addressed in the first tree.
The binary tree itself is stored in a bitstream in pre-order traversal. In a tree bitstream 0 bit means, this is a node, 1 -- this is a leaf. Leafs are numbered (zero-based), in pre-order sequence. Start offset of the binary tree for zero character is 37464 + 57 = 374BB. Now we can rebuild it and it will look like that:
--o--o--o-[0]
| | |
| | `--o--o-[1]
| | | |
| | | `--o--o-[2]
| | | | |
| | | | `--o--o-[3]
| | | | | |
| | | | | `--o-[4]
| | | | | |
| | | | | `-[5]
| | | | |
| | | | `-[6]
| | | |
| | | `-[7]
| | |
| | `--o-[8]
| | |
| | `--o--o-[9]
| | | |
| | | `-[1o]
| | |
| | `-[11]
| |
| `--o--o-[12]
| | |
| | `--o-[13]
| | |
| | `-[14]
| |
| `-[15]
|
`--o--o--o--o--o-[16]
| | | | |
| | | | `-[17]
| | | |
| | | `--o--o-[18]
| | | | |
| | | | `--o--o--o--o--o-[19]
| | | | | | | | |
| | | | | | | | `-[2o]
| | | | | | | |
. . . . . . . . . . . .
| |
| `-[54]
|
`--o--o-[55]
| |
| `-[56]
|
`-[57]
exactly 58 leafs, as we have in the 12 bits table before tree. So in general, text crawls right on this tree, meaning 0 - branch left, 1 - branch right until we hit a leaf. Leaf's number is an index in 12 bits table before tree. Index is counted backward from a tree start. For instance text bitstream like b11111 will give us index 57, which is 58*12/8 = 87 bytes away from the tree start (i.e. the beginning of trees structures table 0x37464)
Calculated is an index to 12-bit entries, stored before tree in reverse order. If we'll decode them in ASCII we'll get (0,'W'),(1,'C'),(2,'V'),(3,'b'),(4,'('),(5,'Q'),(6,'J'),(7,'E'),(8,'O'),(9,'K')... Looks reasonable, as long as there are many question sentences, starting with a W-words, like What, Where, When, Who etc.
2. Text
From tracing log we can trace back the first occurence of our text source pointer (it's R2 = 08040e5c in our case). It's command:
03003600 e4923004 ldr r3, [r2], #0x4 R00=03007de0 R01=00000000 R02=08040e5c R03=00000001; R3 is a text bitstream, loaded from text source pointer.
Allright, it's another procedure, starting at 30035BC, we can use the same dump, we've made for 'decode next char' function. This function is called right before string decode and it sets up text source pointer, initial text bitstream word and sets up last decoded byte to 0 (of course). From analysis we can see, that all text is splitted in blocks with 0x100 messages in each. Each block has a section with all these encoded messages going one by one and an FF bytes size block with each encoded message length. Each decoded message is just 0-terminated, but this 0 is also encoded, so to quickly find out where is our exact message starts, our init procedure uses this length table.
In overall the ROM space will look like this:
37464: char-tree structures block (size = 38334-37464 = ED0)
38334: offset table of each separate tree from the start of char-tree block (size = 3842C - 38334 = F8 bytes = 7C pointers)
2 bytes align for next 32bit pointer
3842c: pointer to 37464 (size 4)
38430: pointer to 38334 (size 4)
38434: first block of text bitstream (from first text source pointer in table at 736B8)
38C07: length table for first text block (from second pointer in table at 736Bc. Size 0x100, visually)
38D07: second block of text bitstream
...
735D5: length table for last text block (size E2, as we've found from disassembly)
736B8: table of 2-pointers structs: first pointer to start of block's text, second pointer to start of block's lengths (the address of table was found in InitDecodeState function). Visually it's 150 bytes long. Each entry is 8 bytes (2 32bit pointers), so 2A text blocks, 29 of them contain FF messages, and the last one has E2 messages, it's 0x29E2 messages for whole game.
"Golden Sun - The Lost Age (UE)" ROM uses the same text encoding scheme with the same functions, but they are assembled at another addresses. Text structures, of course, is also stored at another offsets:
5F914: char-tree structures block (size = 60A4C-5F914 = 0x1138)
60A4C: offset table of each separate tree from the start of char-tree block (size = 60C2F - 60A4C = 1E4 bytes = F2 pointers)
60C30: pointer to 5F914 (size 4)
60C34: pointer to 60A4C (size 4)
60C38: first block of text bitstream (from first text source pointer in table at A9F54)
614E4: length table for first text block (from second pointer in table at A9F58. Size 0x100, visually)
615E4: second block of text bitstream
...
A9EA5: length table for last text block (size AE, A9F54-A9EA5.)
A9F54: table of 2-pointers structs. Visually it's 188 bytes long. Each entry is 8 bytes (2 32bit pointers), so 31 text blocks, 30 of them contain 0x100 messages, and the last one has AE messages, it's 0x30AE messages for whole game.
after writing compressor tool, the text is a little bit larger than it was in original state. there is some space for text not used by game, but right after compressed text data there's also some data used by alphabet render at the beginning of the game. to move this piece of data to the end of free space you just move the data and change pointer at 20d58 - ptr originally to 08073864
_______________________________________________________FRAME WIDTH________________________________________________________________________
In Russian version word copy consists of 10 symbols, and frame for this word, which we find on the title screen gets redrawn by actual symbols. Let's fix that. If you have no link battle option available (4 items in menu), in viewer you will see, that frame and symbols of word are part of tile map of BG0. the actual problem tile is at address 60024B4. To find frame draw routine, we'll jump from copy to erase string in game (because erase in russian is short enough not to overwrite the frame) and see what writes 0x17th tile to the map:
debugger> bw 060024b4
At 060024b4, Break Always on Write
debugger> trace once off
debugger> trace start
debugger> c
Breakpoint (on write) address 060024b4 old:f000f0c9 new:f000f017
R00=02032ebc R04=00000001 R08=020367bc R12=06002400
R01=06002400 R05=02032ebc R09=03001d24 R13=03007be4
R02=84000040 R06=00000100 R10=03007c0c R14=00000001
R03=040000e0 R07=02032abc R11=00000001 R15=0801614a
CPSR=0000003f (......T Mode: 1f)
08016146 c307 stmia r3!, {r0-r2}
> 08016148 3b0c sub r3, #0xc
0801614a 0864 lsr r4, r4, #0x01
debugger> mh 02032ebc
02032ebc f000 f000 f000 f000 f000 f000 f000 f000 ................
02032ecc f000 f000 f000 f000 f000 f000 f000 f000 ................
02032edc f000 f000 f000 f000 f000 f000 f000 f000 ................
02032eec f000 f000 f000 f000 f000 f000 f000 f000 ................
02032efc f000 f000 f000 f000 f000 f000 f000 f000 ................
02032f0c f000 f000 f000 f000 f000 f000 f000 f000 ................
02032f1c f000 f000 f010 f011 f011 f011 f011 f011 ................
02032f2c f011 f011 f012 f000 f000 f000 f000 f000 ................
02032f3c f000 f000 f000 f000 f000 f000 f000 f000 ................
02032f4c f000 f000 f000 f000 f000 f000 f000 f000 ................
02032f5c f000 f000 f016 f0ca f0cb f0cc f0cd f0ce ................
02032f6c f0cf f020 f017 f000 f000 f000 f000 f000 .. .............
02032f7c f000 f000 f000 f000 f000 f000 f000 f000 ................
02032f8c f000 f000 f000 f000 f000 f000 f000 f000 ................
02032f9c f000 f000 f013 f014 f014 f014 f014 f014 ................
02032fac f014 f014 f015 f000 f000 f000 f000 f000 ................
OK, so the frame is copied via DMA. start of frame is 02032ebc, in VRAM - 06002400. Tile has offset b4, so it's 02032ebc+b4= 02032f70, which we also clearly see on the memory hexview.
debugger> bpw 02032f70 2
Added break on write at 02032f70 for 2 bytes
debugger> trace once off
debugger> trace start
debugger> c
Breakpoint (on write) address 02032f70 old:f09d new:f017
R00=02032f70 R04=00000007 R08=00000009 R12=000000ff
R01=080171b9 R05=02032f70 R09=0000002e R13=03007c08
R02=81000007 R06=00000002 R10=00000007 R14=080171b9
R03=0000f017 R07=00000002 R11=02032abc R15=080171d2
CPSR=0000003f (......T Mode: 1f)
080171ce 802b strh r3, [r5, #0x0]
> 080171d0 3502 add r5, #0x2
080171d2 444d add r5, r9
debugger> trace stop
from log:
080171CA 4b07 ldr r3, [$080171e8] (=$0000f017) R00=02032f70 R01=080171b9 R02=81000007 R03=0000000e
080171CC 3601 add r6, #0x1 R00=02032f70 R01=080171b9 R02=81000007 R03=0000f017
080171CE 802b strh r3, [r5, #0x0] R00=02032f70 R01=080171b9 R02=81000007 R03=0000f017
Analysis showed that subroutine 080170f8 used for every frame draw in the game, and R2 is passed with width. From log file:
0801649A 8984 ldrh r4, [r0, #0xc] R00=02032fbc R01=020367bc R02=00000003 R03=00000000 R04=00000378
0801649C 89c1 ldrh r1, [r0, #0xe] R00=02032fbc R01=020367bc R02=00000003 R03=00000000 R04=00000012
0801649E 8902 ldrh r2, [r0, #0x8] R00=02032fbc R01=00000011 R02=00000003 R03=00000000 R04=00000012
080164A0 8943 ldrh r3, [r0, #0xa] R00=02032fbc R01=00000011 R02=00000009 R03=00000000 R04=00000012
080164A2 1c20 add r0, r4, #0x0 R00=02032fbc R01=00000011 R02=00000009 R03=00000003 R04=00000012
080164A4 f000 bl $080170f8 R00=00000012 R01=00000011 R02=00000009 R03=00000003 R04=00000012
080164A6 fe28 blh $0c50 R00=00000012 R01=00000011 R02=00000009 R03=00000003 R04=00000012
080170F8 b5e0 push {r5-r7,lr} R00=00000012 R01=00000011 R02=00000009 R03=00000003 R04=00000012
width is stored 02032fbc+8=02032fc4. this is filled right after we press start at the mountain screen. Let's catch that.
debugger> bpw 02032fc4 1
Added break on write at 02032fc4 for 1 bytes
debugger> c
Breakpoint (on write) address 02032fc4 old:00000000 new:0000000
R00=02032fe0 R04=00000000 R08=00000000 R12=00000000
R01=02012180 R05=00000000 R09=00000000 R13=03007e54
R02=00005e00 R06=00000000 R10=0809f1b0 R14=0800fae5
R03=00000000 R07=00000000 R11=0809f1a8 R15=030013bc
CPSR=2000001f (..C.... Mode: 1f)
030013b4 e8a013f8 stmia r0!, {r3-r9,r12}
> 030013b8 e8b113f8 ldmia r1!, {r3-r9,r12}
030013bc e8a013f8 stmia r0!, {r3-r9,r12}
debugger> trace once off
debugger> trace start
debugger> c
Breakpoint (on write) address 02032fc4 old:00000000 new:0000000
R00=03007e98 R04=02032abc R08=02030000 R12=03001810
R01=02032abc R05=03007e98 R09=050001c0 R13=03007e98
R02=850004bf R06=00000002 R10=0809f1b0 R14=08015f3d
R03=040000e0 R07=00000000 R11=0809f1a8 R15=08015f50
CPSR=0000003f (......T Mode: 1f)
08015f4c c307 stmia r3!, {r0-r2}
> 08015f4e 3b0c sub r3, #0xc
08015f50 4b13 ldr r3, [$08015fa0] (=$00000ea3)
debugger> c
Breakpoint (on write) address 02032fc4 old:0000 new:0009
R00=00000012 R04=02032fbc R08=00000000 R12=00000009
R01=00000000 R05=02032fbc R09=0203684a R13=03007c20
R02=00000009 R06=00000002 R10=00000011 R14=00000003
R03=00000003 R07=00000011 R11=00000010 R15=0801632a
CPSR=6000003f (.ZC...T Mode: 1f)
08016326 812a strh r2, [r5, #0x8]
> 08016328 816b strh r3, [r5, #0xa]
0801632a 4642 mov r2, r8
debugger> trace stop
From log file:
08016326 812a strh r2, [r5, #0x8] R00=00000012 R01=00000000 R02=00000009 R03=00000003 R04=02032fbc R05=02032fbc
search up
08028882 5e5a ldsh r2, [r3, r1] R00=00000012 R01=00000000 R02=0203680c R03=0203684c
and finally
08028A70 2107 mov r1, #0x7 R00=00000011 R01=00000018 R02=00000000 R03=00000000 R04=03001e50 R05=00000001
08028A72 2200 mov r2, #0x0 R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=00000001
08028A74 f7ff bl $08028808 R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=00000001
08028A76 fec8 blh $0d90 R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=00000001
08028808 b5e0 push {r5-r7,lr} R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=00000001
0802880A 4657 mov r7, r10 R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=00000001
0802880C 464e mov r6, r9 R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=00000001
0802880E 4645 mov r5, r8 R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=00000001
08028810 b4e0 push {r5-r7} R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=02030000
08028812 4b24 ldr r3, [$080288a4] (=$03001f38) R00=00000011 R01=00000007 R02=00000000 R03=00000000 R04=03001e50 R05=02030000
08028814 681f ldr r7, [r3, #0x0] R00=00000011 R01=00000007 R02=00000000 R03=03001f38 R04=03001e50 R05=02030000
08028816 2390 mov r3, #0x90 R00=00000011 R01=00000007 R02=00000000 R03=03001f38 R04=03001e50 R05=02030000
08028818 19db add r3, r3, r7 R00=00000011 R01=00000007 R02=00000000 R03=00000090 R04=03001e50 R05=02030000
0802881A 4698 mov r8, r3 R00=00000011 R01=00000007 R02=00000000 R03=0203684c R04=03001e50 R05=02030000
0802881C 1c3b add r3, r7, #0x0 R00=00000011 R01=00000007 R02=00000000 R03=0203684c R04=03001e50 R05=02030000
0802881E 4682 mov r10, r0 R00=00000011 R01=00000007 R02=00000000 R03=020367bc R04=03001e50 R05=02030000
08028820 3102 add r1, #0x2 R00=00000011 R01=00000007 R02=00000000 R03=020367bc R04=03001e50 R05=02030000
08028822 4645 mov r5, r8 R00=00000011 R01=00000009 R02=00000000 R03=020367bc R04=03001e50 R05=02030000
08028824 3392 add r3, #0x92 R00=00000011 R01=00000009 R02=00000000 R03=020367bc R04=03001e50 R05=0203684c
08028826 8029 strh r1, [r5, #0x0] R00=00000011 R01=00000009 R02=00000000 R03=0203684e R04=03001e50 R05=0203684c
so we change 28a70 in ROM from 07 to 08 and voila.
(for psynergy width is loaded from 020348c8 == 08 08021ED8 2208 mov r2, #0x8 21ED8 change to 09)
------------The first match in arena-----------------------------------------
The first match gfx:
in VRAM 06014380 dma from 0203ddcc
buffer is filled by
Breakpoint (on write) address 0203ddcc old:00 new:00
R00=08434fea R04=00000007 R08=08434ca8 R12=00000003
R01=0203ddcc R05=00000001 R09=0203d94c R13=03007e38
R02=0012ab41 R06=0000023f R10=0203d92c R14=03003588
R03=00002556 R07=00000000 R11=00000010 R15=030035c8
CPSR=2000001f (..C.... Mode: 1f)
030035c0 e5e17001 strbt r7, [r1, #0x1]!
> 030035c4 e2555001 subs r5, r5, #0x1
030035c8 1afffff9 bne $030035b4
Which is yet another compression scheme
sub_300347C - start
30036bc exit
I've found bytes 01 50 55 E2 (which is copied to 030035c4) in ROM at 0x268C, so just go and analyse in IDA area around 0800268C, comparing code, which called our function, we see, that the start is at sub_8002544
difference between gba rom address and ram copied addresses is 4fff0c8.
Code analysis showed, that R0 contains source pointer, and R1 - destination. From tracing log:
0300347C e92d42e0 stmfd sp!, {r5-r7,r9,lr}
if we search for place in rom, decompression routine is called from here:
08005368 4640 mov r0, r8 R00=08002544 R01=0300347c R02=840000b1 R03=040000d4 R04=030077ff R05=000000b1 R06=0300347c R07=020301c8 R08=08073864
0800536A 4651 mov r1, r10 R00=08073864 R01=0300347c R02=840000b1 R03=040000d4 R04=030077ff R05=000000b1 R06=0300347c R07=020301c8 R08=08073864
0800536C f001 bl $080072fc R00=08073864 R01=020367bc R02=840000b1 R03=040000d4 R04=030077ff R05=000000b1 R06=0300347c R07=020301c8 R08=08073864
73864 contains this graphics part compressed. Let's find out where the pointer 0x08073864 came from. Again, tracing back:
08020C42 4945 ldr r1, [$08020d58] (=$08073864) Than means, there is no pointer table and pointer to compressed graphics is loaded right in the code body.
(?)Debugging code, I've found that one of compressed blocks is placed 434ca8 - 435ef7 in ROM. decompressor/compressor write.
ram out buffer 203d92c - 203f9cb (not sure)
First byte is compression type. 1 means usual previously discussed LZSS code with 8-bit chunks of LZ flags (block stream), and 0 means new LZSS compression scheme with sequental 1 bit of LZ flag in bitstream Other than 1 or 0 codes force exit from decompression routine.
32039c (main ptr table of game starts from 320000) has pointer to our compressed piece. Other entries in main table can be various files, including, for example, blocks encoded with byteFlag LZSS scheme with pointer table in front of it.