Initial commit.

This commit is contained in:
Ariel Antonitis 2019-08-07 20:26:15 -04:00
commit f605b299e0
158 changed files with 1099617 additions and 0 deletions

37
.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
*.exe
*.o
*.i
*.elf
*.gba
*.sgm
*.sa1
*.sg1
*.1bpp
*.4bpp
*.8bpp
*.gbapal
*.lz
*.rl
*.latfont
*.hwjpnfont
*.fwjpnfont
sound/**/*.bin
sound/songs/midi/*.s
tools/agbcc
tools/binutils
tools/br_ips/br_ips
tools/br_ips/ips_patch
*.map
*.ld
*.bat
*.dump
*.sa*
Thumbs.db
build/
.DS_Store
*.ddump
.idea/
porymap.project.cfg
.vscode/
.ropeproject/
.ccls-cache/

35
Makefile Normal file
View File

@ -0,0 +1,35 @@
AS := tools/binutils/bin/arm-none-eabi-as
LD := tools/binutils/bin/arm-none-eabi-ld
OBJCOPY := tools/binutils/bin/arm-none-eabi-objcopy
SHA1SUM := sha1sum -c
GBAFIX := tools/gbafix/gbafix
ASFLAGS := -mcpu=arm7tdmi
ASFILE := $(wildcard asm/*.s data/*.s)
OBJFILE := $(ASFILE:.s=.o)
NAME := pokeemerald_jp
ROM := $(NAME).gba
ELF := $(NAME).elf
TITLE := POKEMON EMER
GAMECODE := BPEJ
.PHONY: all compare clean
all: $(ROM)
compare: $(ROM)
$(SHA1SUM) rom_jp.sha1
clean:
rm -f $(ROM) $(ELF) $(OBJFILE)
$(ROM): $(ELF)
$(OBJCOPY) -O binary $< $@
$(ELF): %.elf: $(OBJFILE) ld_script_jp.txt
$(LD) -T ld_script_jp.txt -Map $*.map -o $@ $(OBJFILE) -L tools/agbcc/lib -lgcc -lc
$(GBAFIX) -t"$(TITLE)" -c$(GAMECODE) -m01 --silent $@
$(OBJFILE): %.o: %.s
$(AS) $(ASFLAGS) -o $@ $<

1173
asm/agb_flash.s Normal file

File diff suppressed because it is too large Load Diff

107
asm/libagbsyscall.s Normal file
View File

@ -0,0 +1,107 @@
.include "asm/macros.inc"
.include "constants/constants.inc"
.text
.syntax unified
thumb_func_start ArcTan2
ArcTan2: @ 0x082959B0
svc #0xa
bx lr
thumb_func_end ArcTan2
thumb_func_start BgAffineSet
BgAffineSet: @ 0x082959B4
svc #0xe
bx lr
thumb_func_end BgAffineSet
thumb_func_start CpuFastSet
CpuFastSet: @ 0x082959B8
svc #0xc
bx lr
thumb_func_end CpuFastSet
thumb_func_start CpuSet
CpuSet: @ 0x082959BC
svc #0xb
bx lr
thumb_func_end CpuSet
thumb_func_start Div
Div: @ 0x082959C0
svc #6
bx lr
thumb_func_end Div
thumb_func_start LZ77UnCompVram
LZ77UnCompVram: @ 0x082959C4
svc #0x12
bx lr
thumb_func_end LZ77UnCompVram
thumb_func_start LZ77UnCompWram
LZ77UnCompWram: @ 0x082959C8
svc #0x11
bx lr
thumb_func_end LZ77UnCompWram
thumb_func_start MultiBoot
MultiBoot: @ 0x082959CC
movs r1, #1
svc #0x25
bx lr
.align 2, 0
thumb_func_end MultiBoot
thumb_func_start ObjAffineSet
ObjAffineSet: @ 0x082959D4
svc #0xf
bx lr
thumb_func_end ObjAffineSet
thumb_func_start RLUnCompVram
RLUnCompVram: @ 0x082959D8
svc #0x15
bx lr
thumb_func_end RLUnCompVram
thumb_func_start RLUnCompWram
RLUnCompWram: @ 0x082959DC
svc #0x14
bx lr
thumb_func_end RLUnCompWram
thumb_func_start RegisterRamReset
RegisterRamReset: @ 0x082959E0
svc #1
bx lr
thumb_func_end RegisterRamReset
thumb_func_start SoftReset
SoftReset: @ 0x082959E4
ldr r3, _082959F4
movs r2, #0
strb r2, [r3]
ldr r1, _082959F8
mov sp, r1
svc #1
svc #0
movs r0, r0
.align 2, 0
_082959F4: .4byte 0x04000208
_082959F8: .4byte gUnknown_3007F00
thumb_func_end SoftReset
thumb_func_start Sqrt
Sqrt: @ 0x082959FC
svc #8
bx lr
thumb_func_end Sqrt
thumb_func_start VBlankIntrWait
VBlankIntrWait: @ 0x08295A00
movs r2, #0
svc #5
bx lr
.align 2, 0
thumb_func_end VBlankIntrWait

8850
asm/libc.s Normal file

File diff suppressed because it is too large Load Diff

5046
asm/libgcc.s Normal file

File diff suppressed because it is too large Load Diff

454
asm/libgcnmultiboot.s Normal file
View File

@ -0,0 +1,454 @@
.include "asm/macros.inc"
.include "constants/constants.inc"
.text
.syntax unified
thumb_func_start GameCubeMultiBoot_Hash
GameCubeMultiBoot_Hash: @ 0x0828D2F8
push {r4, lr}
ldr r4, _0828D400
eors r3, r1
movs r2, #0x20
_0828D300:
lsrs r3, r3, #1
blo _0828D306
eors r3, r4
_0828D306:
subs r2, #1
bne _0828D300
pop {r4, pc}
thumb_func_end GameCubeMultiBoot_Hash
thumb_func_start GameCubeMultiBoot_Main
GameCubeMultiBoot_Main: @ 0x0828D30C
ldr r1, [r0, #0x28]
cmp r1, #0
beq _0828D332
ldrb r1, [r0, #1]
adds r1, #1
strb r1, [r0, #1]
ldrb r1, [r0, #2]
cmp r1, #2
beq _0828D37C
ldr r3, _0828D5C0
ldrh r2, [r3, #8]
movs r1, #0
strh r1, [r3, #8]
ldrb r1, [r0]
cmp r1, #0xa
bgt _0828D330
adds r1, #1
strb r1, [r0]
_0828D330:
strh r2, [r3, #8]
_0828D332:
bhs GameCubeMultiBoot_Init
ldrb r1, [r0, #2]
cmp r1, #0
bne _0828D37E
ldr r1, [r0, #0x24]
ldr r2, [r0, #0x20]
subs r1, r1, r2
beq _0828D3FE
cmp r1, #0xa0
blo _0828D3FE
push {r4, r5, r6}
movs r1, #0x98
adds r2, #4
ldr r4, _0828D408
_0828D34E:
ldm r2!, {r5}
ldm r4!, {r6}
cmp r5, r6
bne _0828D364
subs r1, #4
bne _0828D34E
ldm r2!, {r5}
ldm r4!, {r6}
eors r5, r6
lsrs r5, r5, #8
str r2, [r0, #0x20]
_0828D364:
pop {r4, r5, r6}
bne GameCubeMultiBoot_Init
movs r1, #1
strb r1, [r0, #2]
ldr r1, [r0, #4]
ldr r2, [r0, #8]
eors r1, r2
str r1, [r0, #0x18]
ldr r2, _0828D404
muls r1, r2, r1
adds r1, #1
str r1, [r0, #0x14]
_0828D37C:
bx lr
_0828D37E:
ldr r1, [r0, #0x24]
mov ip, r1
ldr r3, [r0, #0x18]
push {r4, r5, r6, r7}
ldr r4, [r0, #0x20]
ldr r5, _0828D404
ldr r6, [r0, #0x14]
ldr r7, _0828D400
_0828D38E:
cmp r4, ip
bhs _0828D3AE
ldr r1, [r4]
eors r1, r6
adds r1, r1, r3
stm r4!, {r1}
eors r3, r1
movs r2, #0x20
_0828D39E:
lsrs r3, r3, #1
blo _0828D3A4
eors r3, r7
_0828D3A4:
subs r2, #1
bne _0828D39E
muls r6, r5, r6
adds r6, #1
b _0828D38E
_0828D3AE:
str r4, [r0, #0x20]
str r6, [r0, #0x14]
pop {r4, r5, r6, r7}
str r3, [r0, #0x18]
ldrh r1, [r0, #0x12]
cmp r1, #0
bne _0828D3FE
ldr r1, [r0, #0x24]
ldr r2, [r0, #0x20]
cmp r1, r2
bne _0828D3FE
ldr r1, [r0, #0xc]
cmp r1, #0
beq _0828D3E8
ldrh r1, [r0, #0x10]
cmp r1, #0
beq _0828D37C
mov ip, lr
movs r1, #0xbb
ldr r3, [r0, #0xc]
bl GameCubeMultiBoot_Hash
ldrh r1, [r0, #0x10]
mov lr, ip
subs r1, r1, r3
bne GameCubeMultiBoot_Init
movs r1, #2
strb r1, [r0, #2]
bx lr
_0828D3E8:
mov ip, lr
ldrb r1, [r0, #3]
lsls r1, r1, #0x18
subs r1, #1
str r1, [r0, #0xc]
bl GameCubeMultiBoot_Hash
lsls r3, r3, #8
adds r3, #0xff
str r3, [r0, #0x1c]
bx ip
_0828D3FE:
bx lr
.align 2, 0
_0828D400: .4byte 0x0000A1C1
_0828D404: .4byte 0x6177614B
_0828D408: .4byte 0x08000004
thumb_func_end GameCubeMultiBoot_Main
thumb_func_start GameCubeMultiBoot_ExecuteProgram
GameCubeMultiBoot_ExecuteProgram: @ 0x0828D40C
ldrb r1, [r0, #2]
cmp r1, #2
bne _0828D41E
ldr r3, _0828D5C0
movs r1, #0
strh r1, [r3, #8]
ldr r1, _0828D5D0
adds r1, #0xc0
bx r1
_0828D41E:
bx lr
thumb_func_end GameCubeMultiBoot_ExecuteProgram
thumb_func_start GameCubeMultiBoot_Init
GameCubeMultiBoot_Init:
ldr r3, _0828D5C0
ldrh r2, [r3, #8]
movs r1, #0
strh r1, [r3, #8]
add r3, pc, #0x54
str r3, [r0, #0x28]
ldrb r3, [r0, #3]
push {r3}
ldrb r3, [r0, #1]
push {r0, r3}
adds r3, r0, #0
adds r3, #0x20
_0828D438:
stm r0!, {r1}
cmp r0, r3
blo _0828D438
pop {r0, r3}
lsrs r3, r3, #1
strb r3, [r0, #3]
pop {r3}
strb r3, [r0, #1]
ldr r3, _0828D5C4
lsls r0, r3, #0xa
strh r0, [r3, #0x14]
movs r0, #0xc0
lsls r0, r0, #8
strh r0, [r3, #0x14]
movs r0, #0x47
strh r0, [r3, #0x20]
strh r1, [r3, #0x38]
ldr r3, _0828D5C0
movs r0, #0x80
strh r0, [r3, #2]
ldrh r1, [r3]
orrs r1, r0
strh r1, [r3]
strh r2, [r3, #8]
bx lr
thumb_func_end GameCubeMultiBoot_ExecuteProgram
non_word_aligned_thumb_func_start GameCubeMultiBoot_HandleSerialInterrupt
GameCubeMultiBoot_HandleSerialInterrupt: @ 0x0828D46A
ldr r3, _0828D5C4
ldrh r1, [r3, #0x20]
strh r1, [r3, #0x20]
movs r2, #0
strb r2, [r0]
ldr r2, [r0, #0x28]
cmp r2, #0
beq _0828D48C
lsrs r1, r1, #1
bhs _0828D48E
mov pc, r2
_0828D480:
movs r2, #0
strh r2, [r3, #0x38]
_0828D484:
str r2, [r0, #0x28]
_0828D486:
ldr r3, _0828D5C8
ldrh r1, [r3, #2]
strb r1, [r0, #3]
_0828D48C:
bx lr
_0828D48E:
ldr r1, [r3, #0x30]
ldr r1, _0828D5CC
str r1, [r3, #0x34]
movs r1, #0x10
strh r1, [r3, #0x38]
ldrb r1, [r0, #3]
strb r1, [r0, #9]
ldrb r1, [r0, #2]
cmp r1, #0
bne _0828D480
ldr r1, _0828D5D0
str r1, [r0, #0x20]
str r1, [r0, #0x24]
add r2, pc, #0x0
b _0828D484
thumb_func_end GameCubeMultiBoot_HandleSerialInterrupt
thumb_func_start GcMbIntrHandler_CheckGameCodeSent
GcMbIntrHandler_CheckGameCodeSent: @ 0x0828D4AC
lsls r1, r1, #0x1f
blo _0828D480
bmi _0828D4BC
add r2, pc, #0x4
b _0828D484
.align 2, 0
thumb_func_end GcMbIntrHandler_CheckGameCodeSent
thumb_func_start GcMbIntrHandler_CheckHandshakeResponse
GcMbIntrHandler_CheckHandshakeResponse: @ 0x0828D4B8
lsrs r1, r1, #1
blo _0828D480
_0828D4BC:
ldr r1, [r3, #0x30]
ldr r2, _0828D5CC
cmp r1, r2
bne _0828D480
ldrb r1, [r0, #3]
strb r1, [r0, #0xb]
add r2, pc, #0x0
b _0828D484
thumb_func_end GcMbIntrHandler_CheckHandshakeResponse
thumb_func_start GcMbIntrHandler_ReceiveKeyA
GcMbIntrHandler_ReceiveKeyA: @ 0x0828D4CC
lsrs r1, r1, #1
blo _0828D480
ldr r1, [r3, #0x30]
lsrs r2, r1, #0x18
cmp r2, #0xdd
bne _0828D480
str r1, [r0, #4]
ldrb r1, [r0, #1]
strb r1, [r0, #0xa]
movs r2, #0
movs r3, #0
ldr r1, [r0, #8]
lsrs r1, r1, #8
_0828D4E6:
lsrs r1, r1, #1
adcs r2, r3
cmp r1, #0
bne _0828D4E6
cmp r2, #0xe
bgt _0828D4F8
cmp r2, #7
bge _0828D4FA
movs r1, #0xff
_0828D4F8:
strb r1, [r0, #0xa]
_0828D4FA:
ldr r1, [r0, #8]
adds r1, #0xee
ldr r3, _0828D5C4
str r1, [r3, #0x34]
movs r1, #0x30
strh r1, [r3, #0x38]
add r2, pc, #0x4
b _0828D484
.align 2, 0
thumb_func_end GcMbIntrHandler_ReceiveKeyA
thumb_func_start GcMbIntrHandler_CheckKeyBSent
GcMbIntrHandler_CheckKeyBSent: @ 0x0828D50C
lsls r1, r1, #0x1f
blo _0828D480
bmi _0828D51C
add r2, pc, #0x4
b _0828D484
.align 2, 0
thumb_func_end GcMbIntrHandler_CheckKeyBSent
thumb_func_start GcMbIntrHandler_CheckImageSizeResponse
GcMbIntrHandler_CheckImageSizeResponse: @ 0x0828D518
lsrs r1, r1, #1
blo _0828D480
_0828D51C:
ldr r1, [r3, #0x30]
ldr r2, _0828D5BC
cmp r1, r2
bhs _0828D480
adds r1, #1
adds r1, r1, r1
strh r1, [r0, #0x12]
ldrb r1, [r0, #2]
cmp r1, #0
_0828D52E:
bne _0828D480
ldr r1, _0828D5D0
str r1, [r0, #0x20]
str r1, [r0, #0x24]
add r2, pc, #0x4
b _0828D484
.align 2, 0
thumb_func_end GcMbIntrHandler_CheckImageSizeResponse
thumb_func_start GcMbIntrHandler_CheckImageResponse
GcMbIntrHandler_CheckImageResponse: @ 0x0828D53C
lsrs r1, r1, #1
blo _0828D480
ldr r2, [r0, #0x24]
movs r1, #4
ands r1, r2
adds r1, #8
lsls r1, r1, #2
strh r1, [r3, #0x38]
ldr r1, [r3, #0x30]
stm r2!, {r1}
str r2, [r0, #0x24]
ldrh r1, [r0, #0x12]
subs r1, #1
strh r1, [r0, #0x12]
bne _0828D486
_0828D55A:
ldrb r1, [r0, #1]
lsls r1, r1, #8
adds r1, #0xcc
str r1, [r3, #0x34]
add r2, pc, #0x4
b _0828D484
.align 2, 0
thumb_func_end GcMbIntrHandler_CheckImageResponse
thumb_func_start GcMbIntrHandler_CheckCounter2Sent
GcMbIntrHandler_CheckCounter2Sent: @ 0x0828D568
lsls r1, r1, #0x1f
_0828D56A:
blo _0828D480
ldr r1, [r0, #0x1c]
cmp r1, #0
beq _0828D55A
str r1, [r3, #0x34]
add r2, pc, #0x0
b _0828D484
thumb_func_end GcMbIntrHandler_CheckCounter2Sent
thumb_func_start GcMbIntrHandler_CheckKeyCDerivationSent
GcMbIntrHandler_CheckKeyCDerivationSent: @ 0x0828D578
lsls r1, r1, #0x1f
blo _0828D56A
bmi _0828D588
add r2, pc, #0x4
b _0828D484
.align 2, 0
thumb_func_end GcMbIntrHandler_CheckKeyCDerivationSent
thumb_func_start GcMbIntrHandler_CheckBootKeyResponse
GcMbIntrHandler_CheckBootKeyResponse: @ 0x0828D584
lsrs r1, r1, #1
blo _0828D56A
_0828D588:
ldr r1, [r3, #0x30]
lsrs r2, r1, #0x18
cmp r2, #0xbb
bne _0828D52E
strh r1, [r0, #0x10]
add r2, pc, #0x4
b _0828D484
.align 2, 0
thumb_func_end GcMbIntrHandler_CheckBootKeyResponse
thumb_func_start GcMbIntrHandler_StopUnconditionally
GcMbIntrHandler_StopUnconditionally: @ 0x0828D598
b _0828D480
thumb_func_end GcMbIntrHandler_StopUnconditionally
non_word_aligned_thumb_func_start GameCubeMultiBoot_Quit
GameCubeMultiBoot_Quit: @ 0x0828D59A
ldr r3, _0828D5C0
ldrh r2, [r3, #8]
movs r1, #0
strh r1, [r3, #8]
ldr r3, _0828D5C4
movs r0, #7
strh r0, [r3, #0x20]
lsls r0, r3, #0xa
strh r0, [r3, #0x14]
ldr r3, _0828D5C0
movs r0, #0x80
strh r0, [r3, #2]
ldrh r1, [r3]
bics r1, r0
strh r1, [r3]
strh r2, [r3, #8]
bx lr
.align 2, 0
_0828D5BC: .4byte 0x00004000
_0828D5C0: .4byte 0x04000200
_0828D5C4: .4byte 0x04000120
_0828D5C8: .4byte 0x04000004
_0828D5CC: .4byte 0x4A565841
_0828D5D0: .4byte gUnknown_2000000
thumb_func_end GameCubeMultiBoot_Quit

502
asm/libisagbprn.s Normal file
View File

@ -0,0 +1,502 @@
.include "asm/macros.inc"
.include "constants/constants.inc"
.text
.syntax unified
thumb_func_start AGBPrintInit
AGBPrintInit: @ 0x08295600
push {r4, r7, lr}
sub sp, #0x10
mov r7, sp
ldr r0, _0829567C
str r0, [r7]
ldr r0, _08295680
str r0, [r7, #4]
ldr r0, _08295684
str r0, [r7, #8]
adds r0, r7, #0
adds r0, #0xc
ldr r1, [r7, #4]
ldrh r2, [r1]
strh r2, [r0]
ldr r0, [r7, #4]
movs r2, #0xc0
lsls r2, r2, #5
adds r1, r2, #0
strh r1, [r0]
ldr r0, [r7, #8]
movs r1, #0x20
strh r1, [r0]
ldr r0, [r7]
ldr r1, [r7]
ldr r2, [r7]
ldrh r3, [r2, #6]
movs r4, #0
ands r3, r4
adds r4, r3, #0
strh r4, [r2, #6]
ldrh r2, [r1, #4]
movs r3, #0
ands r2, r3
adds r3, r2, #0
strh r3, [r1, #4]
ldrh r1, [r0]
movs r2, #0
ands r1, r2
adds r2, r1, #0
strh r2, [r0]
ldr r0, [r7]
ldrh r1, [r0, #2]
movs r2, #0
ands r1, r2
adds r2, r1, #0
movs r3, #0xfd
adds r1, r2, #0
orrs r1, r3
adds r2, r1, #0
strh r2, [r0, #2]
ldr r0, [r7, #8]
movs r1, #0
strh r1, [r0]
ldr r0, [r7, #4]
adds r1, r7, #0
adds r1, #0xc
ldrh r2, [r1]
strh r2, [r0]
add sp, #0x10
pop {r4, r7}
pop {r0}
bx r0
.align 2, 0
_0829567C: .4byte 0x09FE20F8
_08295680: .4byte 0x04000204
_08295684: .4byte 0x09FE2FFE
thumb_func_end AGBPrintInit
thumb_func_start AGBPutcInternal
AGBPutcInternal: @ 0x08295688
push {r4, r7, lr}
sub sp, #0x14
mov r7, sp
adds r1, r7, #0
strb r0, [r1]
ldr r0, _08295708
str r0, [r7, #4]
ldr r0, [r7, #4]
ldrh r1, [r0, #2]
lsls r2, r1, #0x10
lsrs r0, r2, #0x10
lsls r1, r0, #0x10
movs r2, #0x80
lsls r2, r2, #0x14
adds r0, r1, r2
str r0, [r7, #8]
ldr r0, _0829570C
str r0, [r7, #0xc]
adds r0, r7, #0
adds r0, #0x10
ldr r1, [r7, #4]
ldrh r2, [r1, #6]
lsls r1, r2, #0x10
lsrs r2, r1, #0x10
lsrs r1, r2, #1
adds r3, r1, #0
lsls r2, r3, #0x10
lsrs r1, r2, #0x10
adds r2, r1, #0
lsls r1, r2, #1
ldr r2, [r7, #8]
adds r1, r1, r2
ldrh r2, [r1]
strh r2, [r0]
ldr r0, [r7, #0xc]
movs r1, #0x20
strh r1, [r0]
adds r0, r7, #0
adds r0, #0x10
ldr r1, [r7, #4]
ldrh r2, [r1, #6]
movs r3, #1
adds r1, r2, #0
ands r1, r3
adds r3, r1, #0
lsls r2, r3, #0x10
lsrs r1, r2, #0x10
cmp r1, #0
beq _08295710
adds r1, r7, #0
adds r1, #0x10
ldrh r2, [r1]
movs r3, #0xff
adds r1, r2, #0
ands r1, r3
adds r2, r7, #0
ldrb r3, [r2]
adds r2, r3, #0
lsls r3, r2, #8
adds r2, r3, #0
adds r3, r1, #0
orrs r3, r2
adds r1, r3, #0
b _08295728
.align 2, 0
_08295708: .4byte 0x09FE20F8
_0829570C: .4byte 0x09FE2FFE
_08295710:
adds r2, r7, #0
adds r2, #0x10
ldrh r3, [r2]
movs r4, #0xff
lsls r4, r4, #8
adds r2, r3, #0
ands r2, r4
adds r3, r7, #0
ldrb r4, [r3]
adds r3, r4, #0
orrs r3, r2
adds r1, r3, #0
_08295728:
strh r1, [r0]
ldr r0, [r7, #4]
ldrh r1, [r0, #6]
lsls r0, r1, #0x10
lsrs r1, r0, #0x10
lsrs r0, r1, #1
adds r2, r0, #0
lsls r1, r2, #0x10
lsrs r0, r1, #0x10
adds r1, r0, #0
lsls r0, r1, #1
ldr r1, [r7, #8]
adds r0, r0, r1
adds r1, r7, #0
adds r1, #0x10
ldrh r2, [r1]
strh r2, [r0]
ldr r1, [r7, #4]
ldr r0, [r7, #4]
ldr r1, [r7, #4]
ldrh r2, [r1, #6]
adds r1, r2, #1
ldrh r2, [r0, #6]
movs r3, #0
ands r2, r3
adds r3, r2, #0
adds r2, r3, #0
orrs r2, r1
adds r1, r2, #0
strh r1, [r0, #6]
ldr r0, [r7, #0xc]
movs r1, #0
strh r1, [r0]
add sp, #0x14
pop {r4, r7}
pop {r0}
bx r0
.align 2, 0
thumb_func_end AGBPutcInternal
thumb_func_start AGBPutc
AGBPutc: @ 0x08295774
push {r7, lr}
sub sp, #0x10
mov r7, sp
adds r1, r7, #0
strb r0, [r1]
ldr r0, _082957CC
str r0, [r7, #4]
adds r0, r7, #0
adds r0, #8
ldr r1, [r7, #4]
ldrh r2, [r1]
strh r2, [r0]
ldr r0, [r7, #4]
movs r2, #0xc0
lsls r2, r2, #5
adds r1, r2, #0
strh r1, [r0]
adds r0, r7, #0
ldrb r1, [r0]
adds r0, r1, #0
bl AGBPutcInternal
ldr r0, [r7, #4]
adds r1, r7, #0
adds r1, #8
ldrh r2, [r1]
strh r2, [r0]
ldr r0, _082957D0
str r0, [r7, #0xc]
ldr r1, [r7, #0xc]
ldrh r0, [r1, #6]
ldr r1, [r7, #0xc]
ldrh r2, [r1, #4]
subs r1, r2, #1
lsls r2, r1, #0x10
lsrs r1, r2, #0x10
cmp r0, r1
bne _082957C4
bl AGBPrintFlush1Block
_082957C4:
add sp, #0x10
pop {r7}
pop {r0}
bx r0
.align 2, 0
_082957CC: .4byte 0x04000204
_082957D0: .4byte 0x09FE20F8
thumb_func_end AGBPutc
thumb_func_start AGBPrint
AGBPrint: @ 0x082957D4
push {r7, lr}
sub sp, #0x10
mov r7, sp
str r0, [r7]
ldr r0, _08295804
str r0, [r7, #4]
ldr r0, _08295808
str r0, [r7, #8]
adds r0, r7, #0
adds r0, #0xc
ldr r1, [r7, #8]
ldrh r2, [r1]
strh r2, [r0]
ldr r0, [r7, #8]
movs r2, #0xc0
lsls r2, r2, #5
adds r1, r2, #0
strh r1, [r0]
_082957F8:
ldr r0, [r7]
ldrb r1, [r0]
cmp r1, #0
bne _0829580C
b _0829581E
.align 2, 0
_08295804: .4byte 0x09FE20F8
_08295808: .4byte 0x04000204
_0829580C:
ldr r0, [r7]
ldrb r1, [r0]
adds r0, r1, #0
bl AGBPutc
ldr r0, [r7]
adds r1, r0, #1
str r1, [r7]
b _082957F8
_0829581E:
ldr r0, [r7, #8]
adds r1, r7, #0
adds r1, #0xc
ldrh r2, [r1]
strh r2, [r0]
add sp, #0x10
pop {r7}
pop {r0}
bx r0
thumb_func_end AGBPrint
thumb_func_start AGBPrintf
AGBPrintf: @ 0x08295830
push {r0, r1, r2, r3}
push {r7, lr}
sub sp, #0x104
mov r7, sp
movs r1, #0x88
lsls r1, r1, #1
adds r0, r7, r1
adds r1, r7, #0
movs r3, #0x80
lsls r3, r3, #1
adds r2, r7, r3
str r0, [r2]
adds r0, r7, #0
adds r1, r7, #0
movs r1, #0x80
lsls r1, r1, #1
adds r3, r7, r1
ldr r2, [r3]
movs r3, #0x86
lsls r3, r3, #1
adds r3, r3, r7
ldr r1, [r3]
bl vsprintf
adds r1, r7, #0
adds r0, r1, #0
bl AGBPrint
add sp, #0x104
pop {r7}
pop {r3}
add sp, #0x10
bx r3
.align 2, 0
thumb_func_end AGBPrintf
thumb_func_start AGBPrintTransferDataInternal
AGBPrintTransferDataInternal: @ 0x08295874
push {r4, r7, lr}
sub sp, #0x20
mov r7, sp
str r0, [r7]
ldr r0, _082958DC
str r0, [r7, #0x18]
ldr r0, _082958E0
str r0, [r7, #0x1c]
ldr r0, _082958E4
str r0, [r7, #4]
ldr r0, _082958E8
str r0, [r7, #8]
adds r0, r7, #0
adds r0, #0xc
ldr r1, [r7, #8]
ldrh r2, [r1]
strh r2, [r0]
ldr r0, _082958EC
str r0, [r7, #0x10]
adds r0, r7, #0
adds r0, #0x14
ldr r1, [r7, #0x10]
ldrh r2, [r1]
strh r2, [r0]
ldr r0, [r7, #8]
adds r1, r7, #0
adds r1, #0xc
ldrh r2, [r1]
ldr r3, _082958F0
adds r1, r2, #0
ands r1, r3
adds r2, r1, #0
strh r2, [r0]
ldr r0, [r7, #0x10]
movs r2, #0xc0
lsls r2, r2, #5
adds r1, r2, #0
strh r1, [r0]
ldr r0, [r7]
cmp r0, #0
beq _0829590A
_082958C6:
ldr r1, [r7, #0x1c]
ldr r0, [r7, #0x1c]
ldrh r1, [r1, #6]
lsls r2, r1, #0x10
lsrs r1, r2, #0x10
ldrh r0, [r0, #4]
lsls r2, r0, #0x10
lsrs r0, r2, #0x10
cmp r1, r0
bne _082958F4
b _08295908
.align 2, 0
_082958DC: .4byte 0x09FE2FFE
_082958E0: .4byte 0x09FE20F8
_082958E4: .4byte 0x09FE209D
_082958E8: .4byte 0x04000208
_082958EC: .4byte 0x04000204
_082958F0: .4byte 0x0000FFFE
_082958F4:
ldr r0, [r7, #0x18]
movs r1, #0x20
strh r1, [r0]
ldr r4, [r7, #4]
bl _call_via_r4
ldr r0, [r7, #0x18]
movs r1, #0
strh r1, [r0]
b _082958C6
_08295908:
b _08295930
_0829590A:
ldr r1, [r7, #0x1c]
ldr r0, [r7, #0x1c]
ldrh r1, [r1, #6]
lsls r2, r1, #0x10
lsrs r1, r2, #0x10
ldrh r0, [r0, #4]
lsls r2, r0, #0x10
lsrs r0, r2, #0x10
cmp r1, r0
beq _08295930
ldr r0, [r7, #0x18]
movs r1, #0x20
strh r1, [r0]
ldr r4, [r7, #4]
bl _call_via_r4
ldr r0, [r7, #0x18]
movs r1, #0
strh r1, [r0]
_08295930:
ldr r0, [r7, #0x10]
adds r1, r7, #0
adds r1, #0x14
ldrh r2, [r1]
strh r2, [r0]
ldr r0, [r7, #8]
adds r1, r7, #0
adds r1, #0xc
ldrh r2, [r1]
strh r2, [r0]
add sp, #0x20
pop {r4, r7}
pop {r0}
bx r0
thumb_func_end AGBPrintTransferDataInternal
thumb_func_start AGBPrintFlush1Block
AGBPrintFlush1Block: @ 0x0829594C
push {r7, lr}
mov r7, sp
movs r0, #0
bl AGBPrintTransferDataInternal
pop {r7}
pop {r0}
bx r0
thumb_func_end AGBPrintFlush1Block
thumb_func_start AGBPrintFlush
AGBPrintFlush: @ 0x0829595C
push {r7, lr}
mov r7, sp
movs r0, #1
bl AGBPrintTransferDataInternal
pop {r7}
pop {r0}
bx r0
thumb_func_end AGBPrintFlush
thumb_func_start AGBAssert
AGBAssert: @ 0x0829596C
push {r7, lr}
sub sp, #0x10
mov r7, sp
str r0, [r7]
str r1, [r7, #4]
str r2, [r7, #8]
str r3, [r7, #0xc]
ldr r0, [r7, #0xc]
cmp r0, #0
beq _08295998
ldr r0, _08295994
ldr r2, [r7, #4]
ldr r3, [r7, #8]
ldr r1, [r7]
bl AGBPrintf
bl AGBPrintFlush
.hword 0xEFFF
b _082959A4
.align 2, 0
_08295994: .4byte gUnknown_890EEF4
_08295998:
ldr r0, _082959AC
ldr r2, [r7, #4]
ldr r3, [r7, #8]
ldr r1, [r7]
bl AGBPrintf
_082959A4:
add sp, #0x10
pop {r7}
pop {r0}
bx r0
.align 2, 0
_082959AC: .4byte gUnknown_890EF28
thumb_func_end AGBAssert

395
asm/librfu.s Normal file
View File

@ -0,0 +1,395 @@
.include "asm/macros.inc"
.include "constants/constants.inc"
.text
.syntax unified
thumb_func_start sub_082952F4
sub_082952F4: @ 0x082952F4
push {r4, r5, r6, r7, lr}
mov r7, r8
push {r7}
lsls r0, r0, #0x18
lsrs r5, r0, #0x18
ldr r0, _0829530C
ldrh r0, [r0]
cmp r0, #0
bne _08295310
movs r0, #1
rsbs r0, r0, #0
b _08295390
.align 2, 0
_0829530C: .4byte 0x04000208
_08295310:
ldr r0, _0829533C
ldrh r0, [r0]
mov r8, r0
ldr r4, _08295340
ldr r1, [r4]
movs r0, #0xa
str r0, [r1]
ldr r0, _08295344
bl STWI_set_Callback_ID
bl sub_082953A8
ldr r0, [r4]
ldrb r0, [r0, #0xa]
lsls r0, r0, #2
ldr r1, _08295348
adds r4, r0, r1
lsls r0, r5, #0x1b
lsrs r5, r0, #0x18
movs r7, #0
b _0829535E
.align 2, 0
_0829533C: .4byte 0x04000200
_08295340: .4byte gUnknown_3007608
_08295344: .4byte sub_08295510
_08295348: .4byte 0x04000100
_0829534C:
strh r6, [r4, #2]
strh r6, [r4]
movs r0, #0x83
strh r0, [r4, #2]
_08295354:
ldrh r0, [r4]
cmp r0, #0x1f
bls _08295354
strh r7, [r4, #2]
strh r7, [r4]
_0829535E:
subs r0, r5, #1
lsls r0, r0, #0x18
lsrs r5, r0, #0x18
cmp r5, #0xff
beq _08295372
bl sub_0829541C
adds r6, r0, #0
cmp r6, #0
beq _0829534C
_08295372:
ldr r1, _0829539C
movs r0, #0
strh r0, [r1]
ldr r0, _082953A0
mov r2, r8
strh r2, [r0]
movs r0, #1
strh r0, [r1]
ldr r0, _082953A4
ldr r1, [r0]
movs r0, #0
str r0, [r1]
bl STWI_set_Callback_ID
adds r0, r6, #0
_08295390:
pop {r3}
mov r8, r3
pop {r4, r5, r6, r7}
pop {r1}
bx r1
.align 2, 0
_0829539C: .4byte 0x04000208
_082953A0: .4byte 0x04000200
_082953A4: .4byte gUnknown_3007608
thumb_func_end sub_082952F4
thumb_func_start sub_082953A8
sub_082953A8: @ 0x082953A8
push {r4, r5, lr}
sub sp, #4
ldr r3, _08295400
movs r4, #0
strh r4, [r3]
ldr r2, _08295404
ldr r0, _08295408
ldr r0, [r0]
movs r1, #8
ldrb r0, [r0, #0xa]
lsls r1, r0
movs r5, #0x80
orrs r1, r5
ldrh r0, [r2]
bics r0, r1
strh r0, [r2]
movs r0, #1
strh r0, [r3]
ldr r0, _0829540C
strh r4, [r0]
subs r2, #0xd8
movs r1, #0x80
lsls r1, r1, #5
adds r0, r1, #0
strh r0, [r2]
ldrh r0, [r2]
movs r3, #0x81
lsls r3, r3, #7
adds r1, r3, #0
orrs r0, r1
strh r0, [r2]
movs r0, #0
str r0, [sp]
ldr r1, _08295410
ldr r2, _08295414
mov r0, sp
bl CpuSet
ldr r0, _08295418
strh r5, [r0]
add sp, #4
pop {r4, r5}
pop {r0}
bx r0
.align 2, 0
_08295400: .4byte 0x04000208
_08295404: .4byte 0x04000200
_08295408: .4byte gUnknown_3007608
_0829540C: .4byte 0x04000134
_08295410: .4byte gUnknown_3007640
_08295414: .4byte 0x05000003
_08295418: .4byte 0x04000202
thumb_func_end sub_082953A8
thumb_func_start sub_0829541C
sub_0829541C: @ 0x0829541C
push {r4, r5, r6, r7, lr}
ldr r0, _08295430
ldrb r1, [r0, #1]
mov ip, r1
adds r7, r0, #0
cmp r1, #0
beq _08295434
cmp r1, #1
beq _0829546C
b _08295504
.align 2, 0
_08295430: .4byte gUnknown_3007640
_08295434:
movs r6, #1
strb r6, [r7]
ldr r3, _08295460
ldrh r0, [r3]
movs r5, #1
orrs r0, r5
strh r0, [r3]
ldr r4, _08295464
mov r0, ip
strh r0, [r4]
ldr r2, _08295468
ldrh r0, [r2]
movs r1, #0x80
orrs r0, r1
strh r0, [r2]
strh r6, [r4]
strb r5, [r7, #1]
ldrb r0, [r3]
orrs r0, r1
strb r0, [r3]
b _08295508
.align 2, 0
_08295460: .4byte 0x04000128
_08295464: .4byte 0x04000208
_08295468: .4byte 0x04000200
_0829546C:
ldrh r0, [r7, #0xa]
cmp r0, #0
bne _08295500
ldrb r0, [r7]
cmp r0, #1
bne _0829549C
ldrh r0, [r7, #2]
cmp r0, #0
bne _08295508
ldr r3, _08295494
strh r0, [r3]
ldr r2, _08295498
ldrh r0, [r2]
movs r1, #0x80
orrs r0, r1
strh r0, [r2]
mov r1, ip
strh r1, [r3]
b _08295508
.align 2, 0
_08295494: .4byte 0x04000208
_08295498: .4byte 0x04000128
_0829549C:
ldrh r1, [r7, #4]
ldr r0, _082954E8
cmp r1, r0
beq _08295508
ldrh r6, [r7, #2]
cmp r6, #0
bne _08295508
ldr r4, _082954EC
strh r6, [r4]
ldr r3, _082954F0
ldrh r1, [r3]
ldr r0, _082954F4
ands r0, r1
strh r0, [r3]
mov r7, ip
strh r7, [r4]
ldr r2, _082954F8
strh r6, [r2]
movs r1, #0x80
lsls r1, r1, #5
adds r0, r1, #0
strh r0, [r2]
ldr r0, _082954FC
movs r5, #0x80
strh r5, [r0]
ldrh r0, [r2]
movs r7, #0x81
lsls r7, r7, #7
adds r1, r7, #0
orrs r0, r1
strh r0, [r2]
strh r6, [r4]
ldrh r0, [r3]
orrs r0, r5
strh r0, [r3]
mov r0, ip
strh r0, [r4]
b _08295508
.align 2, 0
_082954E8: .4byte 0x00008001
_082954EC: .4byte 0x04000208
_082954F0: .4byte 0x04000200
_082954F4: .4byte 0x0000FF7F
_082954F8: .4byte 0x04000128
_082954FC: .4byte 0x04000202
_08295500:
movs r0, #2
strb r0, [r7, #1]
_08295504:
ldrh r0, [r7, #0xa]
b _0829550A
_08295508:
movs r0, #0
_0829550A:
pop {r4, r5, r6, r7}
pop {r1}
bx r1
thumb_func_end sub_0829541C
thumb_func_start sub_08295510
sub_08295510: @ 0x08295510
push {r4, r5, lr}
ldr r0, _08295558
ldr r5, [r0]
ldr r0, _0829555C
ldrb r1, [r0]
adds r4, r0, #0
cmp r1, #1
beq _0829552A
ldr r0, _08295560
ldrh r1, [r0]
movs r2, #0x80
orrs r1, r2
strh r1, [r0]
_0829552A:
ldrb r2, [r4]
lsls r1, r2, #4
adds r0, r5, #0
lsls r0, r1
lsrs r1, r0, #0x10
movs r0, #1
subs r0, r0, r2
lsls r0, r0, #4
lsls r5, r0
adds r0, r5, #0
lsrs r5, r0, #0x10
ldrh r0, [r4, #0xa]
cmp r0, #0
bne _0829557E
ldrh r2, [r4, #6]
cmp r1, r2
bne _0829557C
ldrh r3, [r4, #2]
cmp r3, #3
bls _08295564
strh r5, [r4, #0xa]
b _0829557E
.align 2, 0
_08295558: .4byte 0x04000120
_0829555C: .4byte gUnknown_3007640
_08295560: .4byte 0x04000128
_08295564:
ldrh r0, [r4, #4]
mvns r0, r0
lsls r0, r0, #0x10
lsrs r0, r0, #0x10
cmp r1, r0
bne _0829557E
mvns r0, r2
lsls r0, r0, #0x10
lsrs r0, r0, #0x10
cmp r5, r0
bne _0829557E
adds r0, r3, #1
_0829557C:
strh r0, [r4, #2]
_0829557E:
ldrh r0, [r4, #2]
cmp r0, #3
bhi _08295594
lsls r0, r0, #1
ldr r1, _08295590
adds r0, r0, r1
ldrh r0, [r0]
b _08295596
.align 2, 0
_08295590: .4byte gUnknown_890EEDC
_08295594:
ldr r0, _082955EC
_08295596:
strh r0, [r4, #4]
mvns r0, r5
strh r0, [r4, #6]
ldr r3, _082955F0
ldrh r2, [r4, #4]
ldrb r1, [r4]
movs r0, #1
subs r0, r0, r1
lsls r0, r0, #4
lsls r2, r0
ldrh r0, [r4, #6]
lsls r1, r1, #4
lsls r0, r1
adds r2, r2, r0
str r2, [r3]
ldrb r0, [r4]
cmp r0, #1
bne _082955E4
ldrh r0, [r4, #2]
cmp r0, #0
bne _082955C6
ldr r0, _082955F4
cmp r5, r0
bne _082955E4
_082955C6:
movs r0, #0
ldr r1, _082955F8
_082955CA:
adds r0, #1
lsls r0, r0, #0x10
lsrs r0, r0, #0x10
cmp r0, r1
bls _082955CA
ldrh r0, [r4, #0xa]
cmp r0, #0
bne _082955E4
ldr r0, _082955FC
ldrh r1, [r0]
movs r2, #0x80
orrs r1, r2
strh r1, [r0]
_082955E4:
pop {r4, r5}
pop {r0}
bx r0
.align 2, 0
_082955EC: .4byte 0x00008001
_082955F0: .4byte 0x04000120
_082955F4: .4byte 0x0000494E
_082955F8: .4byte 0x00000257
_082955FC: .4byte 0x04000128
thumb_func_end sub_08295510

700
asm/librfu_intr.s Normal file
View File

@ -0,0 +1,700 @@
.include "asm/macros.inc"
.include "constants/constants.inc"
.text
.syntax unified
arm_func_start IntrSIO32
IntrSIO32: @ 0x08291ADC
mov ip, sp
push {fp, ip, lr, pc}
ldr r3, _08291B3C
ldr r0, [r3]
ldr r2, [r0]
sub fp, ip, #4
cmp r2, #0xa
bne _08291B18
ldr r0, [r0, #0x20]
cmp r0, #0
ldmdbeq fp, {fp, sp, lr}
bxeq lr
arm_func_end IntrSIO32
arm_func_start sub_08291B0C
sub_08291B0C: @ 0x08291B0C
bl sub_08292438
ldmdb fp, {fp, sp, lr}
bx lr
_08291B18:
ldrb r3, [r0, #0x14]
cmp r3, #1
bne _08291B30
bl sio32intr_clock_master
ldmdb fp, {fp, sp, lr}
bx lr
_08291B30:
bl sio32intr_clock_slave
ldmdb fp, {fp, sp, lr}
bx lr
.align 2, 0
_08291B3C: .4byte gUnknown_3007608
arm_func_end sub_08291B0C
arm_func_start sio32intr_clock_master
sio32intr_clock_master: @ 0x08291B40
mov ip, sp
push {r4, r5, r6, fp, ip, lr, pc}
mov r0, #0x50
sub fp, ip, #4
bl STWI_set_timer_in_RAM
mov r4, #0x120
ldr r2, _08291DB4
add r4, r4, #0x4000000
ldr lr, [r4]
ldr ip, [r2]
ldr r3, [ip]
mov r6, r2
cmp r3, #0
bne _08291BC0
cmp lr, #-0x80000000
bne _08291C40
ldrb r2, [ip, #5]
ldrb r3, [ip, #4]
cmp r2, r3
bhi _08291BB0
ldr r3, [ip, #0x24]
mov r1, r2
ldr r2, [r3, r1, lsl #2]
str r2, [r4]
ldrb r3, [ip, #5]
add r3, r3, #1
strb r3, [ip, #5]
b _08291C9C
_08291BB0:
mov r3, #1
str r3, [ip]
str lr, [r4]
b _08291C9C
_08291BC0:
ldr r3, [ip]
cmp r3, #1
bne _08291C50
mov r3, #-0x67000000
add r3, r3, #0x660000
mov r5, #-0x80000000
and r2, lr, r5, asr #15
cmp r2, r3
bne _08291C40
mov r3, #0
strb r3, [ip, #8]
ldr r1, [r6]
ldrb r0, [r1, #8]
ldr r2, [r1, #0x28]
str lr, [r2, r0, lsl #2]
ldrb r3, [r1, #8]
add r3, r3, #1
strb r3, [r1, #8]
ldr r2, [r6]
strb lr, [r2, #9]
ldr r3, [r6]
lsr r2, lr, #8
strb r2, [r3, #7]
ldr r1, [r6]
ldrb r2, [r1, #7]
ldrb r3, [r1, #8]
cmp r2, r3
blo _08291C88
mov r3, #2
str r3, [r1]
str r5, [r4]
b _08291C9C
_08291C40:
bl STWI_stop_timer_in_RAM
mov r0, #0x82
bl STWI_set_timer_in_RAM
b _08291DC8
_08291C50:
ldr r3, [ip]
cmp r3, #2
bne _08291C9C
ldrb r1, [ip, #8]
ldr r2, [ip, #0x28]
str lr, [r2, r1, lsl #2]
ldrb r3, [ip, #8]
add r3, r3, #1
strb r3, [ip, #8]
ldr r1, [r6]
ldrb r2, [r1, #7]
ldrb r3, [r1, #8]
cmp r2, r3
bhs _08291C94
_08291C88:
mov r3, #3
str r3, [r1]
b _08291C9C
_08291C94:
mov r3, #-0x80000000
str r3, [r4]
_08291C9C:
mov r0, #1
bl handshake_wait
lsl r0, r0, #0x10
cmp r0, #0x10000
beq _08291DC8
mov r4, #0x128
add r4, r4, #0x4000000
mov r5, #0x5000
add r3, r5, #0xb
strh r3, [r4]
mov r0, #0
bl handshake_wait
lsl r0, r0, #0x10
cmp r0, #0x10000
beq _08291DC8
bl STWI_stop_timer_in_RAM
ldr r1, [r6]
ldr r0, [r1]
cmp r0, #3
bne _08291DB8
ldrb r3, [r1, #9]
cmp r3, #0xa5
cmpne r3, #0xa7
beq _08291D10
and r3, r3, #0xff
cmp r3, #0xb5
beq _08291D10
cmp r3, #0xb7
bne _08291D58
_08291D10:
mov r1, #0x120
add r1, r1, #0x4000000
mov ip, #0x128
add ip, ip, #0x4000000
ldr r0, [r6]
mov r3, #0
strb r3, [r0, #0x14]
mov r2, #-0x80000000
str r2, [r1]
add r3, r3, #0x5000
add r2, r3, #2
strh r2, [ip]
add r3, r3, #0x82
strh r3, [ip]
ldr r2, [r6]
mov r3, #5
str r3, [r2]
b _08291D88
_08291D58:
cmp r3, #0xee
bne _08291D78
add r3, r5, #3
strh r3, [r4]
mov r2, #4
str r2, [r1]
strh r0, [r1, #0x12]
b _08291D88
_08291D78:
add r3, r5, #3
strh r3, [r4]
mov r2, #4
str r2, [r1]
_08291D88:
ldr r2, [r6]
mov r3, #0
strb r3, [r2, #0x2c]
ldr r0, [r6]
ldr r2, [r0, #0x18]
cmp r2, r3
beq _08291DC8
ldrh r1, [r0, #0x12]
ldrb r0, [r0, #6]
bl sub_08292430
b _08291DC8
.align 2, 0
_08291DB4: .4byte gUnknown_3007608
_08291DB8:
add r3, r5, #3
strh r3, [r4]
add r2, r5, #0x83
strh r2, [r4]
_08291DC8:
ldmdb fp, {r4, r5, r6, fp, sp, lr}
bx lr
arm_func_end sio32intr_clock_master
arm_func_start sio32intr_clock_slave
sio32intr_clock_slave: @ 0x08291DD0
mov ip, sp
push {r4, r5, r6, fp, ip, lr, pc}
ldr r4, _0829217C
mov r0, #0x64
ldr r3, [r4]
mov r6, #0
strb r6, [r3, #0x10]
sub fp, ip, #4
bl STWI_set_timer_in_RAM
mov r0, r6
bl handshake_wait
lsl r0, r0, #0x10
cmp r0, #0x10000
mov r5, r4
beq _082921D4
mov r3, #0x128
add r3, r3, #0x4000000
mov r2, #0x5000
add r2, r2, #0xa
strh r2, [r3]
mov lr, #0x120
ldr r0, [r5]
add lr, lr, #0x4000000
ldr ip, [lr]
ldr r3, [r0]
cmp r3, #5
bne _08291F00
ldr r3, [r0, #0x28]
mov r4, #1
mov r0, #-0x67000000
str ip, [r3]
add r0, r0, #0x660000
ldr r2, [r5]
lsr r3, r0, #0x10
strb r4, [r2, #5]
cmp r3, ip, lsr #16
bne _0829204C
ldr r3, [r5]
lsr r2, ip, #8
strb r2, [r3, #4]
ldr r2, [r5]
strb ip, [r2, #6]
ldr r1, [r5]
ldrb r3, [r1, #4]
cmp r3, r6
bne _08291EE4
ldrb r2, [r1, #6]
sub r3, r2, #0x27
cmp r2, #0x36
cmpne r3, #2
bhi _08291EB8
add r3, r2, #0x80
strb r3, [r1, #9]
ldr r2, [r5]
ldrb r3, [r2, #9]
ldr r1, [r2, #0x24]
add r3, r3, r0
b _08291F68
_08291EB8:
ldr r2, [r1, #0x24]
ldr r3, _08292180
str r3, [r2]
ldr r2, [r5]
ldrb r3, [r2, #6]
sub r3, r3, #0x10
cmp r3, #0x2d
bhi _08291FA0
ldr r3, [r2, #0x24]
str r4, [r3, #4]
b _08291FAC
_08291EE4:
mov r3, #-0x80000000
str r3, [lr]
strb r4, [r1, #5]
ldr r2, [r5]
add r3, r3, #-0x7ffffffa
str r3, [r2]
b _0829205C
_08291F00:
ldr r3, [r0]
cmp r3, #6
bne _08292000
ldrb r1, [r0, #5]
ldr r2, [r0, #0x28]
str ip, [r2, r1, lsl #2]
ldrb r3, [r0, #5]
add r3, r3, #1
strb r3, [r0, #5]
ldr r1, [r5]
ldrb r2, [r1, #4]
ldrb r3, [r1, #5]
cmp r2, r3
bhs _08291FF4
ldrb r2, [r1, #6]
sub r3, r2, #0x28
cmp r2, #0x36
cmpne r3, #1
bhi _08291F78
add r3, r2, #0x80
strb r3, [r1, #9]
ldr r2, [r5]
ldrb r3, [r2, #9]
ldr r1, [r2, #0x24]
orr r3, r3, #0x99000000
orr r3, r3, #0x660000
_08291F68:
str r3, [r1]
ldr r2, [r5]
strb r6, [r2, #7]
b _08291FC4
_08291F78:
ldr r2, [r1, #0x24]
ldr r3, _08292180
str r3, [r2]
ldr r2, [r5]
ldrb r3, [r2, #6]
sub r3, r3, #0x10
cmp r3, #0x2d
ldrls r2, [r2, #0x24]
movls r3, #1
bls _08291FA8
_08291FA0:
ldr r2, [r2, #0x24]
mov r3, #2
_08291FA8:
str r3, [r2, #4]
_08291FAC:
ldr r2, [r5]
mov r3, #1
strb r3, [r2, #7]
ldr r1, [r5]
add r3, r3, #2
strh r3, [r1, #0x12]
_08291FC4:
ldr r0, [r5]
ldr r2, [r0, #0x24]
mov r3, #0x120
ldr r1, [r2]
add r3, r3, #0x4000000
str r1, [r3]
mov r2, #1
strb r2, [r0, #8]
ldr r1, [r5]
mov r3, #7
str r3, [r1]
b _0829205C
_08291FF4:
mov r3, #-0x80000000
str r3, [lr]
b _0829205C
_08292000:
ldr r3, [r0]
cmp r3, #7
bne _0829205C
cmp ip, #-0x80000000
bne _0829204C
ldrb r2, [r0, #7]
ldrb r3, [r0, #8]
cmp r2, r3
movlo r3, #8
strlo r3, [r0]
blo _0829205C
ldrb r1, [r0, #8]
ldr r3, [r0, #0x24]
ldr r2, [r3, r1, lsl #2]
str r2, [lr]
ldrb r3, [r0, #8]
add r3, r3, #1
strb r3, [r0, #8]
b _0829205C
_0829204C:
bl STWI_stop_timer_in_RAM
mov r0, #0x64
bl STWI_set_timer_in_RAM
b _082921D4
_0829205C:
mov r0, #1
bl handshake_wait
lsl r0, r0, #0x10
cmp r0, #0x10000
beq _082921D4
mov r6, r5
ldr r3, [r6]
ldr r2, [r3]
cmp r2, #8
bne _08292124
mov r4, #0x128
add r4, r4, #0x4000000
mov r3, #0x5000
add r3, r3, #2
strh r3, [r4]
bl STWI_stop_timer_in_RAM
arm_func_end sio32intr_clock_slave
arm_func_start sub_0829209C
sub_0829209C: @ 0x0829209C
ldr r0, [r6]
ldrh r3, [r0, #0x12]
cmp r3, #3
bne _082920D0
bl STWI_init_slave
ldr r3, [r6]
ldr r1, [r3, #0x1c]
cmp r1, #0
beq _082921D4
mov r0, #0x1ec
add r0, r0, #2
bl sub_08292434
b _082921D4
_082920D0:
mov r3, #0x120
add r3, r3, #0x4000000
mov r1, #0
str r1, [r3]
mov r2, #0x5000
strh r1, [r4]
add r2, r2, #3
strh r2, [r4]
mov r3, #1
strb r3, [r0, #0x14]
ldr r0, [r5]
ldr r2, [r0, #0x1c]
str r1, [r0]
cmp r2, r1
beq _082921D4
ldrb r3, [r0, #4]
ldrb r0, [r0, #6]
mov r1, r2
orr r0, r0, r3, lsl #8
bl sub_08292434
b _082921D4
_08292124:
mov r3, #0x208
add r3, r3, #0x4000000
mov r2, #0
strh r2, [r3]
mov r1, #0x100
add r2, r1, #0x4000002
ldrh r3, [r2]
tst r3, #0x80
beq _082921A8
ldrh r3, [r2]
tst r3, #3
bne _08292184
mov r2, #0xff00
add r1, r1, #0x4000000
ldrh r3, [r1]
add r2, r2, #0x9b
cmp r3, r2
bls _082921A8
_0829216C:
ldrh r3, [r1]
cmp r3, r2
bhi _0829216C
b _082921A8
.align 2, 0
_0829217C: .4byte gUnknown_3007608
_08292180: .4byte 0x996601EE
_08292184:
mov r2, #0xff00
add r1, r1, #0x4000000
ldrh r3, [r1]
add r2, r2, #0xfe
cmp r3, r2
bls _082921A8
_0829219C:
ldrh r3, [r1]
cmp r3, r2
bhi _0829219C
_082921A8:
mov r1, #0x128
add r1, r1, #0x4000000
mov r0, #0x208
add r0, r0, #0x4000000
mov r3, #0x5000
add r2, r3, #2
strh r2, [r1]
add r3, r3, #0x82
strh r3, [r1]
mov r2, #1
strh r2, [r0]
_082921D4:
ldmdb fp, {r4, r5, r6, fp, sp, lr}
bx lr
arm_func_end sub_0829209C
arm_func_start handshake_wait
handshake_wait: @ 0x082921DC
mov ip, sp
push {fp, ip, lr, pc}
mov r1, #0x128
add r1, r1, #0x4000000
lsl r0, r0, #0x10
ldr r2, _08292240
sub fp, ip, #4
lsr lr, r0, #0xe
ldr ip, [r2]
_08292200:
ldrb r3, [ip, #0x10]
and r0, r3, #0xff
cmp r0, #1
beq _0829222C
ldrh r3, [r1]
and r3, r3, #4
cmp r3, lr
bne _08292200
mov r0, #0
ldmdb fp, {fp, sp, lr}
bx lr
_0829222C:
ldr r2, [r2]
mov r3, #0
strb r3, [r2, #0x10]
ldmdb fp, {fp, sp, lr}
bx lr
.align 2, 0
_08292240: .4byte gUnknown_3007608
arm_func_end handshake_wait
arm_func_start STWI_set_timer_in_RAM
STWI_set_timer_in_RAM: @ 0x08292244
mov ip, sp
push {r4, r5, fp, ip, lr, pc}
mov r1, #0x208
add r1, r1, #0x4000000
mov r3, #0
sub fp, ip, #4
ldr ip, _082922FC
and lr, r0, #0xff
ldr r2, [ip]
cmp lr, #0x50
ldrb r0, [r2, #0xa]
mov r4, ip
mov r2, lr
strh r3, [r1]
lsl r0, r0, #2
add r3, r3, #0x100
add r1, r3, #0x4000000
add r3, r3, #0x4000002
add r5, r0, r3
beq _082922CC
bgt _082922A4
cmp lr, #0x32
beq _082922B8
b _08292318
_082922A4:
cmp r2, #0x64
beq _082922E4
cmp r2, #0x82
beq _08292300
b _08292318
_082922B8:
mvn r3, #0x334
strh r3, [r0, r1]
ldr r2, [r4]
mov r3, #1
b _08292314
_082922CC:
mov r3, #-0x52000000
asr r3, r3, #0x14
strh r3, [r0, r1]
ldr r2, [r4]
mov r3, #2
b _08292314
_082922E4:
mvn r3, #0x660
sub r3, r3, #9
strh r3, [r0, r1]
ldr r2, [r4]
mov r3, #3
b _08292314
.align 2, 0
_082922FC: .4byte gUnknown_3007608
_08292300:
mvn r3, #0x850
sub r3, r3, #2
strh r3, [r0, r1]
ldr r2, [r4]
mov r3, #4
_08292314:
str r3, [r2, #0xc]
_08292318:
mov ip, #0x200
add ip, ip, #0x4000002
mov r3, #0xc3
strh r3, [r5]
mov r1, #0x208
ldr r2, [r4]
add r1, r1, #0x4000000
ldrb r0, [r2, #0xa]
sub r3, r3, #0xbb
lsl r3, r3, r0
strh r3, [ip]
mov r2, #1
strh r2, [r1]
ldmdb fp, {r4, r5, fp, sp, lr}
bx lr
STWI_stop_timer_in_RAM:
mov ip, sp
push {fp, ip, lr, pc}
mov r1, #0x100
ldr lr, _082923A0
add r0, r1, #0x4000000
ldr r2, [lr]
sub fp, ip, #4
ldrb r3, [r2, #0xa]
mov ip, #0
str ip, [r2, #0xc]
lsl r3, r3, #2
strh ip, [r3, r0]
ldr r2, [lr]
ldrb r3, [r2, #0xa]
add r1, r1, #0x4000002
lsl r3, r3, #2
strh ip, [r3, r1]
ldmdb fp, {fp, sp, lr}
bx lr
.align 2, 0
_082923A0: .4byte gUnknown_3007608
arm_func_end STWI_set_timer_in_RAM
arm_func_start STWI_init_slave
STWI_init_slave: @ 0x082923A4
mov ip, sp
push {fp, ip, lr, pc}
ldr r0, _0829242C
ldr r2, [r0]
mov r3, #5
str r3, [r2]
mov r1, #0
strb r1, [r2, #0x14]
ldr r3, [r0]
strb r1, [r3, #4]
ldr r2, [r0]
strb r1, [r2, #5]
ldr r3, [r0]
strb r1, [r3, #6]
ldr r2, [r0]
strb r1, [r2, #7]
ldr r3, [r0]
strb r1, [r3, #8]
ldr r2, [r0]
strb r1, [r2, #9]
ldr r3, [r0]
str r1, [r3, #0xc]
sub fp, ip, #4
strb r1, [r3, #0x10]
mov r2, #0x128
ldr ip, [r0]
add r2, r2, #0x4000000
strh r1, [ip, #0x12]
mov r3, #0x5000
strb r1, [ip, #0x15]
add r3, r3, #0x82
strh r3, [r2]
ldmdb fp, {fp, sp, lr}
bx lr
.align 2, 0
_0829242C: .4byte gUnknown_3007608
arm_func_end STWI_init_slave
arm_func_start sub_08292430
sub_08292430: @ 0x08292430
bx r2
arm_func_end sub_08292430
arm_func_start sub_08292434
sub_08292434: @ 0x08292434
bx r1
arm_func_end sub_08292434
arm_func_start sub_08292438
sub_08292438: @ 0x08292438
bx r0
arm_func_end sub_08292438

6285
asm/librfu_rfu.s Normal file

File diff suppressed because it is too large Load Diff

1560
asm/librfu_stwi.s Normal file

File diff suppressed because it is too large Load Diff

8850
asm/libs.s.bak Normal file

File diff suppressed because it is too large Load Diff

5323
asm/m4a.s Normal file

File diff suppressed because it is too large Load Diff

135
asm/macros.inc Normal file
View File

@ -0,0 +1,135 @@
.include "asm/macros/asm.inc"
.include "asm/macros/function.inc"
.include "asm/macros/movement.inc"
.include "asm/macros/window.inc"
.include "asm/macros/pokemon_data.inc"
.include "asm/macros/ec.inc"
.include "asm/macros/map.inc"
.include "asm/macros/field_effect_script.inc"
.macro region_map_entry x, y, width, height, name
.byte \x
.byte \y
.byte \width
.byte \height
.4byte gMapName_\name
.endm
.macro obj_tiles address, uncompressed_size, tag = 0
.4byte \address
.2byte \uncompressed_size
.2byte \tag
.endm
.macro null_obj_tiles
obj_tiles 0, 0, 0
.endm
.macro obj_pal address, tag
.4byte \address
.2byte \tag
.2byte 0@ padding
.endm
.macro null_obj_pal
obj_pal 0, 0
.endm
.macro paired_pals tag, address
.2byte \tag
.2byte 0 @ padding
.4byte \address
.endm
@ For object animation frames.
.macro obj_frame_tiles address, uncompressed_size
.4byte \address
.2byte \uncompressed_size
.2byte 0 @ padding
.endm
.macro spr_template tile_tag, pal_tag, oam, anims, images, affine_anims, callback
.2byte \tile_tag
.2byte \pal_tag
.4byte \oam
.4byte \anims
.4byte \images
.4byte \affine_anims
.4byte \callback
.endm
@ Berry trees have a table defining the palette slot used for each of their 5
@ stages. However, the first 2 stages always use the same slots regardless of
@ the type of tree and the slots of the last 3 stages always equal each other.
.macro berry_tree_palette_slot_table slot
.byte 3, 4, \slot, \slot, \slot
.endm
.macro subsprite x, y, priority, tile_num_offset, size
.byte \x
.byte \y
.2byte ((\priority) << 14) | ((\tile_num_offset) << 4) | SPRITE_SIZE_\size
.endm
.macro obj_image_anim_frame pic_id, duration, flags = 0
.2byte \pic_id
.byte (\flags) | (\duration)
.byte 0 @ padding
.endm
.macro obj_image_anim_loop count
.2byte 0xfffd
.byte \count
.byte 0 @ padding
.endm
.macro obj_image_anim_jump target_index
.2byte 0xfffe
.byte \target_index
.byte 0 @ padding
.endm
.macro obj_image_anim_end
.2byte 0xffff
.2byte 0 @ padding
.endm
.macro obj_rot_scal_anim_frame delta_x_scale, delta_y_scale, delta_angle, duration
.2byte \delta_x_scale
.2byte \delta_y_scale
.byte \delta_angle
.byte \duration
.2byte 0 @ padding
.endm
.macro obj_rot_scal_anim_loop count
.2byte 0x7ffd
.2byte \count
.4byte 0 @ padding
.endm
.macro obj_rot_scal_anim_jump target_index
.2byte 0x7ffe
.2byte \target_index
.4byte 0 @ padding
.endm
.macro obj_rot_scal_anim_end unknown=0
.2byte 0x7fff
.2byte \unknown
.fill 4 @ padding
.endm
.macro door_anim_frame unknown, offset
.byte \unknown
.byte 0 @ padding
.2byte \offset
.endm
.macro door_anim_gfx metatile_num, unknown, unknown2, tile_addr, palette_addr
.2byte \metatile_num
.byte \unknown
.byte \unknown2
.4byte \tile_addr
.4byte \palette_addr
.endm

12
asm/macros/asm.inc Normal file
View File

@ -0,0 +1,12 @@
.macro inc x
.set \x, \x + 1
.endm
.macro enum_start x=0
.set __enum__, \x
.endm
.macro enum constant
.equiv \constant, __enum__
inc __enum__
.endm

View File

@ -0,0 +1,619 @@
.macro if_random_less_than param0:req, param1:req
.byte 0x0
.byte \param0
.4byte \param1
.endm
.macro if_random_greater_than param0:req, param1:req
.byte 0x1
.byte \param0
.4byte \param1
.endm
.macro if_random_equal param0:req, param1:req
.byte 0x2
.byte \param0
.4byte \param1
.endm
.macro if_random_not_equal param0:req, param1:req
.byte 0x3
.byte \param0
.4byte \param1
.endm
.macro score param0:req
.byte 0x4
.byte \param0
.endm
.macro if_hp_less_than battler:req, param1:req, param2:req
.byte 0x5
.byte \battler
.byte \param1
.4byte \param2
.endm
.macro if_hp_more_than battler:req, param1:req, param2:req
.byte 0x6
.byte \battler
.byte \param1
.4byte \param2
.endm
.macro if_hp_equal battler:req, param1:req, param2:req
.byte 0x7
.byte \battler
.byte \param1
.4byte \param2
.endm
.macro if_hp_not_equal battler:req, param1:req, param2:req
.byte 0x8
.byte \battler
.byte \param1
.4byte \param2
.endm
.macro if_status battler:req, status1:req, param2:req
.byte 0x9
.byte \battler
.4byte \status1
.4byte \param2
.endm
.macro if_not_status battler:req, status1:req, param2:req
.byte 0xa
.byte \battler
.4byte \status1
.4byte \param2
.endm
.macro if_status2 battler:req, status2:req, param2:req
.byte 0xb
.byte \battler
.4byte \status2
.4byte \param2
.endm
.macro if_not_status2 battler:req, status2:req, param2:req
.byte 0xc
.byte \battler
.4byte \status2
.4byte \param2
.endm
.macro if_status3 battler:req, status3:req, param2:req
.byte 0xd
.byte \battler
.4byte \status3
.4byte \param2
.endm
.macro if_not_status3 battler:req, status3:req, param2:req
.byte 0xe
.byte \battler
.4byte \status3
.4byte \param2
.endm
.macro if_side_affecting battler:req, sidestatus:req, param2:req
.byte 0xf
.byte \battler
.4byte \sidestatus
.4byte \param2
.endm
.macro if_not_side_affecting battler:req, sidestatus:req, param2:req
.byte 0x10
.byte \battler
.4byte \sidestatus
.4byte \param2
.endm
.macro if_less_than param0:req, param1:req
.byte 0x11
.byte \param0
.4byte \param1
.endm
.macro if_more_than param0:req, param1:req
.byte 0x12
.byte \param0
.4byte \param1
.endm
.macro if_equal param0:req, param1:req
.byte 0x13
.byte \param0
.4byte \param1
.endm
.macro if_not_equal param0:req, param1:req
.byte 0x14
.byte \param0
.4byte \param1
.endm
.macro if_less_than_ptr param0:req, param1:req
.byte 0x15
.4byte \param0
.4byte \param1
.endm
.macro if_more_than_ptr param0:req, param1:req
.byte 0x16
.4byte \param0
.4byte \param1
.endm
.macro if_equal_ptr param0:req, param1:req
.byte 0x17
.4byte \param0
.4byte \param1
.endm
.macro if_not_equal_ptr param0:req, param1:req
.byte 0x18
.4byte \param0
.4byte \param1
.endm
.macro if_move param0:req, param1:req
.byte 0x19
.2byte \param0
.4byte \param1
.endm
.macro if_not_move param0:req, param1:req
.byte 0x1a
.2byte \param0
.4byte \param1
.endm
.macro if_in_bytes param0:req, param1:req
.byte 0x1b
.4byte \param0
.4byte \param1
.endm
.macro if_not_in_bytes param0:req, param1:req
.byte 0x1c
.4byte \param0
.4byte \param1
.endm
.macro if_in_hwords param0:req, param1:req
.byte 0x1d
.4byte \param0
.4byte \param1
.endm
.macro if_not_in_hwords param0:req, param1:req
.byte 0x1e
.4byte \param0
.4byte \param1
.endm
.macro if_user_has_attacking_move param0:req
.byte 0x1f
.4byte \param0
.endm
.macro if_user_has_no_attacking_moves param0:req
.byte 0x20
.4byte \param0
.endm
.macro get_turn_count
.byte 0x21
.endm
.macro get_type param0:req
.byte 0x22
.byte \param0
.endm
.macro get_considered_move_power
.byte 0x23
.endm
.macro get_how_powerful_move_is
.byte 0x24
.endm
.macro get_last_used_bank_move battler:req
.byte 0x25
.byte \battler
.endm
.macro if_equal_ param0:req, param1:req
.byte 0x26
.byte \param0
.4byte \param1
.endm
.macro if_not_equal_ param0:req, param1:req
.byte 0x27
.byte \param0
.4byte \param1
.endm
.macro if_user_goes param0:req, param1:req
.byte 0x28
.byte \param0
.4byte \param1
.endm
.macro if_user_doesnt_go param0:req, param1:req
.byte 0x29
.byte \param0
.4byte \param1
.endm
.macro nullsub_2A
.byte 0x2a
.endm
.macro nullsub_2B
.byte 0x2b
.endm
.macro count_usable_party_mons battler:req
.byte 0x2c
.byte \battler
.endm
.macro get_considered_move
.byte 0x2d
.endm
.macro get_considered_move_effect
.byte 0x2e
.endm
.macro get_ability battler:req
.byte 0x2f
.byte \battler
.endm
.macro get_highest_type_effectiveness
.byte 0x30
.endm
.macro if_type_effectiveness param0:req, param1:req
.byte 0x31
.byte \param0
.4byte \param1
.endm
.macro nullsub_32
.byte 0x32
.endm
.macro nullsub_33
.byte 0x33
.endm
.macro if_status_in_party battler:req, status1:req, param2:req
.byte 0x34
.byte \battler
.4byte \status1
.4byte \param2
.endm
.macro if_status_not_in_party battler:req, status1:req, param2:req
.byte 0x35
.byte \battler
.4byte \status1
.4byte \param2
.endm
.macro get_weather
.byte 0x36
.endm
.macro if_effect param0:req, param1:req
.byte 0x37
.byte \param0
.4byte \param1
.endm
.macro if_not_effect param0:req, param1:req
.byte 0x38
.byte \param0
.4byte \param1
.endm
.macro if_stat_level_less_than battler:req, stat:req, param2:req, param3:req
.byte 0x39
.byte \battler
.byte \stat
.byte \param2
.4byte \param3
.endm
.macro if_stat_level_more_than battler:req, stat:req, param2:req, param3:req
.byte 0x3a
.byte \battler
.byte \stat
.byte \param2
.4byte \param3
.endm
.macro if_stat_level_equal battler:req, stat:req, param2:req, param3:req
.byte 0x3b
.byte \battler
.byte \stat
.byte \param2
.4byte \param3
.endm
.macro if_stat_level_not_equal battler:req, stat:req, param2:req, param3:req
.byte 0x3c
.byte \battler
.byte \stat
.byte \param2
.4byte \param3
.endm
.macro if_can_faint param0:req
.byte 0x3d
.4byte \param0
.endm
.macro if_cant_faint param0:req
.byte 0x3e
.4byte \param0
.endm
.macro if_has_move battler:req, param1:req, param2:req
.byte 0x3f
.byte \battler
.2byte \param1
.4byte \param2
.endm
.macro if_doesnt_have_move battler:req, param1:req, param2:req
.byte 0x40
.byte \battler
.2byte \param1
.4byte \param2
.endm
.macro if_has_move_with_effect battler:req, param1:req, param2:req
.byte 0x41
.byte \battler
.byte \param1
.4byte \param2
.endm
.macro if_doesnt_have_move_with_effect battler:req, param1:req, param2:req
.byte 0x42
.byte \battler
.byte \param1
.4byte \param2
.endm
.macro if_any_move_disabled_or_encored battler:req, param1:req, param2:req
.byte 0x43
.byte \battler
.byte \param1
.4byte \param2
.endm
.macro if_curr_move_disabled_or_encored param0:req, param1:req
.byte 0x44
.byte \param0
.4byte \param1
.endm
.macro flee
.byte 0x45
.endm
.macro if_random_safari_flee param0:req
.byte 0x46
.4byte \param0
.endm
.macro watch
.byte 0x47
.endm
.macro get_hold_effect battler:req
.byte 0x48
.byte \battler
.endm
.macro get_gender battler:req
.byte 0x49
.byte \battler
.endm
.macro is_first_turn_for battler:req
.byte 0x4a
.byte \battler
.endm
.macro get_stockpile_count battler:req
.byte 0x4b
.byte \battler
.endm
.macro is_double_battle
.byte 0x4c
.endm
.macro get_used_held_item battler:req
.byte 0x4d
.byte \battler
.endm
.macro get_move_type_from_result
.byte 0x4e
.endm
.macro get_move_power_from_result
.byte 0x4f
.endm
.macro get_move_effect_from_result
.byte 0x50
.endm
.macro get_protect_count battler:req
.byte 0x51
.byte \battler
.endm
.macro nullsub_52
.byte 0x52
.endm
.macro nullsub_53
.byte 0x53
.endm
.macro nullsub_54
.byte 0x54
.endm
.macro nullsub_55
.byte 0x55
.endm
.macro nullsub_56
.byte 0x56
.endm
.macro nullsub_57
.byte 0x57
.endm
.macro call param0:req
.byte 0x58
.4byte \param0
.endm
.macro goto param0:req
.byte 0x59
.4byte \param0
.endm
.macro end
.byte 0x5a
.endm
.macro if_level_cond param0:req, param1:req
.byte 0x5b
.byte \param0
.4byte \param1
.endm
.macro if_target_taunted param0:req
.byte 0x5c
.4byte \param0
.endm
.macro if_target_not_taunted param0:req
.byte 0x5d
.4byte \param0
.endm
.macro if_target_is_ally param0:req
.byte 0x5e
.4byte \param0
.endm
.macro is_of_type battler:req, type:req
.byte 0x5f
.byte \battler
.byte \type
.endm
.macro check_ability battler:req, ability:req
.byte 0x60
.byte \battler
.byte \ability
.endm
.macro if_flash_fired battler:req, param1:req
.byte 0x61
.byte \battler
.4byte \param1
.endm
.macro if_holds_item battler:req, param1:req, param2:req
.byte 0x62
.byte \battler
.2byte \param1
.4byte \param2
.endm
@ useful script macros
.macro get_curr_move_type
get_type AI_TYPE_MOVE
.endm
.macro get_user_type1
get_type AI_TYPE1_USER
.endm
.macro get_user_type2
get_type AI_TYPE2_USER
.endm
.macro get_target_type1
get_type AI_TYPE1_TARGET
.endm
.macro get_target_type2
get_type AI_TYPE2_TARGET
.endm
.macro if_ability battler:req, ability:req, ptr:req
check_ability \battler, \ability
if_equal 1, \ptr
.endm
.macro if_no_ability battler:req, ability:req, ptr:req
check_ability \battler, \ability
if_equal 0, \ptr
.endm
.macro if_type battler:req, type:req, ptr:req
is_of_type \battler, \type
if_equal 1, \ptr
.endm
.macro if_no_type battler:req, type:req, ptr:req
is_of_type \battler, \type
if_equal 0, \ptr
.endm
.macro if_target_faster ptr:req
if_user_goes 1, \ptr
.endm
.macro if_user_faster ptr:req
if_user_goes 0, \ptr
.endm
.macro if_double_battle ptr:req
is_double_battle
if_equal 1, \ptr
.endm
.macro if_not_double_battle ptr:req
is_double_battle
if_equal 0, \ptr
.endm
.macro if_any_move_disabled battler:req, ptr:req
if_any_move_disabled_or_encored \battler, 0, \ptr
.endm
.macro if_any_move_encored battler:req, ptr:req
if_any_move_disabled_or_encored \battler, 1, \ptr
.endm

View File

@ -0,0 +1,285 @@
@ commands
.macro loadspritegfx tag:req
.byte 0x0
.2byte \tag
.endm
.macro unloadspritegfx tag:req
.byte 0x1
.2byte \tag
.endm
.macro createsprite template:req, anim_battler:req, subpriority_offset:req, argv:vararg
.byte 0x02
.4byte \template
.if \anim_battler == ANIM_TARGET
.byte 0x80 | (\subpriority_offset & 0x7F)
.else
.byte (\subpriority_offset & 0x7F)
.endif
.byte (.Lsprite_\@_2 - .Lsprite_\@_1) / 2
.Lsprite_\@_1:
.2byte \argv
.Lsprite_\@_2:
.endm
.macro createvisualtask addr:req, priority:req, argv:vararg
.byte 0x03
.4byte \addr
.byte \priority
.byte (.Lcreatetask_\@_2 - .Lcreatetask_\@_1) / 2
.Lcreatetask_\@_1:
.2byte \argv
.Lcreatetask_\@_2:
.endm
.macro delay param0:req
.byte 0x4
.byte \param0
.endm
.macro waitforvisualfinish
.byte 0x5
.endm
.macro hang1
.byte 0x6
.endm
.macro hang2
.byte 0x7
.endm
.macro end
.byte 0x8
.endm
.macro playse se:req
.byte 0x9
.2byte \se
.endm
.macro monbg battler:req
.byte 0xa
.byte \battler
.endm
.macro clearmonbg battler:req
.byte 0xb
.byte \battler
.endm
.macro setalpha eva:req, evb:req
.byte 0x0C
.2byte ((\evb) << 8) | (\eva)
.endm
.macro blendoff
.byte 0xd
.endm
.macro call param0:req
.byte 0xe
.4byte \param0
.endm
.macro return
.byte 0xf
.endm
.macro setarg param0:req, param1:req
.byte 0x10
.byte \param0
.2byte \param1
.endm
.macro choosetwoturnanim param0:req, param1:req
.byte 0x11
.4byte \param0
.4byte \param1
.endm
.macro jumpifmoveturn param0:req, ptr:req
.byte 0x12
.byte \param0
.4byte \ptr
.endm
.macro goto ptr:req
.byte 0x13
.4byte \ptr
.endm
.macro fadetobg bg:req
.byte 0x14
.byte \bg
.endm
.macro restorebg
.byte 0x15
.endm
.macro waitbgfadeout
.byte 0x16
.endm
.macro waitbgfadein
.byte 0x17
.endm
.macro changebg bg:req
.byte 0x18
.byte \bg
.endm
.macro playsewithpan se:req, pan:req
.byte 0x19
.2byte \se
.byte \pan
.endm
.macro setpan pan:req
.byte 0x1a
.byte \pan
.endm
.macro panse_1B se:req, param1:req, param2:req, param3:req, param4:req
.byte 0x1b
.2byte \se
.byte \param1
.byte \param2
.byte \param3
.byte \param4
.endm
.macro loopsewithpan se:req, pan:req, wait:req, times:req
.byte 0x1c
.2byte \se
.byte \pan
.byte \wait
.byte \times
.endm
.macro waitplaysewithpan se:req, pan:req, wait:req
.byte 0x1d
.2byte \se
.byte \pan
.byte \wait
.endm
.macro setbldcnt param0:req
.byte 0x1e
.2byte \param0
.endm
.macro createsoundtask addr:req, argv:vararg
.byte 0x1F
.4byte \addr
.byte (.Lcreatetask_1F_\@_2 - .Lcreatetask_1F_\@_1) / 2
.Lcreatetask_1F_\@_1:
.2byte \argv
.Lcreatetask_1F_\@_2:
.endm
.macro waitsound
.byte 0x20
.endm
.macro jumpargeq param0:req, param1:req, ptr:req
.byte 0x21
.byte \param0
.2byte \param1
.4byte \ptr
.endm
.macro monbg_22 battler:req
.byte 0x22
.byte \battler
.endm
.macro clearmonbg_23 battler:req
.byte 0x23
.byte \battler
.endm
.macro jumpifcontest ptr:req
.byte 0x24
.4byte \ptr
.endm
.macro fadetobgfromset param0:req, param1:req, param2:req
.byte 0x25
.byte \param0
.byte \param1
.byte \param2
.endm
.macro panse_26 se:req, param1:req, param2:req, param3:req, param4:req
.byte 0x26
.2byte \se
.byte \param1
.byte \param2
.byte \param3
.byte \param4
.endm
.macro panse_27 se:req, param1:req, param2:req, param3:req, param4:req
.byte 0x27
.2byte \se
.byte \param1
.byte \param2
.byte \param3
.byte \param4
.endm
.macro monbgprio_28 battler:req
.byte 0x28
.byte \battler
.endm
.macro monbgprio_29
.byte 0x29
.endm
.macro monbgprio_2A battler:req
.byte 0x2a
.byte \battler
.endm
.macro invisible battler:req
.byte 0x2b
.byte \battler
.endm
.macro visible battler:req
.byte 0x2c
.byte \battler
.endm
.macro doublebattle_2D battler:req
.byte 0x2d
.byte \battler
.endm
.macro doublebattle_2E battler:req
.byte 0x2e
.byte \battler
.endm
.macro stopsound
.byte 0x2f
.endm
@ useful macros
.macro jumpreteq value:req, ptr:req
jumpargeq ARG_RET_ID, \value, \ptr
.endm
.macro jumprettrue ptr:req
jumpreteq TRUE, \ptr
.endm
.macro jumpretfalse ptr:req
jumpreteq FALSE, \ptr
.endm

1433
asm/macros/battle_script.inc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,506 @@
@ Add a positive/negative value to the score of the move being evaluated.
.macro score score
.byte 0x00
.byte \score
.endm
@ turn (AKA "Appeal No.")
.macro get_turn
.byte 0x01
.endm
.macro if_turn_less_than param, addr
.byte 0x02
.byte \param
.4byte \addr
.endm
.macro if_turn_more_than param, addr
.byte 0x03
.byte \param
.4byte \addr
.endm
.macro if_turn_eq param, addr
.byte 0x04
.byte \param
.4byte \addr
.endm
.macro if_turn_not_eq param, addr
.byte 0x05
.byte \param
.4byte \addr
.endm
@ audience excitement
.macro get_excitement
.byte 0x06
.endm
.macro if_excitement_less_than param, addr
.byte 0x07
.byte \param
.4byte \addr
.endm
.macro if_excitement_more_than param, addr
.byte 0x08
.byte \param
.4byte \addr
.endm
.macro if_excitement_eq param, addr
.byte 0x09
.byte \param
.4byte \addr
.endm
.macro if_excitement_not_eq param, addr
.byte 0x0A
.byte \param
.4byte \addr
.endm
@ the order that the user goes in the current turn
.macro get_user_order
.byte 0x0B
.endm
.macro if_user_order_less_than param addr
.byte 0x0C
.byte \param
.4byte \addr
.endm
.macro if_user_order_more_than param addr
.byte 0x0D
.byte \param
.4byte \addr
.endm
.macro if_user_order_eq param addr
.byte 0x0E
.byte \param
.4byte \addr
.endm
.macro if_user_order_not_eq param addr
.byte 0x0F
.byte \param
.4byte \addr
.endm
@ user condition
.macro get_user_condition
.byte 0x10
.endm
.macro if_user_condition_less_than param, addr
.byte 0x11
.byte \param
.4byte \addr
.endm
.macro if_user_condition_more_than param, addr
.byte 0x12
.byte \param
.4byte \addr
.endm
.macro if_user_condition_eq param, addr
.byte 0x13
.byte \param
.4byte \addr
.endm
.macro if_user_condition_not_eq param, addr
.byte 0x14
.byte \param
.4byte \addr
.endm
@ 15
@ 16
@ 17
@ 18
@ 19
@ 1A
@ 1B
@ 1C
@ 1D
@ 1E
@ contest type
.macro get_contest_type
.byte 0x1F
.endm
.macro if_contest_type_eq param, addr
.byte 0x20
.byte \param
.4byte \addr
.endm
.macro if_contest_type_not_eq param, addr
.byte 0x21
.byte \param
.4byte \addr
.endm
@ move excitement (change in excitement due to move)
.macro get_move_excitement
.byte 0x22
.endm
.macro if_move_excitement_less_than param, addr
.byte 0x23
.byte \param
.4byte \addr
.endm
.macro if_move_excitement_more_than param, addr
.byte 0x24
.byte \param
.4byte \addr
.endm
.macro if_move_excitement_eq param, addr
.byte 0x25
.byte \param
.4byte \addr
.endm
.macro if_move_excitement_not_eq param, addr
.byte 0x26
.byte \param
.4byte \addr
.endm
@ move effect
.macro get_effect
.byte 0x27
.endm
.macro if_effect_eq param, addr
.byte 0x28
.byte \param
.4byte \addr
.endm
.macro if_effect_not_eq param, addr
.byte 0x29
.byte \param
.4byte \addr
.endm
@ move effect type
.macro get_effect_type
.byte 0x2A
.endm
.macro if_effect_type_eq param, addr
.byte 0x2B
.byte \param
.4byte \addr
.endm
.macro if_effect_type_not_eq param, addr
.byte 0x2C
.byte \param
.4byte \addr
.endm
@ whether the current move is the most appealing in the user's moveset
.macro check_most_appealing_move
.byte 0x2D
.endm
.macro if_most_appealing_move addr
.byte 0x2E
.4byte \addr
.endm
@ 2F
@ 30
@ 31
@ 32
@ 33
@ 34
@ 35
@ 36
@ 37
@ 38
@ 39
@ 3A
@ number of times current move has been used
.macro get_move_used_count
.byte 0x3B
.endm
.macro if_move_used_count_less_than param, addr
.byte 0x3C
.byte \param
.4byte \addr
.endm
.macro if_move_used_count_more_than param, addr
.byte 0x3D
.byte \param
.4byte \addr
.endm
.macro if_move_used_count_eq param, addr
.byte 0x3E
.byte \param
.4byte \addr
.endm
.macro if_move_used_count_not_eq param, addr
.byte 0x3F
.byte \param
.4byte \addr
.endm
@ whether the current move is a combo starter (with another move in the moveset)
.macro check_combo_starter
.byte 0x40
.endm
.macro if_combo_starter addr
.byte 0x41
.4byte \addr
.endm
.macro if_not_combo_starter addr
.byte 0x42
.4byte \addr
.endm
@ whether the current move is a combo finisher (with another move in the moveset)
.macro check_combo_finisher
.byte 0x43
.endm
.macro if_combo_finisher addr
.byte 0x44
.4byte \addr
.endm
.macro if_not_combo_finisher addr
.byte 0x45
.4byte \addr
.endm
@ whether the current move would finish a combo
.macro check_would_finish_combo
.byte 0x46
.endm
.macro if_would_finish_combo addr
.byte 0x47
.4byte \addr
.endm
.macro if_would_not_finish_combo addr
.byte 0x48
.4byte \addr
.endm
@ condition of mon (indexed by order)
.macro get_condition mon
.byte 0x49
.byte \mon
.endm
.macro if_condition_less_than mon, value, addr
.byte 0x4A
.byte \mon
.byte \value
.4byte \addr
.endm
.macro if_condition_more_than mon, value, addr
.byte 0x4B
.byte \mon
.byte \value
.4byte \addr
.endm
.macro if_condition_eq mon, value, addr
.byte 0x4C
.byte \mon
.byte \value
.4byte \addr
.endm
.macro if_condition_not_eq mon, value, addr
.byte 0x4D
.byte \mon
.byte \value
.4byte \addr
.endm
@ whether the mon used a combo starter move
@ Even though this value is always 1 or 0 (i.e. TRUE/FALSE),
@ there are less-than and greater-than comparison operations for some reason.
.macro get_used_combo_starter mon
.byte 0x4E
.byte \mon
.endm
.macro if_used_combo_starter_less_than mon, value, addr
.byte 0x4F
.byte \mon
.byte \value
.4byte \addr
.endm
.macro if_used_combo_starter_more_than mon, value, addr
.byte 0x50
.byte \mon
.byte \value
.4byte \addr
.endm
.macro if_used_combo_starter_eq mon, value, addr
.byte 0x51
.byte \mon
.byte \value
.4byte \addr
.endm
.macro if_used_combo_starter_not_eq mon, value, addr
.byte 0x52
.byte \mon
.byte \value
.4byte \addr
.endm
@ whether the mon can make an appeal
.macro check_can_participate mon
.byte 0x53
.byte \mon
.endm
.macro if_can_participate mon, addr
.byte 0x54
.byte \mon
.4byte \addr
.endm
.macro if_cannot_participate mon, addr
.byte 0x55
.byte \mon
.4byte \addr
.endm
@ 56
@ 57
.macro contest_58 param addr
.byte 0x58
.byte \param
.4byte \addr
.endm
@ 59
@ 5A
@ 5B
@ 5C
@ 5D
@ 5E
@ 5F
@ 60
@ 61
@ 62
@ 63
@ 64
@ 65
@ 66
@ 67
@ 68
@ 69
@ 6A
@ 6B
@ 6C
@ 6D
@ 6E
@ 6F
@ 70
@ 71
@ 72
@ 73
@ 74
@ 75
@ 76
@ 77
@ 78
@ 79
@ 7A
@ 7B
@ 7C
.macro if_random param addr
.byte 0x7D
.byte \param
.4byte \addr
.endm
@ 7E
.macro jump addr
.byte 0x7F
.4byte \addr
.endm
.macro call addr
.byte 0x80
.4byte \addr
.endm
.macro end
.byte 0x81
.endm
.macro check_user_has_exciting_move
.byte 0x82
.endm
.macro if_user_has_exciting_move addr
.byte 0x83
.4byte \addr
.endm
.macro if_user_doesnt_have_exciting_move addr
.byte 0x84
.4byte \addr
.endm
@ 85
@ 86
.macro if_effect_in_user_moveset param addr
.byte 0x87
.2byte \param
.4byte \addr
.endm

19
asm/macros/ec.inc Normal file
View File

@ -0,0 +1,19 @@
.macro ec_word word
.2byte EC_WORD_\word
.endm
.macro ec_move1 name
.2byte (EC_GROUP_MOVE_1 << 9) | MOVE_\name
.endm
.macro ec_move2 name
.2byte (EC_GROUP_MOVE_2 << 9) | MOVE_\name
.endm
.macro ec_pokemon1 name
.2byte (EC_GROUP_POKEMON << 9) | SPECIES_\name
.endm
.macro ec_pokemon2 name
.2byte (EC_GROUP_POKEMON2 << 9) | SPECIES_\name
.endm

1668
asm/macros/event.inc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
.macro field_eff_loadtiles address
.byte 0
.4byte \address
.endm
.macro field_eff_loadfadedpal address
.byte 1
.4byte \address
.endm
.macro field_eff_loadpal address
.byte 2
.4byte \address
.endm
.macro field_eff_callnative address
.byte 3
.4byte \address
.endm
.macro field_eff_end
.byte 4
.endm
.macro field_eff_loadgfx_callnative tiles_address, palette_address, function_address
.byte 5
.4byte \tiles_address
.4byte \palette_address
.4byte \function_address
.endm
.macro field_eff_loadtiles_callnative tiles_address, function_address
.byte 6
.4byte \tiles_address
.4byte \function_address
.endm
.macro field_eff_loadfadedpal_callnative palette_address, function_address
.byte 7
.4byte \palette_address
.4byte \function_address
.endm

29
asm/macros/function.inc Normal file
View File

@ -0,0 +1,29 @@
.macro arm_func_start name
.align 2, 0
.global \name
.arm
.type \name, %function
.endm
.macro arm_func_end name
.size \name, .-\name
.endm
.macro thumb_func_start name
.align 2, 0
.global \name
.thumb
.thumb_func
.type \name, %function
.endm
.macro non_word_aligned_thumb_func_start name
.global \name
.thumb
.thumb_func
.type \name, %function
.endm
.macro thumb_func_end name
.size \name, .-\name
.endm

13
asm/macros/m4a.inc Normal file
View File

@ -0,0 +1,13 @@
.macro song label, music_player, unknown
.4byte \label
.2byte \music_player
.2byte \unknown
.endm
.macro music_player info_struct, track_struct, unknown_1, unknown_2
.4byte \info_struct
.4byte \track_struct
.byte \unknown_1
.space 1
.2byte \unknown_2
.endm

107
asm/macros/map.inc Normal file
View File

@ -0,0 +1,107 @@
.macro map map_id
.byte \map_id >> 8 @ map group
.byte \map_id & 0xFF @ map num
.endm
.macro map_script type, address
.byte \type
.4byte \address
.endm
.macro map_script_2 word1, word2, address
.2byte \word1
.2byte \word2
.4byte \address
.endm
.macro object_event index:req, gfx:req, replacement:req, x:req, y:req, elevation:req, movement_type:req, x_radius:req, y_radius:req, trainer_type:req, sight_radius_tree_etc:req, script:req, event_flag:req
.byte \index, \gfx, \replacement, 0
.2byte \x
.2byte \y
.byte \elevation, \movement_type, ((\y_radius << 4) | \x_radius), 0
.2byte \trainer_type, \sight_radius_tree_etc
.4byte \script
.2byte \event_flag
.2byte 0
inc _num_npcs
.endm
.macro warp_def x, y, byte, warp, map_id
.2byte \x, \y
.byte \byte, \warp
.byte \map_id & 0xFF @ map num
.byte \map_id >> 8 @ map group
inc _num_warps
.endm
.macro coord_event x, y, elevation, trigger, index, script
.2byte \x, \y
.byte \elevation, 0
.2byte \trigger, \index, 0
.4byte \script
inc _num_traps
.endm
.macro coord_weather_event x, y, elevation, weather
.2byte \x, \y
.byte \elevation, 0
.2byte \weather
.2byte 0, 0
.4byte 0
inc _num_traps
.endm
.macro bg_event x, y, elevation, kind, arg6, arg7, arg8
.2byte \x, \y
.byte \elevation, \kind
.2byte 0
.if \kind < 5
.4byte \arg6
.else
.2byte \arg6
.byte \arg7, \arg8
.endif
inc _num_signs
.endm
.macro bg_hidden_item_event x, y, height, item, flag
bg_event \x, \y, \height, 7, \item, ((\flag) - FLAG_HIDDEN_ITEMS_START), 0
.endm
.macro bg_secret_base_event x, y, height, secret_base_id
bg_event \x, \y, \height, 8, \secret_base_id, 0, 0
.endm
.macro map_events npcs, warps, traps, signs
.byte _num_npcs, _num_warps, _num_traps, _num_signs
.4byte \npcs, \warps, \traps, \signs
reset_map_events
.endm
.macro reset_map_events
.set _num_npcs, 0
.set _num_warps, 0
.set _num_traps, 0
.set _num_signs, 0
.endm
reset_map_events
.equiv connection_down, 1
.equiv connection_up, 2
.equiv connection_left, 3
.equiv connection_right, 4
.equiv connection_dive, 5
.equiv connection_emerge, 6
.macro connection direction, offset, map
.4byte connection_\direction
.4byte \offset
map \map
.space 2
.endm
.macro map_header_flags allow_bike:req, allow_escape_rope:req, allow_run:req, show_map_name:req
.byte ((\show_map_name & 1) << 3) | ((\allow_run & 1) << 2) | ((\allow_escape_rope & 1) << 1) | \allow_bike
.endm

131
asm/macros/movement.inc Normal file
View File

@ -0,0 +1,131 @@
.macro create_movement_action name
enum _\name
.macro \name
.byte _\name
.endm
.endm
enum_start
create_movement_action face_down
create_movement_action face_up
create_movement_action face_left
create_movement_action face_right
create_movement_action walk_slow_down
create_movement_action walk_slow_up
create_movement_action walk_slow_left
create_movement_action walk_slow_right
create_movement_action walk_down
create_movement_action walk_up
create_movement_action walk_left
create_movement_action walk_right
create_movement_action jump_2_down
create_movement_action jump_2_up
create_movement_action jump_2_left
create_movement_action jump_2_right
create_movement_action delay_1
create_movement_action delay_2
create_movement_action delay_4
create_movement_action delay_8
create_movement_action delay_16
create_movement_action walk_fast_down
create_movement_action walk_fast_up
create_movement_action walk_fast_left
create_movement_action walk_fast_right
create_movement_action walk_in_place_slow_down
create_movement_action walk_in_place_slow_up
create_movement_action walk_in_place_slow_left
create_movement_action walk_in_place_slow_right
create_movement_action walk_in_place_down
create_movement_action walk_in_place_up
create_movement_action walk_in_place_left
create_movement_action walk_in_place_right
create_movement_action walk_in_place_fast_down
create_movement_action walk_in_place_fast_up
create_movement_action walk_in_place_fast_left
create_movement_action walk_in_place_fast_right
create_movement_action walk_in_place_fastest_down
create_movement_action walk_in_place_fastest_up
create_movement_action walk_in_place_fastest_left
create_movement_action walk_in_place_fastest_right
create_movement_action ride_water_current_down
create_movement_action ride_water_current_up
create_movement_action ride_water_current_left
create_movement_action ride_water_current_right
create_movement_action walk_fastest_down
create_movement_action walk_fastest_up
create_movement_action walk_fastest_left
create_movement_action walk_fastest_right
create_movement_action slide_down
create_movement_action slide_up
create_movement_action slide_left
create_movement_action slide_right
create_movement_action player_run_down
create_movement_action player_run_up
create_movement_action player_run_left
create_movement_action player_run_right
create_movement_action start_anim_in_direction
create_movement_action jump_special_down
create_movement_action jump_special_up
create_movement_action jump_special_left
create_movement_action jump_special_right
create_movement_action face_player
create_movement_action face_away_player
create_movement_action lock_facing_direction
create_movement_action unlock_facing_direction
create_movement_action jump_down
create_movement_action jump_up
create_movement_action jump_left
create_movement_action jump_right
create_movement_action jump_in_place_down
create_movement_action jump_in_place_up
create_movement_action jump_in_place_left
create_movement_action jump_in_place_right
create_movement_action jump_in_place_down_up
create_movement_action jump_in_place_up_down
create_movement_action jump_in_place_left_right
create_movement_action jump_in_place_right_left
create_movement_action face_original_direction
create_movement_action nurse_joy_bow
create_movement_action enable_jump_landing_ground_effect
create_movement_action disable_jump_landing_ground_effect
create_movement_action disable_anim
create_movement_action restore_anim
create_movement_action set_invisible
create_movement_action set_visible
create_movement_action emote_exclamation_mark
create_movement_action emote_question_mark
create_movement_action emote_heart
create_movement_action reveal_trainer
create_movement_action rock_smash_break
create_movement_action cut_tree
create_movement_action set_fixed_priority
create_movement_action clear_fixed_priority
create_movement_action init_affine_anim
create_movement_action clear_affine_anim
create_movement_action unknown_movement_1
create_movement_action unknown_movement_2
create_movement_action walk_down_start_affine
create_movement_action walk_down_affine
enum_start 0x8C
create_movement_action walk_diag_northwest
create_movement_action walk_diag_northeast
create_movement_action walk_diag_southwest
create_movement_action walk_diag_southeast
create_movement_action walk_slow_diag_northwest
create_movement_action walk_slow_diag_northeast
create_movement_action walk_slow_diag_southwest
create_movement_action walk_slow_diag_southeast
create_movement_action store_lock_anim
create_movement_action free_unlock_anim
create_movement_action walk_left_affine
create_movement_action walk_right_affine
create_movement_action levitate
create_movement_action stop_levitate
create_movement_action destroy_extra_task
create_movement_action figure_8
create_movement_action fly_up
create_movement_action fly_down
enum_start 0xfe
create_movement_action step_end

125
asm/macros/music_voice.inc Normal file
View File

@ -0,0 +1,125 @@
.macro voice_directsound base_midi_key, pan, sample_data_pointer, attack, decay, sustain, release
.byte 0
_voice_directsound \base_midi_key, \pan, \sample_data_pointer, \attack, \decay, \sustain, \release
.endm
.macro voice_directsound_no_resample base_midi_key, pan, sample_data_pointer, attack, decay, sustain, release
.byte 8
_voice_directsound \base_midi_key, \pan, \sample_data_pointer, \attack, \decay, \sustain, \release
.endm
.macro voice_directsound_alt base_midi_key, pan, sample_data_pointer, attack, decay, sustain, release
.byte 16
_voice_directsound \base_midi_key, \pan, \sample_data_pointer, \attack, \decay, \sustain, \release
.endm
.macro _voice_directsound base_midi_key, pan, sample_data_pointer, attack, decay, sustain, release
.byte \base_midi_key
.byte 0
.if \pan != 0
.byte (0x80 | \pan)
.else
.byte 0
.endif
.4byte \sample_data_pointer
.byte \attack
.byte \decay
.byte \sustain
.byte \release
.endm
.macro voice_square_1 sweep, duty_cycle, attack, decay, sustain, release
_voice_square_1 1, \sweep, \duty_cycle, \attack, \decay, \sustain, \release
.endm
.macro voice_square_1_alt sweep, duty_cycle, attack, decay, sustain, release
_voice_square_1 9, \sweep, \duty_cycle, \attack, \decay, \sustain, \release
.endm
.macro _voice_square_1 type, sweep, duty_cycle, attack, decay, sustain, release
.byte \type, 60, 0
.byte \sweep
.byte (\duty_cycle & 0x3)
.byte 0, 0, 0
.byte (\attack & 0x7)
.byte (\decay & 0x7)
.byte (\sustain & 0xF)
.byte (\release & 0x7)
.endm
.macro voice_square_2 duty_cycle, attack, decay, sustain, release
_voice_square_2 2, \duty_cycle, \attack, \decay, \sustain, \release
.endm
.macro voice_square_2_alt duty_cycle, attack, decay, sustain, release
_voice_square_2 10, \duty_cycle, \attack, \decay, \sustain, \release
.endm
.macro _voice_square_2 type, duty_cycle, attack, decay, sustain, release
.byte \type, 60, 0, 0
.byte (\duty_cycle & 0x3)
.byte 0, 0, 0
.byte (\attack & 0x7)
.byte (\decay & 0x7)
.byte (\sustain & 0xF)
.byte (\release & 0x7)
.endm
.macro voice_programmable_wave wave_samples_pointer, attack, decay, sustain, release
_voice_programmable_wave 3, \wave_samples_pointer, \attack, \decay, \sustain, \release
.endm
.macro voice_programmable_wave_alt wave_samples_pointer, attack, decay, sustain, release
_voice_programmable_wave 11, \wave_samples_pointer, \attack, \decay, \sustain, \release
.endm
.macro _voice_programmable_wave type, wave_samples_pointer, attack, decay, sustain, release
.byte \type, 60, 0, 0
.4byte \wave_samples_pointer
.byte (\attack & 0x7)
.byte (\decay & 0x7)
.byte (\sustain & 0xF)
.byte (\release & 0x7)
.endm
.macro voice_noise period, attack, decay, sustain, release
_voice_noise 4, \period, \attack, \decay, \sustain, \release
.endm
.macro voice_noise_alt period, attack, decay, sustain, release
_voice_noise 12, \period, \attack, \decay, \sustain, \release
.endm
.macro _voice_noise type, period, attack, decay, sustain, release
.byte \type, 60, 0, 0
.byte (\period & 0x1)
.byte 0, 0, 0
.byte (\attack & 0x7)
.byte (\decay & 0x7)
.byte (\sustain & 0xF)
.byte (\release & 0x7)
.endm
.macro voice_keysplit voice_group_pointer, keysplit_table_pointer
.byte 0x40, 0, 0, 0
.4byte \voice_group_pointer
.4byte \keysplit_table_pointer
.endm
.macro voice_keysplit_all voice_group_pointer
.byte 0x80, 0, 0, 0
.4byte \voice_group_pointer
.4byte 0
.endm
.macro cry sample
.byte 0x20, 60, 0, 0
.4byte \sample
.byte 0xff, 0, 0xff, 0
.endm
.macro cry2 sample
.byte 0x30, 60, 0, 0
.4byte \sample
.byte 0xff, 0, 0xff, 0
.endm

View File

@ -0,0 +1,56 @@
.macro pokedex_entry height, width, text_pointer, pokemon_scale, pokemon_offset, trainer_scale, trainer_offset
.2byte \height @ in decimeters
.2byte \width @ in hectograms
.4byte \text_pointer
.2byte 0 @ unused
.2byte \pokemon_scale
.2byte \pokemon_offset
.2byte \trainer_scale
.2byte \trainer_offset
.2byte 0 @ padding
.endm
.macro base_stats hp, attack, defense, speed, sp_attack, sp_defense
.byte \hp
.byte \attack
.byte \defense
.byte \speed
.byte \sp_attack
.byte \sp_defense
.endm
.macro ev_yield hp, attack, defense, speed, sp_attack, sp_defense
.2byte (\sp_defense << 10) | (\sp_attack << 8) | (\speed << 6) | (\defense << 4) | (\attack << 2) | \hp
.endm
.macro level_up_move level, move
.2byte (\level << 9) | \move
.endm
.macro evo_entry method, parameter, target_species
.2byte \method
.2byte \parameter
.2byte \target_species
.2byte 0 @ padding
.endm
.macro empty_evo_entries count
.fill 8 * \count, 1, 0
.endm
.macro egg_moves_begin species
.2byte 20000 + \species
.endm
@ If the min level equals the max level, only one level argument is needed.
.macro wild_mon species, min_level, max_level
.byte \min_level
.ifb \max_level
.byte \min_level
.else
.byte \max_level
.endif
.2byte \species
.endm

35
asm/macros/window.inc Normal file
View File

@ -0,0 +1,35 @@
.macro window_template bg_id, x, y, width, height, palette, vram_tile_offset
.byte \bg_id
.byte \x
.byte \y
.byte \width
.byte \height
.byte \palette
.2byte \vram_tile_offset
.endm
.macro null_window_template
window_template 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0000
.endm
.macro glyph_width_func font_id, func
.4byte \font_id
.4byte \func
.endm
.macro keypad_icon tile_offset, width, height
.2byte \tile_offset
.byte \width
.byte \height
.endm
.macro font_info func, max_glyph_width, glyph_height, glyph_spacing, line_spacing, text_color, shadow_color, bg_color
.4byte \func
.byte \max_glyph_width
.byte \glyph_height
.byte \glyph_spacing
.byte \line_spacing
.byte \text_color << 4 @ low nybble seems unused
.byte (\shadow_color << 4) | \bg_color
.2byte 0 @ padding
.endm

973185
asm/rom.s Normal file

File diff suppressed because it is too large Load Diff

1191
asm/siirtc.s Normal file

File diff suppressed because it is too large Load Diff

7
asmdiff.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
OBJDUMP="tools/binutils/bin/arm-none-eabi-objdump -D -bbinary -marmv4t -Mforce-thumb"
OPTIONS="--start-address=$(($1)) --stop-address=$(($1 + $2))"
$OBJDUMP $OPTIONS baserom_jp.gba > baserom_jp.dump
$OBJDUMP $OPTIONS pokeemerald_jp.gba > pokeemerald_jp.dump
diff -u baserom_jp.dump pokeemerald_jp.dump | less

12
build_tools.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
make -C tools/gbagfx CXX=${1:-g++}
make -C tools/scaninc CXX=${1:-g++}
make -C tools/preproc CXX=${1:-g++}
make -C tools/bin2c CXX=${1:-g++}
make -C tools/rsfont CXX=${1:-g++}
make -C tools/aif2pcm CXX=${1:-g++}
make -C tools/ramscrgen CXX=${1:-g++}
make -C tools/gbafix CXX=${1:-g++}
make -C tools/mid2agb CXX=${1:-g++}
make -C tools/mapjson CXX=${1:-g++}
make -C tools/jsonproc CXX=${1:-g++}

View File

@ -0,0 +1,6 @@
.set BERRY_FIRMNESS_UNKNOWN, 0
.set BERRY_FIRMNESS_VERY_SOFT, 1
.set BERRY_FIRMNESS_SOFT, 2
.set BERRY_FIRMNESS_HARD, 3
.set BERRY_FIRMNESS_VERY_HARD, 4
.set BERRY_FIRMNESS_SUPER_HARD, 5

6
constants/constants.inc Normal file
View File

@ -0,0 +1,6 @@
.include "constants/gba_constants.inc"
.include "constants/global.inc"
.include "constants/contest_constants.inc"
.include "constants/pokemon_data_constants.inc"
.include "constants/berry_constants.inc"
.include "constants/contest_move_effects.inc"

View File

@ -0,0 +1,68 @@
.set CONTEST_COOL, 0x00
.set CONTEST_BEAUTY, 0x01
.set CONTEST_CUTE, 0x02
.set CONTEST_SMART, 0x03
.set CONTEST_TOUGH, 0x04
.set COMBO_STARTER_RAIN_DANCE, 0x01
.set COMBO_STARTER_RAGE, 0x02
.set COMBO_STARTER_FOCUS_ENERGY, 0x03
.set COMBO_STARTER_HYPNOSIS, 0x04
.set COMBO_STARTER_ENDURE, 0x05
.set COMBO_STARTER_HORN_ATTACK, 0x06
.set COMBO_STARTER_SWORDS_DANCE, 0x07
.set COMBO_STARTER_STOCKPILE, 0x08
.set COMBO_STARTER_SUNNY_DAY, 0x09
.set COMBO_STARTER_REST, 0x0A
.set COMBO_STARTER_VICE_GRIP, 0x0B
.set COMBO_STARTER_DEFENSE_CURL, 0x0C
.set COMBO_STARTER_CHARGE, 0x0D
.set COMBO_STARTER_ROCK_THROW, 0x0E
.set COMBO_STARTER_YAWN, 0x0F
.set COMBO_STARTER_SCARY_FACE, 0x10
.set COMBO_STARTER_POWDER_SNOW, 0x11
.set COMBO_STARTER_LOCK_ON, 0x12
.set COMBO_STARTER_SOFT_BOILED, 0x13
.set COMBO_STARTER_MEAN_LOOK, 0x14
.set COMBO_STARTER_SCRATCH, 0x15
.set COMBO_STARTER_GROWTH, 0x16
.set COMBO_STARTER_HAIL, 0x17
.set COMBO_STARTER_SANDSTORM, 0x18
.set COMBO_STARTER_BELLY_DRUM, 0x19
.set COMBO_STARTER_MIND_READER, 0x1A
.set COMBO_STARTER_DRAGON_BREATH, 0x1B
.set COMBO_STARTER_DRAGON_RAGE, 0x1C
.set COMBO_STARTER_DRAGON_DANCE, 0x1D
.set COMBO_STARTER_SURF, 0x1E
.set COMBO_STARTER_DIVE, 0x1F
.set COMBO_STARTER_STRING_SHOT, 0x20
.set COMBO_STARTER_LEER, 0x21
.set COMBO_STARTER_TAUNT, 0x22
.set COMBO_STARTER_CHARM, 0x23
.set COMBO_STARTER_HARDEN, 0x24
.set COMBO_STARTER_SING, 0x25
.set COMBO_STARTER_EARTHQUAKE, 0x26
.set COMBO_STARTER_DOUBLE_TEAM, 0x27
.set COMBO_STARTER_CURSE, 0x28
.set COMBO_STARTER_SWEET_SCENT, 0x29
.set COMBO_STARTER_SLUDGE, 0x2A
.set COMBO_STARTER_SLUDGE_BOMB, 0x2B
.set COMBO_STARTER_THUNDER_PUNCH, 0x2C
.set COMBO_STARTER_FIRE_PUNCH, 0x2D
.set COMBO_STARTER_ICE_PUNCH, 0x2E
.set COMBO_STARTER_PECK, 0x2F
.set COMBO_STARTER_METAL_SOUND, 0x30
.set COMBO_STARTER_MUD_SPORT, 0x31
.set COMBO_STARTER_WATER_SPORT, 0x32
.set COMBO_STARTER_BONE_CLUB, 0x33
.set COMBO_STARTER_BONEMERANG, 0x34
.set COMBO_STARTER_BONE_RUSH, 0x35
.set COMBO_STARTER_SAND_ATTACK, 0x36
.set COMBO_STARTER_MUD_SLAP, 0x37
.set COMBO_STARTER_FAKE_OUT, 0x38
.set COMBO_STARTER_PSYCHIC, 0x39
.set COMBO_STARTER_KINESIS, 0x3A
.set COMBO_STARTER_CONFUSION, 0x3B
.set COMBO_STARTER_POUND, 0x3C
.set COMBO_STARTER_SMOG, 0x3D
.set COMBO_STARTER_CALM_MIND, 0x3E

View File

@ -0,0 +1,49 @@
enum_start
enum CONTEST_EFFECT_HIGHLY_APPEALING @ 0
enum CONTEST_EFFECT_USER_MORE_EASILY_STARTLED @ 1
enum CONTEST_EFFECT_GREAT_APPEAL_BUT_NO_MORE_MOVES @ 2
enum CONTEST_EFFECT_REPETITION_NOT_BORING @ 3
enum CONTEST_EFFECT_AVOID_STARTLE_ONCE @ 4
enum CONTEST_EFFECT_AVOID_STARTLE @ 5
enum CONTEST_EFFECT_AVOID_STARTLE_SLIGHTLY @ 6
enum CONTEST_EFFECT_USER_LESS_EASILY_STARTLED @ 7
enum CONTEST_EFFECT_STARTLE_FRONT_MON @ 8
enum CONTEST_EFFECT_SLIGHTLY_STARTLE_PREV_MONS @ 9
enum CONTEST_EFFECT_STARTLE_PREV_MON @ 10
enum CONTEST_EFFECT_STARTLE_PREV_MONS @ 11
enum CONTEST_EFFECT_BADLY_STARTLE_FRONT_MON @ 12
enum CONTEST_EFFECT_BADLY_STARTLE_PREV_MONS @ 13
enum CONTEST_EFFECT_STARTLE_PREV_MON_2 @ 14
enum CONTEST_EFFECT_STARTLE_PREV_MONS_2 @ 15
enum CONTEST_EFFECT_SHIFT_JUDGE_ATTENTION @ 16
enum CONTEST_EFFECT_STARTLE_MON_WITH_JUDGES_ATTENTION @ 17
enum CONTEST_EFFECT_JAMS_OTHERS_BUT_MISS_ONE_TURN @ 18
enum CONTEST_EFFECT_STARTLE_MONS_SAME_TYPE_APPEAL @ 19
enum CONTEST_EFFECT_STARTLE_MONS_COOL_APPEAL @ 20
enum CONTEST_EFFECT_STARTLE_MONS_BEAUTY_APPEAL @ 21
enum CONTEST_EFFECT_STARTLE_MONS_CUTE_APPEAL @ 22
enum CONTEST_EFFECT_STARTLE_MONS_SMART_APPEAL @ 23
enum CONTEST_EFFECT_STARTLE_MONS_TOUGH_APPEAL @ 24
enum CONTEST_EFFECT_MAKE_FOLLOWING_MON_NERVOUS @ 25
enum CONTEST_EFFECT_MAKE_FOLLOWING_MONS_NERVOUS @ 26
enum CONTEST_EFFECT_WORSEN_CONDITION_OF_PREV_MONS @ 27
enum CONTEST_EFFECT_BADLY_STARTLES_MONS_IN_GOOD_CONDITION @ 28
enum CONTEST_EFFECT_BETTER_IF_FIRST @ 29
enum CONTEST_EFFECT_BETTER_IF_LAST @ 30
enum CONTEST_EFFECT_APPEAL_AS_GOOD_AS_PREV_ONES @ 31
enum CONTEST_EFFECT_APPEAL_AS_GOOD_AS_PREV_ONE @ 32
enum CONTEST_EFFECT_BETTER_WHEN_LATER @ 33
enum CONTEST_EFFECT_QUALITY_DEPENDS_ON_TIMING @ 34
enum CONTEST_EFFECT_BETTER_IF_SAME_TYPE @ 35
enum CONTEST_EFFECT_BETTER_IF_DIFF_TYPE @ 36
enum CONTEST_EFFECT_AFFECTED_BY_PREV_APPEAL @ 37
enum CONTEST_EFFECT_IMPROVE_CONDITION_PREVENT_NERVOUSNESS @ 38
enum CONTEST_EFFECT_BETTER_WITH_GOOD_CONDITION @ 39
enum CONTEST_EFFECT_NEXT_APPEAL_EARLIER @ 40
enum CONTEST_EFFECT_NEXT_APPEAL_LATER @ 41
enum CONTEST_EFFECT_MAKE_SCRAMBLING_TURN_ORDER_EASIER @ 42
enum CONTEST_EFFECT_SCRAMBLE_NEXT_TURN_ORDER @ 43
enum CONTEST_EFFECT_EXCITE_AUDIENCE_IN_ANY_CONTEST @ 44
enum CONTEST_EFFECT_BADLY_STARTLE_MONS_WITH_GOOD_APPEALS @ 45
enum CONTEST_EFFECT_BETTER_WHEN_AUDIENCE_EXCITED @ 46
enum CONTEST_EFFECT_DONT_EXCITE_AUDIENCE @ 47

490
constants/gba_constants.inc Normal file
View File

@ -0,0 +1,490 @@
.set PSR_USR_MODE, 0x00000010
.set PSR_FIQ_MODE, 0x00000011
.set PSR_IRQ_MODE, 0x00000012
.set PSR_SVC_MODE, 0x00000013
.set PSR_ABT_MODE, 0x00000017
.set PSR_UND_MODE, 0x0000001b
.set PSR_SYS_MODE, 0x0000001f
.set PSR_MODE_MASK, 0x0000001f
.set PSR_T_BIT, 0x00000020
.set PSR_F_BIT, 0x00000040
.set PSR_I_BIT, 0x00000080
.set EWRAM_START, 0x02000000
.set EWRAM_END, EWRAM_START + 0x40000
.set IWRAM_START, 0x03000000
.set IWRAM_END, IWRAM_START + 0x8000
.set PLTT, 0x5000000
.set BG_PLTT, PLTT
.set OBJ_PLTT, PLTT + 0x200
.set VRAM, 0x6000000
.set BG_VRAM, VRAM
.set OBJ_VRAM0, VRAM + 0x10000 @ text-mode BG
.set OBJ_VRAM1, VRAM + 0x14000 @ bitmap-mode BG
.set OAM, 0x7000000
.set SOUND_INFO_PTR, 0x3007FF0
.set INTR_CHECK, 0x3007FF8
.set INTR_VECTOR, 0x3007FFC
.set INTR_FLAG_VBLANK, 1 << 0
.set INTR_FLAG_HBLANK, 1 << 1
.set INTR_FLAG_VCOUNT, 1 << 2
.set INTR_FLAG_TIMER0, 1 << 3
.set INTR_FLAG_TIMER1, 1 << 4
.set INTR_FLAG_TIMER2, 1 << 5
.set INTR_FLAG_TIMER3, 1 << 6
.set INTR_FLAG_SERIAL, 1 << 7
.set INTR_FLAG_DMA0, 1 << 8
.set INTR_FLAG_DMA1, 1 << 9
.set INTR_FLAG_DMA2, 1 << 10
.set INTR_FLAG_DMA3, 1 << 11
.set INTR_FLAG_KEYPAD, 1 << 12
.set INTR_FLAG_GAMEPAK, 1 << 13
.set VCOUNT_VBLANK, 160
.set TOTAL_SCANLINES, 228
.set REG_BASE, 0x4000000 @ I/O register base address
@ I/O register offsets
.set OFFSET_REG_DISPCNT, 0x0
.set OFFSET_REG_DISPSTAT, 0x4
.set OFFSET_REG_VCOUNT, 0x6
.set OFFSET_REG_BG0CNT, 0x8
.set OFFSET_REG_BG1CNT, 0xa
.set OFFSET_REG_BG2CNT, 0xc
.set OFFSET_REG_BG3CNT, 0xe
.set OFFSET_REG_BG0HOFS, 0x10
.set OFFSET_REG_BG0VOFS, 0x12
.set OFFSET_REG_BG1HOFS, 0x14
.set OFFSET_REG_BG1VOFS, 0x16
.set OFFSET_REG_BG2HOFS, 0x18
.set OFFSET_REG_BG2VOFS, 0x1a
.set OFFSET_REG_BG3HOFS, 0x1c
.set OFFSET_REG_BG3VOFS, 0x1e
.set OFFSET_REG_BG2PA, 0x20
.set OFFSET_REG_BG2PB, 0x22
.set OFFSET_REG_BG2PC, 0x24
.set OFFSET_REG_BG2PD, 0x26
.set OFFSET_REG_BG2X_L, 0x28
.set OFFSET_REG_BG2X_H, 0x2a
.set OFFSET_REG_BG2Y_L, 0x2c
.set OFFSET_REG_BG2Y_H, 0x2e
.set OFFSET_REG_BG3PA, 0x30
.set OFFSET_REG_BG3PB, 0x32
.set OFFSET_REG_BG3PC, 0x34
.set OFFSET_REG_BG3PD, 0x36
.set OFFSET_REG_BG3X_L, 0x38
.set OFFSET_REG_BG3X_H, 0x3a
.set OFFSET_REG_BG3Y_L, 0x3c
.set OFFSET_REG_BG3Y_H, 0x3e
.set OFFSET_REG_WIN0H, 0x40
.set OFFSET_REG_WIN1H, 0x42
.set OFFSET_REG_WIN0V, 0x44
.set OFFSET_REG_WIN1V, 0x46
.set OFFSET_REG_WININ, 0x48
.set OFFSET_REG_WINOUT, 0x4a
.set OFFSET_REG_MOSAIC, 0x4c
.set OFFSET_REG_BLDCNT, 0x50
.set OFFSET_REG_BLDALPHA, 0x52
.set OFFSET_REG_BLDY, 0x54
.set OFFSET_REG_SOUND1CNT, 0x60
.set OFFSET_REG_SOUND1CNT_L, 0x60
.set OFFSET_REG_NR10, 0x60
.set OFFSET_REG_SOUND1CNT_H, 0x62
.set OFFSET_REG_NR11, 0x62
.set OFFSET_REG_NR12, 0x63
.set OFFSET_REG_SOUND1CNT_X, 0x64
.set OFFSET_REG_NR13, 0x64
.set OFFSET_REG_NR14, 0x65
.set OFFSET_REG_SOUND2CNT, 0x68
.set OFFSET_REG_SOUND2CNT_L, 0x68
.set OFFSET_REG_NR21, 0x68
.set OFFSET_REG_NR22, 0x69
.set OFFSET_REG_SOUND2CNT_H, 0x6c
.set OFFSET_REG_NR23, 0x6c
.set OFFSET_REG_NR24, 0x6d
.set OFFSET_REG_SOUND3CNT, 0x70
.set OFFSET_REG_SOUND3CNT_L, 0x70
.set OFFSET_REG_NR30, 0x70
.set OFFSET_REG_SOUND3CNT_H, 0x72
.set OFFSET_REG_NR31, 0x72
.set OFFSET_REG_NR32, 0x73
.set OFFSET_REG_SOUND3CNT_X, 0x74
.set OFFSET_REG_NR33, 0x74
.set OFFSET_REG_NR34, 0x75
.set OFFSET_REG_SOUND4CNT, 0x78
.set OFFSET_REG_SOUND4CNT_L, 0x78
.set OFFSET_REG_NR41, 0x78
.set OFFSET_REG_NR42, 0x79
.set OFFSET_REG_SOUND4CNT_H, 0x7c
.set OFFSET_REG_NR43, 0x7c
.set OFFSET_REG_NR44, 0x7d
.set OFFSET_REG_SOUNDCNT, 0x80
.set OFFSET_REG_SOUNDCNT_L, 0x80
.set OFFSET_REG_NR50, 0x80
.set OFFSET_REG_NR51, 0x81
.set OFFSET_REG_SOUNDCNT_H, 0x82
.set OFFSET_REG_SOUNDCNT_X, 0x84
.set OFFSET_REG_NR52, 0x84
.set OFFSET_REG_SOUNDBIAS, 0x88
.set OFFSET_REG_WAVE_RAM, 0x90
.set OFFSET_REG_WAVE_RAM0, 0x90
.set OFFSET_REG_WAVE_RAM0_L, 0x90
.set OFFSET_REG_WAVE_RAM0_H, 0x92
.set OFFSET_REG_WAVE_RAM1, 0x94
.set OFFSET_REG_WAVE_RAM1_L, 0x94
.set OFFSET_REG_WAVE_RAM1_H, 0x96
.set OFFSET_REG_WAVE_RAM2, 0x98
.set OFFSET_REG_WAVE_RAM2_L, 0x98
.set OFFSET_REG_WAVE_RAM2_H, 0x9a
.set OFFSET_REG_WAVE_RAM3, 0x9c
.set OFFSET_REG_WAVE_RAM3_L, 0x9c
.set OFFSET_REG_WAVE_RAM3_H, 0x9e
.set OFFSET_REG_FIFO, 0xa0
.set OFFSET_REG_FIFO_A, 0xa0
.set OFFSET_REG_FIFO_A_L, 0xa0
.set OFFSET_REG_FIFO_A_H, 0xa2
.set OFFSET_REG_FIFO_B, 0xa4
.set OFFSET_REG_FIFO_B_L, 0xa4
.set OFFSET_REG_FIFO_B_H, 0xa6
.set OFFSET_REG_DMA0, 0xb0
.set OFFSET_REG_DMA0SAD, 0xb0
.set OFFSET_REG_DMA0SAD_L, 0xb0
.set OFFSET_REG_DMA0SAD_H, 0xb2
.set OFFSET_REG_DMA0DAD, 0xb4
.set OFFSET_REG_DMA0DAD_L, 0xb4
.set OFFSET_REG_DMA0DAD_H, 0xb6
.set OFFSET_REG_DMA0CNT, 0xb8
.set OFFSET_REG_DMA0CNT_L, 0xb8
.set OFFSET_REG_DMA0CNT_H, 0xba
.set OFFSET_REG_DMA1, 0xbc
.set OFFSET_REG_DMA1SAD, 0xbc
.set OFFSET_REG_DMA1SAD_L, 0xbc
.set OFFSET_REG_DMA1SAD_H, 0xbe
.set OFFSET_REG_DMA1DAD, 0xc0
.set OFFSET_REG_DMA1DAD_L, 0xc0
.set OFFSET_REG_DMA1DAD_H, 0xc2
.set OFFSET_REG_DMA1CNT, 0xc4
.set OFFSET_REG_DMA1CNT_L, 0xc4
.set OFFSET_REG_DMA1CNT_H, 0xc6
.set OFFSET_REG_DMA2, 0xc8
.set OFFSET_REG_DMA2SAD, 0xc8
.set OFFSET_REG_DMA2SAD_L, 0xc8
.set OFFSET_REG_DMA2SAD_H, 0xca
.set OFFSET_REG_DMA2DAD, 0xcc
.set OFFSET_REG_DMA2DAD_L, 0xcc
.set OFFSET_REG_DMA2DAD_H, 0xce
.set OFFSET_REG_DMA2CNT, 0xd0
.set OFFSET_REG_DMA2CNT_L, 0xd0
.set OFFSET_REG_DMA2CNT_H, 0xd2
.set OFFSET_REG_DMA3, 0xd4
.set OFFSET_REG_DMA3SAD, 0xd4
.set OFFSET_REG_DMA3SAD_L, 0xd4
.set OFFSET_REG_DMA3SAD_H, 0xd6
.set OFFSET_REG_DMA3DAD, 0xd8
.set OFFSET_REG_DMA3DAD_L, 0xd8
.set OFFSET_REG_DMA3DAD_H, 0xda
.set OFFSET_REG_DMA3CNT, 0xdc
.set OFFSET_REG_DMA3CNT_L, 0xdc
.set OFFSET_REG_DMA3CNT_H, 0xde
.set OFFSET_REG_TM0CNT, 0x100
.set OFFSET_REG_TM0CNT_L, 0x100
.set OFFSET_REG_TM0CNT_H, 0x102
.set OFFSET_REG_TM1CNT, 0x104
.set OFFSET_REG_TM1CNT_L, 0x104
.set OFFSET_REG_TM1CNT_H, 0x106
.set OFFSET_REG_TM2CNT, 0x108
.set OFFSET_REG_TM2CNT_L, 0x108
.set OFFSET_REG_TM2CNT_H, 0x10a
.set OFFSET_REG_TM3CNT, 0x10c
.set OFFSET_REG_TM3CNT_L, 0x10c
.set OFFSET_REG_TM3CNT_H, 0x10e
.set OFFSET_REG_SIOCNT, 0x128
.set OFFSET_REG_SIODATA8, 0x12a
.set OFFSET_REG_SIODATA32, 0x120
.set OFFSET_REG_SIOMLT_SEND, 0x12a
.set OFFSET_REG_SIOMLT_RECV, 0x120
.set OFFSET_REG_SIOMULTI0, 0x120
.set OFFSET_REG_SIOMULTI1, 0x122
.set OFFSET_REG_SIOMULTI2, 0x124
.set OFFSET_REG_SIOMULTI3, 0x126
.set OFFSET_REG_KEYINPUT, 0x130
.set OFFSET_REG_KEYCNT, 0x132
.set OFFSET_REG_RCNT, 0x134
.set OFFSET_REG_JOYCNT, 0x140
.set OFFSET_REG_JOYSTAT, 0x158
.set OFFSET_REG_JOY_RECV, 0x150
.set OFFSET_REG_JOY_RECV_L, 0x150
.set OFFSET_REG_JOY_RECV_H, 0x152
.set OFFSET_REG_JOY_TRANS, 0x154
.set OFFSET_REG_JOY_TRANS_L, 0x154
.set OFFSET_REG_JOY_TRANS_H, 0x156
.set OFFSET_REG_IME, 0x208
.set OFFSET_REG_IE, 0x200
.set OFFSET_REG_IF, 0x202
.set OFFSET_REG_WAITCNT, 0x204
@ I/O register addresses
.set REG_DISPCNT, REG_BASE + OFFSET_REG_DISPCNT
.set REG_DISPSTAT, REG_BASE + OFFSET_REG_DISPSTAT
.set REG_VCOUNT, REG_BASE + OFFSET_REG_VCOUNT
.set REG_BG0CNT, REG_BASE + OFFSET_REG_BG0CNT
.set REG_BG1CNT, REG_BASE + OFFSET_REG_BG1CNT
.set REG_BG2CNT, REG_BASE + OFFSET_REG_BG2CNT
.set REG_BG3CNT, REG_BASE + OFFSET_REG_BG3CNT
.set REG_BG0HOFS, REG_BASE + OFFSET_REG_BG0HOFS
.set REG_BG0VOFS, REG_BASE + OFFSET_REG_BG0VOFS
.set REG_BG1HOFS, REG_BASE + OFFSET_REG_BG1HOFS
.set REG_BG1VOFS, REG_BASE + OFFSET_REG_BG1VOFS
.set REG_BG2HOFS, REG_BASE + OFFSET_REG_BG2HOFS
.set REG_BG2VOFS, REG_BASE + OFFSET_REG_BG2VOFS
.set REG_BG3HOFS, REG_BASE + OFFSET_REG_BG3HOFS
.set REG_BG3VOFS, REG_BASE + OFFSET_REG_BG3VOFS
.set REG_BG2PA, REG_BASE + OFFSET_REG_BG2PA
.set REG_BG2PB, REG_BASE + OFFSET_REG_BG2PB
.set REG_BG2PC, REG_BASE + OFFSET_REG_BG2PC
.set REG_BG2PD, REG_BASE + OFFSET_REG_BG2PD
.set REG_BG2X_L, REG_BASE + OFFSET_REG_BG2X_L
.set REG_BG2X_H, REG_BASE + OFFSET_REG_BG2X_H
.set REG_BG2Y_L, REG_BASE + OFFSET_REG_BG2Y_L
.set REG_BG2Y_H, REG_BASE + OFFSET_REG_BG2Y_H
.set REG_BG3PA, REG_BASE + OFFSET_REG_BG3PA
.set REG_BG3PB, REG_BASE + OFFSET_REG_BG3PB
.set REG_BG3PC, REG_BASE + OFFSET_REG_BG3PC
.set REG_BG3PD, REG_BASE + OFFSET_REG_BG3PD
.set REG_BG3X_L, REG_BASE + OFFSET_REG_BG3X_L
.set REG_BG3X_H, REG_BASE + OFFSET_REG_BG3X_H
.set REG_BG3Y_L, REG_BASE + OFFSET_REG_BG3Y_L
.set REG_BG3Y_H, REG_BASE + OFFSET_REG_BG3Y_H
.set REG_WIN0H, REG_BASE + OFFSET_REG_WIN0H
.set REG_WIN1H, REG_BASE + OFFSET_REG_WIN1H
.set REG_WIN0V, REG_BASE + OFFSET_REG_WIN0V
.set REG_WIN1V, REG_BASE + OFFSET_REG_WIN1V
.set REG_WININ, REG_BASE + OFFSET_REG_WININ
.set REG_WINOUT, REG_BASE + OFFSET_REG_WINOUT
.set REG_MOSAIC, REG_BASE + OFFSET_REG_MOSAIC
.set REG_BLDCNT, REG_BASE + OFFSET_REG_BLDCNT
.set REG_BLDALPHA, REG_BASE + OFFSET_REG_BLDALPHA
.set REG_BLDY, REG_BASE + OFFSET_REG_BLDY
.set REG_SOUND1CNT, REG_BASE + OFFSET_REG_SOUND1CNT
.set REG_SOUND1CNT_L, REG_BASE + OFFSET_REG_SOUND1CNT_L
.set REG_NR10, REG_BASE + OFFSET_REG_NR10
.set REG_SOUND1CNT_H, REG_BASE + OFFSET_REG_SOUND1CNT_H
.set REG_NR11, REG_BASE + OFFSET_REG_NR11
.set REG_NR12, REG_BASE + OFFSET_REG_NR12
.set REG_SOUND1CNT_X, REG_BASE + OFFSET_REG_SOUND1CNT_X
.set REG_NR13, REG_BASE + OFFSET_REG_NR13
.set REG_NR14, REG_BASE + OFFSET_REG_NR14
.set REG_SOUND2CNT, REG_BASE + OFFSET_REG_SOUND2CNT
.set REG_SOUND2CNT_L, REG_BASE + OFFSET_REG_SOUND2CNT_L
.set REG_NR21, REG_BASE + OFFSET_REG_NR21
.set REG_NR22, REG_BASE + OFFSET_REG_NR22
.set REG_SOUND2CNT_H, REG_BASE + OFFSET_REG_SOUND2CNT_H
.set REG_NR23, REG_BASE + OFFSET_REG_NR23
.set REG_NR24, REG_BASE + OFFSET_REG_NR24
.set REG_SOUND3CNT, REG_BASE + OFFSET_REG_SOUND3CNT
.set REG_SOUND3CNT_L, REG_BASE + OFFSET_REG_SOUND3CNT_L
.set REG_NR30, REG_BASE + OFFSET_REG_NR30
.set REG_SOUND3CNT_H, REG_BASE + OFFSET_REG_SOUND3CNT_H
.set REG_NR31, REG_BASE + OFFSET_REG_NR31
.set REG_NR32, REG_BASE + OFFSET_REG_NR32
.set REG_SOUND3CNT_X, REG_BASE + OFFSET_REG_SOUND3CNT_X
.set REG_NR33, REG_BASE + OFFSET_REG_NR33
.set REG_NR34, REG_BASE + OFFSET_REG_NR34
.set REG_SOUND4CNT, REG_BASE + OFFSET_REG_SOUND4CNT
.set REG_SOUND4CNT_L, REG_BASE + OFFSET_REG_SOUND4CNT_L
.set REG_NR41, REG_BASE + OFFSET_REG_NR41
.set REG_NR42, REG_BASE + OFFSET_REG_NR42
.set REG_SOUND4CNT_H, REG_BASE + OFFSET_REG_SOUND4CNT_H
.set REG_NR43, REG_BASE + OFFSET_REG_NR43
.set REG_NR44, REG_BASE + OFFSET_REG_NR44
.set REG_SOUNDCNT, REG_BASE + OFFSET_REG_SOUNDCNT
.set REG_SOUNDCNT_L, REG_BASE + OFFSET_REG_SOUNDCNT_L
.set REG_NR50, REG_BASE + OFFSET_REG_NR50
.set REG_NR51, REG_BASE + OFFSET_REG_NR51
.set REG_SOUNDCNT_H, REG_BASE + OFFSET_REG_SOUNDCNT_H
.set REG_SOUNDCNT_X, REG_BASE + OFFSET_REG_SOUNDCNT_X
.set REG_NR52, REG_BASE + OFFSET_REG_NR52
.set REG_SOUNDBIAS, REG_BASE + OFFSET_REG_SOUNDBIAS
.set REG_WAVE_RAM, REG_BASE + OFFSET_REG_WAVE_RAM
.set REG_WAVE_RAM0, REG_BASE + OFFSET_REG_WAVE_RAM0
.set REG_WAVE_RAM0_L, REG_BASE + OFFSET_REG_WAVE_RAM0_L
.set REG_WAVE_RAM0_H, REG_BASE + OFFSET_REG_WAVE_RAM0_H
.set REG_WAVE_RAM1, REG_BASE + OFFSET_REG_WAVE_RAM1
.set REG_WAVE_RAM1_L, REG_BASE + OFFSET_REG_WAVE_RAM1_L
.set REG_WAVE_RAM1_H, REG_BASE + OFFSET_REG_WAVE_RAM1_H
.set REG_WAVE_RAM2, REG_BASE + OFFSET_REG_WAVE_RAM2
.set REG_WAVE_RAM2_L, REG_BASE + OFFSET_REG_WAVE_RAM2_L
.set REG_WAVE_RAM2_H, REG_BASE + OFFSET_REG_WAVE_RAM2_H
.set REG_WAVE_RAM3, REG_BASE + OFFSET_REG_WAVE_RAM3
.set REG_WAVE_RAM3_L, REG_BASE + OFFSET_REG_WAVE_RAM3_L
.set REG_WAVE_RAM3_H, REG_BASE + OFFSET_REG_WAVE_RAM3_H
.set REG_FIFO, REG_BASE + OFFSET_REG_FIFO
.set REG_FIFO_A, REG_BASE + OFFSET_REG_FIFO_A
.set REG_FIFO_A_L, REG_BASE + OFFSET_REG_FIFO_A_L
.set REG_FIFO_A_H, REG_BASE + OFFSET_REG_FIFO_A_H
.set REG_FIFO_B, REG_BASE + OFFSET_REG_FIFO_B
.set REG_FIFO_B_L, REG_BASE + OFFSET_REG_FIFO_B_L
.set REG_FIFO_B_H, REG_BASE + OFFSET_REG_FIFO_B_H
.set REG_DMA0, REG_BASE + OFFSET_REG_DMA0
.set REG_DMA0SAD, REG_BASE + OFFSET_REG_DMA0SAD
.set REG_DMA0SAD_L, REG_BASE + OFFSET_REG_DMA0SAD_L
.set REG_DMA0SAD_H, REG_BASE + OFFSET_REG_DMA0SAD_H
.set REG_DMA0DAD, REG_BASE + OFFSET_REG_DMA0DAD
.set REG_DMA0DAD_L, REG_BASE + OFFSET_REG_DMA0DAD_L
.set REG_DMA0DAD_H, REG_BASE + OFFSET_REG_DMA0DAD_H
.set REG_DMA0CNT, REG_BASE + OFFSET_REG_DMA0CNT
.set REG_DMA0CNT_L, REG_BASE + OFFSET_REG_DMA0CNT_L
.set REG_DMA0CNT_H, REG_BASE + OFFSET_REG_DMA0CNT_H
.set REG_DMA1, REG_BASE + OFFSET_REG_DMA1
.set REG_DMA1SAD, REG_BASE + OFFSET_REG_DMA1SAD
.set REG_DMA1SAD_L, REG_BASE + OFFSET_REG_DMA1SAD_L
.set REG_DMA1SAD_H, REG_BASE + OFFSET_REG_DMA1SAD_H
.set REG_DMA1DAD, REG_BASE + OFFSET_REG_DMA1DAD
.set REG_DMA1DAD_L, REG_BASE + OFFSET_REG_DMA1DAD_L
.set REG_DMA1DAD_H, REG_BASE + OFFSET_REG_DMA1DAD_H
.set REG_DMA1CNT, REG_BASE + OFFSET_REG_DMA1CNT
.set REG_DMA1CNT_L, REG_BASE + OFFSET_REG_DMA1CNT_L
.set REG_DMA1CNT_H, REG_BASE + OFFSET_REG_DMA1CNT_H
.set REG_DMA2, REG_BASE + OFFSET_REG_DMA2
.set REG_DMA2SAD, REG_BASE + OFFSET_REG_DMA2SAD
.set REG_DMA2SAD_L, REG_BASE + OFFSET_REG_DMA2SAD_L
.set REG_DMA2SAD_H, REG_BASE + OFFSET_REG_DMA2SAD_H
.set REG_DMA2DAD, REG_BASE + OFFSET_REG_DMA2DAD
.set REG_DMA2DAD_L, REG_BASE + OFFSET_REG_DMA2DAD_L
.set REG_DMA2DAD_H, REG_BASE + OFFSET_REG_DMA2DAD_H
.set REG_DMA2CNT, REG_BASE + OFFSET_REG_DMA2CNT
.set REG_DMA2CNT_L, REG_BASE + OFFSET_REG_DMA2CNT_L
.set REG_DMA2CNT_H, REG_BASE + OFFSET_REG_DMA2CNT_H
.set REG_DMA3, REG_BASE + OFFSET_REG_DMA3
.set REG_DMA3SAD, REG_BASE + OFFSET_REG_DMA3SAD
.set REG_DMA3SAD_L, REG_BASE + OFFSET_REG_DMA3SAD_L
.set REG_DMA3SAD_H, REG_BASE + OFFSET_REG_DMA3SAD_H
.set REG_DMA3DAD, REG_BASE + OFFSET_REG_DMA3DAD
.set REG_DMA3DAD_L, REG_BASE + OFFSET_REG_DMA3DAD_L
.set REG_DMA3DAD_H, REG_BASE + OFFSET_REG_DMA3DAD_H
.set REG_DMA3CNT, REG_BASE + OFFSET_REG_DMA3CNT
.set REG_DMA3CNT_L, REG_BASE + OFFSET_REG_DMA3CNT_L
.set REG_DMA3CNT_H, REG_BASE + OFFSET_REG_DMA3CNT_H
.set REG_TM0CNT, REG_BASE + OFFSET_REG_TM0CNT
.set REG_TM0CNT_L, REG_BASE + OFFSET_REG_TM0CNT_L
.set REG_TM0CNT_H, REG_BASE + OFFSET_REG_TM0CNT_H
.set REG_TM1CNT, REG_BASE + OFFSET_REG_TM1CNT
.set REG_TM1CNT_L, REG_BASE + OFFSET_REG_TM1CNT_L
.set REG_TM1CNT_H, REG_BASE + OFFSET_REG_TM1CNT_H
.set REG_TM2CNT, REG_BASE + OFFSET_REG_TM2CNT
.set REG_TM2CNT_L, REG_BASE + OFFSET_REG_TM2CNT_L
.set REG_TM2CNT_H, REG_BASE + OFFSET_REG_TM2CNT_H
.set REG_TM3CNT, REG_BASE + OFFSET_REG_TM3CNT
.set REG_TM3CNT_L, REG_BASE + OFFSET_REG_TM3CNT_L
.set REG_TM3CNT_H, REG_BASE + OFFSET_REG_TM3CNT_H
.set REG_SIOCNT, REG_BASE + OFFSET_REG_SIOCNT
.set REG_SIODATA8, REG_BASE + OFFSET_REG_SIODATA8
.set REG_SIODATA32, REG_BASE + OFFSET_REG_SIODATA32
.set REG_SIOMLT_SEND, REG_BASE + OFFSET_REG_SIOMLT_SEND
.set REG_SIOMLT_RECV, REG_BASE + OFFSET_REG_SIOMLT_RECV
.set REG_SIOMULTI0, REG_BASE + OFFSET_REG_SIOMULTI0
.set REG_SIOMULTI1, REG_BASE + OFFSET_REG_SIOMULTI1
.set REG_SIOMULTI2, REG_BASE + OFFSET_REG_SIOMULTI2
.set REG_SIOMULTI3, REG_BASE + OFFSET_REG_SIOMULTI3
.set REG_KEYINPUT, REG_BASE + OFFSET_REG_KEYINPUT
.set REG_KEYCNT, REG_BASE + OFFSET_REG_KEYCNT
.set REG_RCNT, REG_BASE + OFFSET_REG_RCNT
.set REG_JOYCNT, REG_BASE + OFFSET_REG_JOYCNT
.set REG_JOYSTAT, REG_BASE + OFFSET_REG_JOYSTAT
.set REG_JOY_RECV, REG_BASE + OFFSET_REG_JOY_RECV
.set REG_JOY_RECV_L, REG_BASE + OFFSET_REG_JOY_RECV_L
.set REG_JOY_RECV_H, REG_BASE + OFFSET_REG_JOY_RECV_H
.set REG_JOY_TRANS, REG_BASE + OFFSET_REG_JOY_TRANS
.set REG_JOY_TRANS_L, REG_BASE + OFFSET_REG_JOY_TRANS_L
.set REG_JOY_TRANS_H, REG_BASE + OFFSET_REG_JOY_TRANS_H
.set REG_IME, REG_BASE + OFFSET_REG_IME
.set REG_IE, REG_BASE + OFFSET_REG_IE
.set REG_IF, REG_BASE + OFFSET_REG_IF
.set REG_WAITCNT, REG_BASE + OFFSET_REG_WAITCNT
@ DMA register constants
.set DMA_DEST_INC, 0x0000
.set DMA_DEST_DEC, 0x0020
.set DMA_DEST_FIXED, 0x0040
.set DMA_DEST_RELOAD, 0x0060
.set DMA_SRC_INC, 0x0000
.set DMA_SRC_DEC, 0x0080
.set DMA_SRC_FIXED, 0x0100
.set DMA_REPEAT, 0x0200
.set DMA_16BIT, 0x0000
.set DMA_32BIT, 0x0400
.set DMA_DREQ_ON, 0x0800
.set DMA_START_NOW, 0x0000
.set DMA_START_VBLANK, 0x1000
.set DMA_START_HBLANK, 0x2000
.set DMA_START_SPECIAL, 0x3000
.set DMA_INTR_ENABLE, 0x4000
.set DMA_ENABLE, 0x8000
@ OAM attribute constants
.set OAM_OBJ_NORMAL, 0x00000000
.set OAM_OBJ_BLEND, 0x00000400
.set OAM_OBJ_WINDOW, 0x00000800
.set OAM_AFFINE_NONE, 0x00000000
.set OAM_AFFINE_NORMAL_SIZE, 0x00000100
.set OAM_OBJ_DISABLED, 0x00000200
.set OAM_AFFINE_DOUBLE_SIZE, 0x00000300
.set OAM_MOSAIC_OFF, 0x00000000
.set OAM_MOSAIC_ON, 0x00001000
.set OAM_4BPP, 0x00000000
.set OAM_8BPP, 0x00002000
.set OAM_H_FLIP, 0x10000000
.set OAM_V_FLIP, 0x20000000
.set OAM_SQUARE, 0x00000000
.set OAM_H_RECTANGLE, 0x00004000
.set OAM_V_RECTANGLE, 0x00008000
.set OAM_SIZE_0, 0x00000000
.set OAM_SIZE_1, 0x40000000
.set OAM_SIZE_2, 0x80000000
.set OAM_SIZE_3, 0xc0000000
.set OAM_SIZE_8x8, OAM_SIZE_0 | OAM_SQUARE
.set OAM_SIZE_16x16, OAM_SIZE_1 | OAM_SQUARE
.set OAM_SIZE_32x32, OAM_SIZE_2 | OAM_SQUARE
.set OAM_SIZE_64x64, OAM_SIZE_3 | OAM_SQUARE
.set OAM_SIZE_16x8, OAM_SIZE_0 | OAM_H_RECTANGLE
.set OAM_SIZE_32x8, OAM_SIZE_1 | OAM_H_RECTANGLE
.set OAM_SIZE_32x16, OAM_SIZE_2 | OAM_H_RECTANGLE
.set OAM_SIZE_64x32, OAM_SIZE_3 | OAM_H_RECTANGLE
.set OAM_SIZE_8x16, OAM_SIZE_0 | OAM_V_RECTANGLE
.set OAM_SIZE_8x32, OAM_SIZE_1 | OAM_V_RECTANGLE
.set OAM_SIZE_16x32, OAM_SIZE_2 | OAM_V_RECTANGLE
.set OAM_SIZE_32x64, OAM_SIZE_3 | OAM_V_RECTANGLE

22
constants/global.inc Normal file
View File

@ -0,0 +1,22 @@
.set TRUE, 1
.set FALSE, 0
.set NULL, 0
.set SPRITE_SIZE_8x8, (OAM_SIZE_0 >> 28) | (OAM_SQUARE >> 14)
.set SPRITE_SIZE_16x16, (OAM_SIZE_1 >> 28) | (OAM_SQUARE >> 14)
.set SPRITE_SIZE_32x32, (OAM_SIZE_2 >> 28) | (OAM_SQUARE >> 14)
.set SPRITE_SIZE_64x64, (OAM_SIZE_3 >> 28) | (OAM_SQUARE >> 14)
.set SPRITE_SIZE_16x8, (OAM_SIZE_0 >> 28) | (OAM_H_RECTANGLE >> 14)
.set SPRITE_SIZE_32x8, (OAM_SIZE_1 >> 28) | (OAM_H_RECTANGLE >> 14)
.set SPRITE_SIZE_32x16, (OAM_SIZE_2 >> 28) | (OAM_H_RECTANGLE >> 14)
.set SPRITE_SIZE_64x32, (OAM_SIZE_3 >> 28) | (OAM_H_RECTANGLE >> 14)
.set SPRITE_SIZE_8x16, (OAM_SIZE_0 >> 28) | (OAM_V_RECTANGLE >> 14)
.set SPRITE_SIZE_8x32, (OAM_SIZE_1 >> 28) | (OAM_V_RECTANGLE >> 14)
.set SPRITE_SIZE_16x32, (OAM_SIZE_2 >> 28) | (OAM_V_RECTANGLE >> 14)
.set SPRITE_SIZE_32x64, (OAM_SIZE_3 >> 28) | (OAM_V_RECTANGLE >> 14)
.set OBJ_IMAGE_ANIM_H_FLIP, 1 << 6
.set OBJ_IMAGE_ANIM_V_FLIP, 1 << 7

199
constants/m4a_constants.inc Normal file
View File

@ -0,0 +1,199 @@
.equiv ID_NUMBER, 0x68736d53
.equiv PCM_DMA_BUF_SIZE, 1584
.equiv TONEDATA_TYPE_CGB, 0x07
.equiv TONEDATA_TYPE_FIX, 0x08
.equiv TONEDATA_TYPE_SPL, 0x40 @ key split
.equiv TONEDATA_TYPE_RHY, 0x80 @ rhythm
.macro struct_begin
.struct 0
.endm
.macro struct_field name, size
\name:
.struct \name + \size
.endm
struct_begin
struct_field o_SoundInfo_ident, 4
struct_field o_SoundInfo_pcmDmaCounter, 1
struct_field o_SoundInfo_reverb, 1
struct_field o_SoundInfo_maxChans, 1
struct_field o_SoundInfo_masterVolume, 1
struct_field o_SoundInfo_freq, 1
struct_field o_SoundInfo_mode, 1
struct_field o_SoundInfo_c15, 1
struct_field o_SoundInfo_pcmDmaPeriod, 1
struct_field o_SoundInfo_maxLines, 1
struct_field o_SoundInfo_gap, 3
struct_field o_SoundInfo_pcmSamplesPerVBlank, 4
struct_field o_SoundInfo_pcmFreq, 4
struct_field o_SoundInfo_divFreq, 4
struct_field o_SoundInfo_cgbChans, 4
struct_field o_SoundInfo_func, 4
struct_field o_SoundInfo_intp, 4
struct_field o_SoundInfo_CgbSound, 4
struct_field o_SoundInfo_CgbOscOff, 4
struct_field o_SoundInfo_MidiKeyToCgbFreq, 4
struct_field o_SoundInfo_MPlayJumpTable, 4
struct_field o_SoundInfo_plynote, 4
struct_field o_SoundInfo_ExtVolPit, 4
struct_field o_SoundInfo_gap2, 16
struct_field o_SoundInfo_chans, 768
struct_field o_SoundInfo_pcmBuffer, PCM_DMA_BUF_SIZE * 2
struct_field SoundInfo_size, 0
struct_begin
struct_field o_SoundChannel_status, 1
struct_field o_SoundChannel_type, 1
struct_field o_SoundChannel_rightVolume, 1
struct_field o_SoundChannel_leftVolume, 1
struct_field o_SoundChannel_attack, 1
struct_field o_SoundChannel_decay, 1
struct_field o_SoundChannel_sustain, 1
struct_field o_SoundChannel_release, 1
struct_field o_SoundChannel_ky, 1
struct_field o_SoundChannel_ev, 1
struct_field o_SoundChannel_er, 1
struct_field o_SoundChannel_el, 1
struct_field o_SoundChannel_iev, 1
struct_field o_SoundChannel_iel, 1
struct_field o_SoundChannel_d1, 1
struct_field o_SoundChannel_d2, 1
struct_field o_SoundChannel_gt, 1
struct_field o_SoundChannel_mk, 1
struct_field o_SoundChannel_ve, 1
struct_field o_SoundChannel_pr, 1
struct_field o_SoundChannel_rp, 1
struct_field o_SoundChannel_d3, 3
struct_field o_SoundChannel_ct, 4
struct_field o_SoundChannel_fw, 4
struct_field o_SoundChannel_freq, 4
struct_field o_SoundChannel_wav, 4
struct_field o_SoundChannel_cp, 4
struct_field o_SoundChannel_track, 4
struct_field o_SoundChannel_pp, 4
struct_field o_SoundChannel_np, 4
struct_field o_SoundChannel_d4, 4
struct_field o_SoundChannel_xpi, 2
struct_field o_SoundChannel_xpc, 2
struct_field SoundChannel_size, 0
struct_begin
struct_field o_MusicPlayerTrack_flags, 1
struct_field o_MusicPlayerTrack_wait, 1
struct_field o_MusicPlayerTrack_patternLevel, 1
struct_field o_MusicPlayerTrack_repN, 1
struct_field o_MusicPlayerTrack_gateTime, 1
struct_field o_MusicPlayerTrack_key, 1
struct_field o_MusicPlayerTrack_velocity, 1
struct_field o_MusicPlayerTrack_runningStatus, 1
struct_field o_MusicPlayerTrack_keyM, 1
struct_field o_MusicPlayerTrack_pitM, 1
struct_field o_MusicPlayerTrack_keyShift, 1
struct_field o_MusicPlayerTrack_keyShiftX, 1
struct_field o_MusicPlayerTrack_tune, 1
struct_field o_MusicPlayerTrack_pitX, 1
struct_field o_MusicPlayerTrack_bend, 1
struct_field o_MusicPlayerTrack_bendRange, 1
struct_field o_MusicPlayerTrack_volMR, 1
struct_field o_MusicPlayerTrack_volML, 1
struct_field o_MusicPlayerTrack_vol, 1
struct_field o_MusicPlayerTrack_volX, 1
struct_field o_MusicPlayerTrack_pan, 1
struct_field o_MusicPlayerTrack_panX, 1
struct_field o_MusicPlayerTrack_modM, 1
struct_field o_MusicPlayerTrack_mod, 1
struct_field o_MusicPlayerTrack_modT, 1
struct_field o_MusicPlayerTrack_lfoSpeed, 1
struct_field o_MusicPlayerTrack_lfoSpeedC, 1
struct_field o_MusicPlayerTrack_lfoDelay, 1
struct_field o_MusicPlayerTrack_lfoDelayC, 1
struct_field o_MusicPlayerTrack_priority, 1
struct_field o_MusicPlayerTrack_echoVolume, 1
struct_field o_MusicPlayerTrack_echoLength, 1
struct_field o_MusicPlayerTrack_chan, 4
struct_field o_MusicPlayerTrack_ToneData_type, 1
struct_field o_MusicPlayerTrack_ToneData_key, 1
struct_field o_MusicPlayerTrack_ToneData_length, 1
struct_field o_MusicPlayerTrack_ToneData_pan_sweep, 1
struct_field o_MusicPlayerTrack_ToneData_wav, 4
struct_field o_MusicPlayerTrack_ToneData_attack, 1
struct_field o_MusicPlayerTrack_ToneData_decay, 1
struct_field o_MusicPlayerTrack_ToneData_sustain, 1
struct_field o_MusicPlayerTrack_ToneData_release, 1
struct_field o_MusicPlayerTrack_gap, 10
struct_field o_MusicPlayerTrack_unk_3A, 2
struct_field o_MusicPlayerTrack_unk_3C, 4
struct_field o_MusicPlayerTrack_cmdPtr, 4
struct_field o_MusicPlayerTrack_patternStack, 12
struct_field MusicPlayerTrack_size, 0
.equiv o_MusicPlayerTrack_ToneData_keySplitTable, o_MusicPlayerTrack_ToneData_attack
struct_begin
struct_field o_MusicPlayerInfo_songHeader, 4
struct_field o_MusicPlayerInfo_status, 4
struct_field o_MusicPlayerInfo_trackCount, 1
struct_field o_MusicPlayerInfo_priority, 1
struct_field o_MusicPlayerInfo_cmd, 1
struct_field o_MusicPlayerInfo_unk_B, 1
struct_field o_MusicPlayerInfo_clock, 4
struct_field o_MusicPlayerInfo_gap, 8
struct_field o_MusicPlayerInfo_memAccArea, 4
struct_field o_MusicPlayerInfo_tempoD, 2
struct_field o_MusicPlayerInfo_tempoU, 2
struct_field o_MusicPlayerInfo_tempoI, 2
struct_field o_MusicPlayerInfo_tempoC, 2
struct_field o_MusicPlayerInfo_fadeOI, 2
struct_field o_MusicPlayerInfo_fadeOC, 2
struct_field o_MusicPlayerInfo_fadeOV, 2
struct_field o_MusicPlayerInfo_padding, 2
struct_field o_MusicPlayerInfo_tracks, 4
struct_field o_MusicPlayerInfo_tone, 4
struct_field o_MusicPlayerInfo_ident, 4
struct_field o_MusicPlayerInfo_func, 4
struct_field o_MusicPlayerInfo_intp, 4
struct_field MusicPlayerInfo_size, 0
struct_begin
struct_field o_CgbChannel_sf, 1
struct_field o_CgbChannel_ty, 1
struct_field o_CgbChannel_rightVolume, 1
struct_field o_CgbChannel_leftVolume, 1
struct_field o_CgbChannel_at, 1
struct_field o_CgbChannel_de, 1
struct_field o_CgbChannel_su, 1
struct_field o_CgbChannel_re, 1
struct_field o_CgbChannel_ky, 1
struct_field o_CgbChannel_ev, 1
struct_field o_CgbChannel_eg, 1
struct_field o_CgbChannel_ec, 1
struct_field o_CgbChannel_echoVolume, 1
struct_field o_CgbChannel_echoLength, 1
struct_field o_CgbChannel_d1, 1
struct_field o_CgbChannel_d2, 1
struct_field o_CgbChannel_gt, 1
struct_field o_CgbChannel_mk, 1
struct_field o_CgbChannel_ve, 1
struct_field o_CgbChannel_pr, 1
struct_field o_CgbChannel_rp, 1
struct_field o_CgbChannel_d3, 3
struct_field o_CgbChannel_d5, 1
struct_field o_CgbChannel_sg, 1
struct_field o_CgbChannel_n4, 1
struct_field o_CgbChannel_pan, 1
struct_field o_CgbChannel_panMask, 1
struct_field o_CgbChannel_mo, 1
struct_field o_CgbChannel_le, 1
struct_field o_CgbChannel_sw, 1
struct_field o_CgbChannel_fr, 4
struct_field o_CgbChannel_wp, 4
struct_field o_CgbChannel_cp, 4
struct_field o_CgbChannel_tp, 4
struct_field o_CgbChannel_pp, 4
struct_field o_CgbChannel_np, 4
struct_field o_CgbChannel_d4, 8
struct_field CgbChannel_size, 0

View File

@ -0,0 +1,35 @@
.set GROWTH_MEDIUM_FAST, 0x00
.set GROWTH_ERRATIC, 0x01
.set GROWTH_FLUCTUATING, 0x02
.set GROWTH_MEDIUM_SLOW, 0x03
.set GROWTH_FAST, 0x04
.set GROWTH_SLOW, 0x05
.set BODY_COLOR_RED, 0x00
.set BODY_COLOR_BLUE, 0x01
.set BODY_COLOR_YELLOW, 0x02
.set BODY_COLOR_GREEN, 0x03
.set BODY_COLOR_BLACK, 0x04
.set BODY_COLOR_BROWN, 0x05
.set BODY_COLOR_PURPLE, 0x06
.set BODY_COLOR_GRAY, 0x07
.set BODY_COLOR_WHITE, 0x08
.set BODY_COLOR_PINK, 0x09
.set F_SUMMARY_SCREEN_FLIP_SPRITE, 0x80
.set EVO_FRIENDSHIP, 0x0001 @ Pokémon levels up with friendship 220
.set EVO_FRIENDSHIP_DAY, 0x0002 @ Pokémon levels up during the day with friendship 220
.set EVO_FRIENDSHIP_NIGHT, 0x0003 @ Pokémon levels up at night with friendship 220
.set EVO_LEVEL, 0x0004 @ Pokémon reaches the specified level
.set EVO_TRADE, 0x0005 @ Pokémon is traded
.set EVO_TRADE_ITEM, 0x0006 @ Pokémon is traded while it's holding the specified item
.set EVO_ITEM, 0x0007 @ specified item is used on Pokémon
.set EVO_LEVEL_ATK_GT_DEF, 0x0008 @ Pokémon reaches the specified level with attack > defense
.set EVO_LEVEL_ATK_EQ_DEF, 0x0009 @ Pokémon reaches the specified level with attack = defense
.set EVO_LEVEL_ATK_LT_DEF, 0x000a @ Pokémon reaches the specified level with attack < defense
.set EVO_LEVEL_SILCOON, 0x000b @ Pokémon reaches the specified level with a Silcoon personality value
.set EVO_LEVEL_CASCOON, 0x000c @ Pokémon reaches the specified level with a Cascoon personality value
.set EVO_LEVEL_NINJASK, 0x000d @ Pokémon reaches the specified level (special value for Ninjask)
.set EVO_LEVEL_SHEDINJA, 0x000e @ Pokémon reaches the specified level (special value for Shedinja)
.set EVO_BEAUTY, 0x000f @ Pokémon levels up with beauty specified value

17717
data/data.s Normal file

File diff suppressed because it is too large Load Diff

1656
data/event_scripts.s Normal file

File diff suppressed because it is too large Load Diff

16022
emerald_jp.cfg Normal file

File diff suppressed because it is too large Load Diff

306
graph_search.py Normal file
View File

@ -0,0 +1,306 @@
import re
import os
import struct
import subprocess
import random
from collections import defaultdict
from typing import List, Dict, Tuple, Set
US_PATH = '../pokeemerald'
def elf_funcs(elf_path: str, unknown=True) -> Dict[str, Tuple[int, int]]: # Find all functions in an ELF file
p = subprocess.run(f'readelf -s -W {elf_path}', stdout=subprocess.PIPE, encoding='utf-8',
shell=True)
lines = p.stdout.split('\n')
elf_reg = re.compile(r'\s*\d+:\s([a-fA-F0-9]{8})\s+(\d+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)')
symbols = {} # Map function names to (address, size tuples)
for line in lines:
m = elf_reg.match(line)
if m and m.group(3) == 'FUNC': # Collect functions
addr, size, dtype, bind, vis, ndx, name = m.groups()
is_unknown = name[:4] == 'sub_'
if unknown is not None and is_unknown != unknown:
continue
addr = int(addr, 16)
size = int(size)
symbols[name] = (addr-1, size)
if unknown is True:
unk_str = 'unknown '
elif unknown is False:
unk_str = 'known '
else:
unk_str = ''
print(f'Found {len(symbols)} {unk_str}functions')
return symbols
# Builds an "instruction graph"
# Maps instructions to a dictionary
# That dictionary maps following instructions, to the places they follow at
# So if you have:
# 0 MOV r0, r1
# 2 BX lr
# {(MOV r0, r1): {(BX lr): {2}}}
def build_graph(rom: str, symbols: Dict[str, Tuple[int, int]]) -> Dict[int, Dict[int, Set[int]]]:
graph = defaultdict(lambda: defaultdict(set))
with open(rom, 'rb') as f:
count = 0
print('Graphed 0 functions', end='')
for name, (addr, size) in symbols.items():
f.seek(addr & 0xfffffe)
data = f.read(size)
instrs = [t[0] for t in struct.iter_unpack('<H', data)] # Unpack instructions as ints
assert len(instrs) == size // 2
last = None
for i, instr in enumerate(instrs):
cursor = addr + 2*i # Address of current instruction
graph[last][instr].add(cursor)
graph[None][instr].add(cursor)
last = instr
count += 1
if count % 100 == 0:
pass
print(f'\rGraphed {count} functions', end='')
if not size:
continue
last = None
for i, instr in enumerate(instrs):
curr = addr + 2*i
positions = graph[last][instr]
assert curr in positions
last = instr
print(f'\rGraphed {count} functions')
return graph
def calc_sizes(map_path: str) -> Dict[int, int]: # Calculate the size of *addresses*. Crude asf
with open(map_path, 'r') as f:
lines = list(line for line in f)
sizes = {} # Maps addresses to their sizes
reg = re.compile(r'\s*(0x[\da-fA-F]+)')
for l1, l2 in zip(lines[:-1], lines[1:]):
m1 = reg.match(l1)
if not m1:
continue
m2 = reg.match(l2)
if not m2:
continue
addr1, addr2 = int(m1.group(1), 16), int(m2.group(1), 16)
if not addr1 & 0xff000000 == addr2 & 0xff000000 == 0x08000000: # Only pay attention to ROM addresses
continue
size = addr2 - addr1
sizes[addr1] = size
return sizes
def asm_funcs(asm_path: str, sizes: Dict[int, int], unknown=True) -> Dict[str, Tuple[int, int]]:
reg = re.compile(r'(\w+): @ (0x[\da-fA-F]+)')
symbols = {} # Maps names to (address, size) tuples
with open(asm_path, 'r') as f:
for line in f:
m = reg.match(line)
if m:
is_unknown = m.group(1).startswith('sub_')
if unknown != is_unknown: # Only want certain functions
continue
addr = int(m.group(2), 16)
if addr not in sizes:
continue
symbols[m.group(1)] = (addr, sizes[addr])
print(f'Found {len(symbols)} {"unknown" if unknown else "known"} JP functions')
return symbols
def match_instrs(instrs: List[int], graph): # Match a sequence of instructions
matches = []
if not instrs:
return []
for base in graph[None][instrs[0]]:
last = instrs[0]
# print(f'Try: {base:08x}')
match_len = 1
for match_len, instr in enumerate(instrs[1:], 1):
addr = base + 2*match_len
potential = graph[last][instr]
# print(f'{instr:04x} {addr:08x} {not sequence_break}')
if addr not in potential: # Sequence is broken
break
last = instr
matches.append((match_len, base))
matches.sort(reverse=True) # Get the best match
if len(instrs) >= 10 and matches and len(instrs)-1 == matches[0][0]: # Perfect match
if not any(t[0] == matches[0][0] for t in matches[1:]): # No other perfect matches
return matches[:1]
return matches
def decode(instrs: List[int]): # Primitive, unfinished THUMB decoder
for op in instrs:
print(f'{op:04x}', end=' ')
if op & 0xf800 == 0x1800: # Add/subtract
opcode = (op >> 9) & 3
codes = ('ADD', 'SUB', 'ADD', 'SUB')
val = (op >> 6) & 7
rs = (op >> 3) & 7
rd = op & 7
flag = 'r' if opcode < 2 else '#'
print(f'{codes[opcode]} r{rd},r{rs},{flag}{val}')
elif op & 0xe000 == 0: # Move shifted register
opcode = (op >> 11) & 3
codes = ('LSL', 'LSR', 'ASR')
offset = (op >> 6) & 31
rs = (op >> 3) & 7
rd = op & 7
print(f'{codes[opcode]} r{rd},r{rs},#{offset}')
elif op & 0xe000 == 0x2000: # Add/sub immediate
opcode = (op >> 11) & 3
codes = ('MOV', 'CMP', 'ADD', 'SUB')
rd = (op >> 8) & 7
nn = op & 255
print(f'{codes[opcode]} r{rd},#{nn}')
elif op & 0xfc00 == 0x4000: # ALU ops
opcode = (op >> 6) & 15
codes = ('AND', 'EOR', 'LSL', 'LSR', 'ASR', 'ADC', 'SBC', 'ROR',
'TST', 'NEG', 'CMP', 'CMN', 'ORR', 'MUL', 'BIC', 'MVN')
rs = (op >> 3) & 7
rd = op & 7
print(f'{codes[opcode]} r{rd}, r{rs}')
elif op & 0xf800 == 0x4800: # Load PC-relative
r = (op >> 8) & 7
print(f'LDR r{r} PC+{4*(op & 255)}')
elif op & 0xf200 == 0x5200: # Load register offset
opcode = (op >> 10) & 3
codes = ('STR', 'STB', 'LDR', 'LDB')
ro = (op >> 6) & 7
rb = (op >> 3) & 7
rd = op & 7
print(f'{codes[opcode]} r{rd},[r{rb},r{ro}]')
elif op & 0xe000 == 0x6000: # Load immediate offset
opcode = (op >> 11) & 3
codes = ('STR', 'LDR', 'STB', 'LDB')
nn = (op >> 6) & 31
if opcode < 2:
nn *= 4
rb = (op >> 3) & 7
rd = op & 7
print(f'{codes[opcode]} r{rd},[r{rb},#{nn}]')
elif op & 0xfc00 == 0x4400: # High register ops
opcode = (op >> 8) & 3
codes = ('ADD', 'CMP', 'MOV', 'NOP', 'BX')
MSBd = (op >> 7) & 1
MSBs = (op >> 6) & 1
rs = (op >> 3) & 7
rd = op & 7
rs |= MSBs << 3
rd += MSBd << 3
if opcode == 3:
flag = 'BLX' if MSBd else 'BX'
print(f'{flag} r{rs}')
else:
print(f'{codes[opcode]} r{rd},r{rs}')
else:
print()
def match_iter(instrs, graph, slices=10): # Iteratively try to find a match
matches = match_instrs(instrs, graph) # Try to match the entire sequence first
if len(matches) == 1: # One perfect match
return matches[0][1]
elif not matches: # No match anywhere
return None
potential = {t[1] for t in matches} # All potential matches
for _ in range(slices): # Slice the list randomly and search for matches
start = random.randint(1, len(instrs))
end = random.randint(1, len(instrs))
if start == end:
end += 1
elif end < start:
start, end = end, start
matches = match_instrs(instrs[start:end], graph)
min_len = max((end - start) // 2, 1)
new_set = {t[1]-2*start for t in matches if t[0] >= min_len}
potential = potential & new_set # Take intersection of old and new locations
if not potential:
return None
elif len(potential) == 1:
addr = potential.pop()
return addr
return None
def match_loop(rom: str, unknown, known: dict, graph) -> Dict[str, str]: # Interactive
rv_known = {addr: name for name, (addr, size) in known.items()}
print(f'Pool: {len(rv_known)}')
keys = list(unknown.keys())
random.shuffle(keys)
unk_exp = re.compile(r'sub_[\dA-Fa-f]+')
replace: Dict[str, str] = {} # Maps old names to new names
# print('At any time, type "quit" to quit and replace all correct names')
match_count = 0
print('Matched 0 functions', end='', flush=True)
with open(rom, 'rb') as f:
for name in keys:
addr, size = unknown[name]
if not size:
continue
f.seek(addr & 0xfffffe)
data = f.read(size)
instrs = [t[0] for t in struct.iter_unpack('<H', data)]
for i in range(len(instrs)-1, -1, -1): # Try to remove trailing 'data'
if instrs[i] in (0x4770, 0x4700, 0x4708): # BX lr, BX r0, BX r1
instrs = instrs[:i+1]
break
# print(f'Trying {name} ({len(instrs)} instructions)')
match = match_iter(instrs, graph)
if match and match in rv_known:
if unk_exp.match(rv_known[match]): # Don't rename to an unknown function
continue
replace[name] = rv_known[match]
rv_known.pop(match) # Remove the match from the pool
match_count += 1
if match_count % 10 == 0:
print(f'\rMatched {match_count} functions', end='')
# print(f'Matched {name} ({addr:08x}) with {rv_known[match]} ({match:08x})')
# try:
# command = input('Replace? (y/N): ')
# except KeyboardInterrupt:
# command = 'N'
# if 'quit' in command:
# break
# elif command in ('y', 'Y'):
# replace[name] = rv_known[match]
print(f'\rMatched {match_count} functions')
return replace
def replace_names(asm_path: str, replace: Dict[str, str]): # Replace lines
with open(asm_path, 'r') as f_in:
count = 0
asm = f_in.read() # Read in the entire file
print(f'Replaced {count} names', end='', flush=True)
for old_name, new_name in replace.items():
asm = asm.replace(old_name, new_name)
count += 1
if count % 100 == 0:
print(f'\rReplaced {count} names', end='')
print(f'\rReplaced {count} names')
with open(asm_path, 'w') as f_out:
f_out.write(asm)
JP_MAP = 'pokeemeraldjp.map'
JP_ASM = os.path.join('asm', 'code.s')
US_ELF = os.path.join('..', 'pokeemerald', 'pokeemerald.elf')
US_ROM = os.path.join('..', 'pokeemerald', 'pokeemerald.gba')
JP_ROM = 'pokeemeraldjp.gba'
JP_ELF = 'pokeemerald_jp.elf'
if __name__ == '__main__':
jp_unknown = elf_funcs(JP_ELF, True)
us_funcs = elf_funcs(US_ELF, None) # ALL functions, including unknown ones
graph = build_graph(US_ROM, us_funcs) # Build instruction graph
replace = match_loop(JP_ROM, jp_unknown, us_funcs, graph)
if replace:
replace_names(JP_ASM, replace)

130
ld_script_jp.txt Normal file
View File

@ -0,0 +1,130 @@
SECTIONS {
. = 0x2000000;
ewram (NOLOAD) :
ALIGN(4)
{
INCLUDE "sym_ewram_jp.txt"
}
. = 0x3000000;
iwram (NOLOAD) :
ALIGN(4)
{
INCLUDE "sym_iwram_jp.txt"
}
. = 0x8000000;
.text :
ALIGN(4)
{
asm/rom.o(.text);
} =0
script_data :
ALIGN(4)
{
data/event_scripts.o(script_data);
} =0
libtext :
ALIGN(4)
{
asm/libgcnmultiboot.o(.text);
asm/m4a.o(.text);
asm/agb_flash.o(.text);
asm/siirtc.o(.text);
asm/librfu_stwi.o(.text);
asm/librfu_intr.o(.text);
asm/librfu_rfu.o(.text);
asm/librfu.o(.text);
asm/libisagbprn.o(.text);
asm/libagbsyscall.o(.text);
asm/libgcc.o(.text);
asm/libc.o(.text);
/*
*libgcc.a:_call_via_rX.o(.text);
*libgcc.a:_divdi3.o(.text);
*libgcc.a:_divsi3.o(.text);
*libgcc.a:_dvmd_tls.o(.text);
*libgcc.a:_fixunsdfsi.o(.text);
*libgcc.a:_fixunssfsi.o(.text);
*libgcc.a:_modsi3.o(.text);
*libgcc.a:_muldi3.o(.text);
*libgcc.a:_udivdi3.o(.text);
*libgcc.a:_udivsi3.o(.text);
*libgcc.a:_umodsi3.o(.text);
*libgcc.a:dp-bit.o(.text);
*libgcc.a:fp-bit.o(.text);
*libgcc.a:_lshrdi3.o(.text);
*libgcc.a:_negdi2.o(.text);
*libc.a:memcpy.o(.text);
*libc.a:memset.o(.text);
*libc.a:strcmp.o(.text);
*libc.a:strcpy.o(.text);
*libc.a:impure.o(.text);
*libc.a:vsprintf.o(.text);
*libc.a:vfprintf.o(.text);
*libc.a:wsetup.o(.text);
*libc.a:dtoa.o(.text);
*libc.a:fflush.o(.text);
*libc.a:findfp.o(.text);
*libc.a:freer.o(.text);
*libc.a:mtrim.o(.text);
*libc.a:fvwrite.o(.text);
*libc.a:fwalk.o(.text);
*libc.a:locale.o(.text);
*libc.a:makebuf.o(.text);
*libc.a:mallocr.o(.text);
*libc.a:mbtowc_r.o(.text);
*libc.a:memchr.o(.text);
*libc.a:memmove.o(.text);
*libc.a:mlock.o(.text);
*libc.a:mprec.o(.text);
*libc.a:s_isinf.o(.text);
*libc.a:s_isnan.o(.text);
*libc.a:sbrkr.o(.text);
*libc.a:stdio.o(.text);
*libc.a:strlen.o(.text);
*libc.a:syscalls.o(.text);
*libc.a:writer.o(.text);
*libc.a:callocr.o(.text);
*libc.a:closer.o(.text);
*libc.a:errno.o(.text);
*libc.a:fstatr.o(.text);
*libc.a:libcfunc.o(.text);
*libc.a:lseekr.o(.text);
*libc.a:readr.o(.text);
*/
} =0
.rodata :
ALIGN(4)
{
data/data.o(.rodata);
} =0
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* Discard everything not specifically mentioned above. */
/DISCARD/ :
{
*(*);
}
}

1
rom_jp.sha1 Normal file
View File

@ -0,0 +1 @@
d7cf8f156ba9c455d164e1ea780a6bf1945465c2 pokeemerald_jp.gba

1630
sym_ewram_jp.txt Normal file

File diff suppressed because it is too large Load Diff

790
sym_iwram_jp.txt Normal file
View File

@ -0,0 +1,790 @@
gUnknown_3000000 = .;
. += 0x4;
gUnknown_3000004 = .;
. += 0x4;
gUnknown_3000008 = .;
. += 0x8;
gUnknown_3000010 = .;
. += 0xc;
gUnknown_300001C = .;
. += 0xd4;
gUnknown_30000F0 = .;
. += 0xa;
gUnknown_30000FA = .;
. += 0x716;
gUnknown_3000810 = .;
. += 0x1;
gUnknown_3000811 = .;
. += 0x7;
gUnknown_3000818 = .;
. += 0x4;
gUnknown_300081C = .;
. += 0x5c;
gUnknown_3000878 = .;
. += 0x60;
gUnknown_30008D8 = .;
. += 0x1;
gUnknown_30008D9 = .;
. += 0x1;
gUnknown_30008DA = .;
. += 0x6;
gUnknown_30008E0 = .;
. += 0x18;
gUnknown_30008F8 = .;
. += 0x4;
gUnknown_30008FC = .;
. += 0x3c;
gUnknown_3000938 = .;
. += 0x10;
gUnknown_3000948 = .;
. += 0xa2;
gUnknown_30009EA = .;
. += 0x2;
gUnknown_30009EC = .;
. += 0x2;
gUnknown_30009EE = .;
. += 0x2;
gUnknown_30009F0 = .;
. += 0x80;
gUnknown_3000A70 = .;
. += 0x100;
gUnknown_3000B70 = .;
. += 0x180;
gUnknown_3000CF0 = .;
. += 0x20;
gUnknown_3000D10 = .;
. += 0x10;
gUnknown_3000D20 = .;
. += 0x30;
gUnknown_3000D50 = .;
. += 0x4;
gUnknown_3000D54 = .;
. += 0x4;
gUnknown_3000D58 = .;
. += 0x4;
gUnknown_3000D5C = .;
. += 0x4;
gUnknown_3000D60 = .;
. += 0x4;
gUnknown_3000D64 = .;
. += 0x4;
gUnknown_3000D68 = .;
. += 0x4;
gUnknown_3000D6C = .;
. += 0x1;
gUnknown_3000D6D = .;
. += 0x1;
gUnknown_3000D6E = .;
. += 0x2;
gUnknown_3000D70 = .;
. += 0x2;
gUnknown_3000D72 = .;
. += 0x1;
gUnknown_3000D73 = .;
. += 0x1;
gUnknown_3000D74 = .;
. += 0x4;
gUnknown_3000D78 = .;
. += 0x8;
gUnknown_3000D80 = .;
. += 0x10;
gUnknown_3000D90 = .;
. += 0x10;
gUnknown_3000DA0 = .;
. += 0x4;
gUnknown_3000DA4 = .;
. += 0x4;
gUnknown_3000DA8 = .;
. += 0x4;
gUnknown_3000DAC = .;
. += 0x4;
gUnknown_3000DB0 = .;
. += 0x8;
gUnknown_3000DB8 = .;
. += 0x8;
gUnknown_3000DC0 = .;
. += 0xc;
gUnknown_3000DCC = .;
. += 0x2;
gUnknown_3000DCE = .;
. += 0x2;
gUnknown_3000DD0 = .;
. += 0x4;
gUnknown_3000DD4 = .;
. += 0x4;
gUnknown_3000DD8 = .;
. += 0x4;
gUnknown_3000DDC = .;
. += 0x4;
gUnknown_3000DE0 = .;
. += 0x8;
gUnknown_3000DE8 = .;
. += 0x10;
gUnknown_3000DF8 = .;
. += 0xc;
gUnknown_3000E04 = .;
. += 0x2;
gUnknown_3000E06 = .;
. += 0x2;
gUnknown_3000E08 = .;
. += 0x4;
gUnknown_3000E0C = .;
. += 0x4;
gUnknown_3000E10 = .;
. += 0x4;
gUnknown_3000E14 = .;
. += 0x4;
gUnknown_3000E18 = .;
. += 0x1;
gUnknown_3000E19 = .;
. += 0x7;
gUnknown_3000E20 = .;
. += 0x8;
gUnknown_3000E28 = .;
. += 0x2;
gUnknown_3000E2A = .;
. += 0x2;
gUnknown_3000E2C = .;
. += 0x4;
gUnknown_3000E30 = .;
. += 0x8;
gUnknown_3000E38 = .;
. += 0x8;
gUnknown_3000E40 = .;
. += 0x78;
gUnknown_3000EB8 = .;
. += 0x74;
gUnknown_3000F2C = .;
. += 0x4;
gUnknown_3000F30 = .;
. += 0x4;
gUnknown_3000F34 = .;
. += 0x2;
gUnknown_3000F36 = .;
. += 0x2;
gUnknown_3000F38 = .;
. += 0x2;
gUnknown_3000F3A = .;
. += 0x2;
gUnknown_3000F3C = .;
. += 0x4;
gUnknown_3000F40 = .;
. += 0x4;
gUnknown_3000F44 = .;
. += 0x4;
gUnknown_3000F48 = .;
. += 0x2;
gUnknown_3000F4A = .;
. += 0x2;
gUnknown_3000F4C = .;
. += 0x1;
gUnknown_3000F4D = .;
. += 0x1;
gUnknown_3000F4E = .;
. += 0x2;
gUnknown_3000F50 = .;
. += 0x8;
gUnknown_3000F58 = .;
. += 0x20;
gUnknown_3000F78 = .;
. += 0x188;
gUnknown_3001100 = .;
. += 0x1;
gUnknown_3001101 = .;
. += 0x1;
gUnknown_3001102 = .;
. += 0x6;
gUnknown_3001108 = .;
. += 0x1c;
gUnknown_3001124 = .;
. += 0xc;
gUnknown_3001130 = .;
. += 0x4;
gUnknown_3001134 = .;
. += 0x4;
gUnknown_3001138 = .;
. += 0x4;
gUnknown_300113C = .;
. += 0x4;
gUnknown_3001140 = .;
. += 0x4;
gUnknown_3001144 = .;
. += 0x4;
gUnknown_3001148 = .;
. += 0x4;
gUnknown_300114C = .;
. += 0x4;
gUnknown_3001150 = .;
. += 0x4;
gUnknown_3001154 = .;
. += 0x4;
gUnknown_3001158 = .;
. += 0x4;
gUnknown_300115C = .;
. += 0x4;
gUnknown_3001160 = .;
. += 0x8;
gUnknown_3001168 = .;
. += 0xc;
gUnknown_3001174 = .;
. += 0x1;
gUnknown_3001175 = .;
. += 0x1;
gUnknown_3001176 = .;
. += 0x2;
gUnknown_3001178 = .;
. += 0x4;
gUnknown_300117C = .;
. += 0x4;
gUnknown_3001180 = .;
. += 0x8;
gUnknown_3001188 = .;
. += 0x68;
gUnknown_30011F0 = .;
. += 0x2;
gUnknown_30011F2 = .;
. += 0x2;
gUnknown_30011F4 = .;
. += 0x2;
gUnknown_30011F6 = .;
. += 0x1;
gUnknown_30011F7 = .;
. += 0x1;
gUnknown_30011F8 = .;
. += 0x4;
gUnknown_30011FC = .;
. += 0x2;
gUnknown_30011FE = .;
. += 0x2;
gUnknown_3001200 = .;
. += 0x2;
gUnknown_3001202 = .;
. += 0x2;
gUnknown_3001204 = .;
. += 0x4;
gUnknown_3001208 = .;
. += 0x2;
gUnknown_300120A = .;
. += 0x1;
gUnknown_300120B = .;
. += 0x5;
gUnknown_3001210 = .;
. += 0x30;
gUnknown_3001240 = .;
. += 0x30;
gUnknown_3001270 = .;
. += 0x4;
gUnknown_3001274 = .;
. += 0x4;
gUnknown_3001278 = .;
. += 0x4;
gUnknown_300127C = .;
. += 0x4;
gUnknown_3001280 = .;
. += 0x4;
gUnknown_3001284 = .;
. += 0x4;
gUnknown_3001288 = .;
. += 0x1;
gUnknown_3001289 = .;
. += 0x1;
gUnknown_300128A = .;
. += 0x2;
gUnknown_300128C = .;
. += 0x4;
gUnknown_3001290 = .;
. += 0x4;
gUnknown_3001294 = .;
. += 0x4;
gUnknown_3001298 = .;
. += 0x8;
gUnknown_30012A0 = .;
. += 0x10;
gUnknown_30012B0 = .;
. += 0x4;
gUnknown_30012B4 = .;
. += 0x4;
gUnknown_30012B8 = .;
. += 0x8;
gUnknown_30012C0 = .;
. += 0x18;
gUnknown_30012D8 = .;
. += 0x2;
gUnknown_30012DA = .;
. += 0x2;
gUnknown_30012DC = .;
. += 0x2;
gUnknown_30012DE = .;
. += 0x2;
gUnknown_30012E0 = .;
. += 0x4;
gUnknown_30012E4 = .;
. += 0x2;
gUnknown_30012E6 = .;
. += 0x2;
gUnknown_30012E8 = .;
. += 0x2;
gUnknown_30012EA = .;
. += 0x2;
gUnknown_30012EC = .;
. += 0x4;
gUnknown_30012F0 = .;
. += 0x2;
gUnknown_30012F2 = .;
. += 0x2;
gUnknown_30012F4 = .;
. += 0x4;
gUnknown_30012F8 = .;
. += 0x770;
gUnknown_3001A68 = .;
. += 0x2;
gUnknown_3001A6A = .;
. += 0x2;
gUnknown_3001A6C = .;
. += 0x4;
gUnknown_3001A70 = .;
. += 0x6;
gUnknown_3001A76 = .;
. += 0x2;
gUnknown_3001A78 = .;
. += 0x18;
gUnknown_3001A90 = .;
. += 0x10;
gUnknown_3001AA0 = .;
. += 0x4;
gUnknown_3001AA4 = .;
. += 0x4;
gUnknown_3001AA8 = .;
. += 0x4;
gUnknown_3001AAC = .;
. += 0x4;
gUnknown_3001AB0 = .;
. += 0xa0;
gUnknown_3001B50 = .;
. += 0x1;
gUnknown_3001B51 = .;
. += 0x7ff;
gUnknown_3002350 = .;
. += 0x4;
gUnknown_3002354 = .;
. += 0xc;
gUnknown_3002360 = .;
. += 0x2c;
gUnknown_300238C = .;
. += 0xc;
gUnknown_3002398 = .;
. += 0x200;
gUnknown_3002598 = .;
. += 0x200;
gUnknown_3002798 = .;
. += 0x4;
gUnknown_300279C = .;
. += 0x4;
gUnknown_30027A0 = .;
. += 0x10;
gUnknown_30027B0 = .;
. += 0x4;
gUnknown_30027B4 = .;
. += 0x18;
gUnknown_30027CC = .;
. += 0x1c;
gUnknown_30027E8 = .;
. += 0x8;
gUnknown_30027F0 = .;
. += 0x800;
gUnknown_3002FF0 = .;
. += 0x4;
gUnknown_3002FF4 = .;
. += 0xc;
gUnknown_3003000 = .;
. += 0x10;
gUnknown_3003010 = .;
. += 0x10;
gUnknown_3003020 = .;
. += 0x4;
gUnknown_3003024 = .;
. += 0xc;
gUnknown_3003030 = .;
. += 0x20;
gUnknown_3003050 = .;
. += 0x20;
gUnknown_3003070 = .;
. += 0x20;
gUnknown_3003090 = .;
. += 0x24;
gUnknown_30030B4 = .;
. += 0x4;
gUnknown_30030B8 = .;
. += 0x4;
gUnknown_30030BC = .;
. += 0x4;
gUnknown_30030C0 = .;
. += 0xc;
gUnknown_30030CC = .;
. += 0x4;
gUnknown_30030D0 = .;
. += 0x3c;
gUnknown_300310C = .;
. += 0x4;
gUnknown_3003110 = .;
. += 0x8;
gUnknown_3003118 = .;
. += 0x4;
gUnknown_300311C = .;
. += 0x8;
gUnknown_3003124 = .;
. += 0xc;
gUnknown_3003130 = .;
. += 0x2;
gUnknown_3003132 = .;
. += 0x2;
gUnknown_3003134 = .;
. += 0x4c;
gUnknown_3003180 = .;
. += 0x4;
gUnknown_3003184 = .;
. += 0x4;
gUnknown_3003188 = .;
. += 0x4;
gUnknown_300318C = .;
. += 0x4;
gUnknown_3003190 = .;
. += 0x4;
gUnknown_3003194 = .;
. += 0x4;
gUnknown_3003198 = .;
. += 0x4;
gUnknown_300319C = .;
. += 0x4;
gUnknown_30031A0 = .;
. += 0x10;
gUnknown_30031B0 = .;
. += 0x10;
gUnknown_30031C0 = .;
. += 0x4;
gUnknown_30031C4 = .;
. += 0xc;
gUnknown_30031D0 = .;
. += 0x10;
gUnknown_30031E0 = .;
. += 0x4;
gUnknown_30031E4 = .;
. += 0x4;
gUnknown_30031E8 = .;
. += 0x8;
gUnknown_30031F0 = .;
. += 0x10;
gUnknown_3003200 = .;
. += 0x10;
gUnknown_3003210 = .;
. += 0x4;
gUnknown_3003214 = .;
. += 0xfbc;
gUnknown_30041D0 = .;
. += 0x4;
gUnknown_30041D4 = .;
. += 0xc;
gUnknown_30041E0 = .;
. += 0x10;
gUnknown_30041F0 = .;
. += 0x40;
gUnknown_3004230 = .;
. += 0xe70;
gUnknown_30050A0 = .;
. += 0x14;
gUnknown_30050B4 = .;
. += 0x58;
gUnknown_300510C = .;
. += 0x2;
gUnknown_300510E = .;
. += 0x76;
gUnknown_3005184 = .;
. += 0xe;
gUnknown_3005192 = .;
. += 0x18;
gUnknown_30051AA = .;
. += 0x1a;
gUnknown_30051C4 = .;
. += 0x57c;
gUnknown_3005740 = .;
. += 0x29f;
gUnknown_30059DF = .;
. += 0x47;
gUnknown_3005A26 = .;
. += 0x3;
gUnknown_3005A29 = .;
. += 0x4;
gUnknown_3005A2D = .;
. += 0x9;
gUnknown_3005A36 = .;
. += 0x1a;
gUnknown_3005A50 = .;
. += 0x10;
gUnknown_3005A60 = .;
. += 0x4;
gUnknown_3005A64 = .;
. += 0xc;
gUnknown_3005A70 = .;
. += 0x2a;
gUnknown_3005A9A = .;
. += 0xc;
gUnknown_3005AA6 = .;
. += 0xe;
gUnknown_3005AB4 = .;
. += 0xc;
gUnknown_3005AC0 = .;
. += 0x10;
gUnknown_3005AD0 = .;
. += 0x4;
gUnknown_3005AD4 = .;
. += 0x4;
gUnknown_3005AD8 = .;
. += 0x4;
gUnknown_3005ADC = .;
. += 0x4;
gUnknown_3005AE0 = .;
. += 0x4;
gUnknown_3005AE4 = .;
. += 0x4;
gUnknown_3005AE8 = .;
. += 0x4;
gUnknown_3005AEC = .;
. += 0x4;
gUnknown_3005AF0 = .;
. += 0x4;
gUnknown_3005AF4 = .;
. += 0x4;
gUnknown_3005AF8 = .;
. += 0x4;
gUnknown_3005AFC = .;
. += 0x4;
gUnknown_3005B00 = .;
. += 0x4;
gUnknown_3005B04 = .;
. += 0x4;
gUnknown_3005B08 = .;
. += 0x4;
gUnknown_3005B0C = .;
. += 0x4;
gUnknown_3005B10 = .;
. += 0x4;
gUnknown_3005B14 = .;
. += 0x4;
gUnknown_3005B18 = .;
. += 0x8;
gUnknown_3005B20 = .;
. += 0x10;
gUnknown_3005B30 = .;
. += 0x18;
gUnknown_3005B48 = .;
. += 0x4;
gUnknown_3005B4C = .;
. += 0x4;
gUnknown_3005B50 = .;
. += 0x4;
gUnknown_3005B54 = .;
. += 0x4;
gUnknown_3005B58 = .;
. += 0x8;
gUnknown_3005B60 = .;
. += 0x8;
gUnknown_3005B68 = .;
. += 0x278;
gUnknown_3005DE0 = .;
. += 0x4;
gUnknown_3005DE4 = .;
. += 0xc;
gUnknown_3005DF0 = .;
. += 0x18;
gUnknown_3005E08 = .;
. += 0x4;
gUnknown_3005E0C = .;
. += 0x4;
gUnknown_3005E10 = .;
. += 0x4;
gUnknown_3005E14 = .;
. += 0x4;
gUnknown_3005E18 = .;
. += 0x4;
gUnknown_3005E1C = .;
. += 0x4;
gUnknown_3005E20 = .;
. += 0x10;
gUnknown_3005E30 = .;
. += 0x10;
gUnknown_3005E40 = .;
. += 0x10;
gUnknown_3005E50 = .;
. += 0x50;
gUnknown_3005EA0 = .;
. += 0x4;
gUnknown_3005EA4 = .;
. += 0x4;
gUnknown_3005EA8 = .;
. += 0x4;
gUnknown_3005EAC = .;
. += 0x4;
gUnknown_3005EB0 = .;
. += 0x4;
gUnknown_3005EB4 = .;
. += 0x4;
gUnknown_3005EB8 = .;
. += 0x4;
gUnknown_3005EBC = .;
. += 0x4;
gUnknown_3005EC0 = .;
. += 0x4;
gUnknown_3005EC4 = .;
. += 0xc;
gUnknown_3005ED0 = .;
. += 0x10;
gUnknown_3005EE0 = .;
. += 0x20;
gUnknown_3005F00 = .;
. += 0x4;
gUnknown_3005F04 = .;
. += 0xc;
gUnknown_3005F10 = .;
. += 0x18;
gUnknown_3005F28 = .;
. += 0x4;
gUnknown_3005F2C = .;
. += 0x4;
gUnknown_3005F30 = .;
. += 0x4;
gUnknown_3005F34 = .;
. += 0x4;
gUnknown_3005F38 = .;
. += 0x4;
gUnknown_3005F3C = .;
. += 0x4;
gUnknown_3005F40 = .;
. += 0x4;
gUnknown_3005F44 = .;
. += 0x4;
gUnknown_3005F48 = .;
. += 0x8;
gUnknown_3005F50 = .;
. += 0x4;
gUnknown_3005F54 = .;
. += 0xc;
gUnknown_3005F60 = .;
. += 0x74;
gUnknown_3005FD4 = .;
. += 0x4;
gUnknown_3005FD8 = .;
. += 0x8;
gUnknown_3005FE0 = .;
. += 0x10;
gUnknown_3005FF0 = .;
. += 0x2c;
gUnknown_300601C = .;
. += 0x4;
gUnknown_3006020 = .;
. += 0x4;
gUnknown_3006024 = .;
. += 0x4;
gUnknown_3006028 = .;
. += 0x4;
gUnknown_300602C = .;
. += 0x4;
gUnknown_3006030 = .;
. += 0x4;
gUnknown_3006034 = .;
. += 0xc;
gUnknown_3006040 = .;
. += 0x10;
gUnknown_3006050 = .;
. += 0x18;
gUnknown_3006068 = .;
. += 0x8;
gUnknown_3006070 = .;
. += 0x4;
gUnknown_3006074 = .;
. += 0x4;
gUnknown_3006078 = .;
. += 0x8;
gUnknown_3006080 = .;
. += 0x90;
gUnknown_3006110 = .;
. += 0x10;
gUnknown_3006120 = .;
. += 0x350;
gUnknown_3006470 = .;
. += 0xc60;
gUnknown_30070D0 = .;
. += 0x70;
gUnknown_3007140 = .;
. += 0x80;
gUnknown_30071C0 = .;
. += 0x4;
gUnknown_30071C4 = .;
. += 0x84;
gUnknown_3007248 = .;
. += 0x4;
gUnknown_300724C = .;
. += 0x4;
gUnknown_3007250 = .;
. += 0x100;
gUnknown_3007350 = .;
. += 0x140;
gUnknown_3007490 = .;
. += 0x40;
gUnknown_30074D0 = .;
. += 0x40;
gUnknown_3007510 = .;
. += 0x40;
gUnknown_3007550 = .;
. += 0x40;
gUnknown_3007590 = .;
. += 0x10;
gUnknown_30075A0 = .;
. += 0x40;
gUnknown_30075E0 = .;
. += 0x4;
gUnknown_30075E4 = .;
. += 0x4;
gUnknown_30075E8 = .;
. += 0x4;
gUnknown_30075EC = .;
. += 0x4;
gUnknown_30075F0 = .;
. += 0x4;
gUnknown_30075F4 = .;
. += 0x4;
gUnknown_30075F8 = .;
. += 0x4;
gUnknown_30075FC = .;
. += 0x4;
gUnknown_3007600 = .;
. += 0x4;
gUnknown_3007604 = .;
. += 0x4;
gUnknown_3007608 = .;
. += 0x8;
gUnknown_3007610 = .;
. += 0x10;
gUnknown_3007620 = .;
. += 0x10;
gUnknown_3007630 = .;
. += 0x4;
gUnknown_3007634 = .;
. += 0x4;
gUnknown_3007638 = .;
. += 0x8;
gUnknown_3007640 = .;
. += 0xc;
gUnknown_300764C = .;
. += 0x7f4;
gUnknown_3007E40 = .;
. += 0xc0;
gUnknown_3007F00 = .;
. += 0xa0;
gUnknown_3007FA0 = .;
. += 0x50;
gUnknown_3007FF0 = .;
. += 0x8;
gUnknown_3007FF8 = .;
. += 0x4;
gUnknown_3007FFC = .;
. += 0x4;

2
tools/aif2pcm/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
aif2pcm

20
tools/aif2pcm/LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2016 huderlem
Copyright (c) 2005, 2006 by Marco Trillo <marcotrillo@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

18
tools/aif2pcm/Makefile Normal file
View File

@ -0,0 +1,18 @@
CC = gcc
CFLAGS = -Wall -Wextra -Wno-switch -Werror -std=c11 -O2
LIBS = -lm
SRCS = main.c extended.c
.PHONY: all clean
all: aif2pcm
@:
aif2pcm: $(SRCS)
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
clean:
$(RM) aif2pcm aif2pcm.exe

172
tools/aif2pcm/extended.c Normal file
View File

@ -0,0 +1,172 @@
/* $Id: extended.c,v 1.8 2006/12/23 11:17:49 toad32767 Exp $ */
/*-
* Copyright (c) 2005, 2006 by Marco Trillo <marcotrillo@gmail.com>
*
* Permission is hereby granted, free of charge, to any
* person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the
* Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice
* shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
* KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <string.h>
#include <stdint.h>
/*
* Infinite & NAN values
* for non-IEEE systems
*/
#ifndef HUGE_VAL
#ifdef HUGE
#define INFINITE_VALUE HUGE
#define NAN_VALUE HUGE
#endif
#else
#define INFINITE_VALUE HUGE_VAL
#define NAN_VALUE HUGE_VAL
#endif
/*
* IEEE 754 Extended Precision
*
* Implementation here is the 80-bit extended precision
* format of Motorola 68881, Motorola 68882 and Motorola
* 68040 FPUs, as well as Intel 80x87 FPUs.
*
* See:
* http://www.freescale.com/files/32bit/doc/fact_sheet/BR509.pdf
*/
/*
* Exponent range: [-16383,16383]
* Precision for mantissa: 64 bits with no hidden bit
* Bias: 16383
*/
/*
* Write IEEE Extended Precision Numbers
*/
void
ieee754_write_extended(double in, uint8_t* out)
{
int sgn, exp, shift;
double fraction, t;
unsigned int lexp, hexp;
unsigned long low, high;
if (in == 0.0) {
memset(out, 0, 10);
return;
}
if (in < 0.0) {
in = fabs(in);
sgn = 1;
} else
sgn = 0;
fraction = frexp(in, &exp);
if (exp == 0 || exp > 16384) {
if (exp > 16384) /* infinite value */
low = high = 0;
else {
low = 0x80000000;
high = 0;
}
exp = 32767;
goto done;
}
fraction = ldexp(fraction, 32);
t = floor(fraction);
low = (unsigned long) t;
fraction -= t;
t = floor(ldexp(fraction, 32));
high = (unsigned long) t;
/* Convert exponents < -16382 to -16382 (then they will be
* stored as -16383) */
if (exp < -16382) {
shift = 0 - exp - 16382;
high >>= shift;
high |= (low << (32 - shift));
low >>= shift;
exp = -16382;
}
exp += 16383 - 1; /* bias */
done:
lexp = ((unsigned int) exp) >> 8;
hexp = ((unsigned int) exp) & 0xFF;
/* big endian */
out[0] = ((uint8_t) sgn) << 7;
out[0] |= (uint8_t) lexp;
out[1] = (uint8_t) hexp;
out[2] = (uint8_t) (low >> 24);
out[3] = (uint8_t) ((low >> 16) & 0xFF);
out[4] = (uint8_t) ((low >> 8) & 0xFF);
out[5] = (uint8_t) (low & 0xFF);
out[6] = (uint8_t) (high >> 24);
out[7] = (uint8_t) ((high >> 16) & 0xFF);
out[8] = (uint8_t) ((high >> 8) & 0xFF);
out[9] = (uint8_t) (high & 0xFF);
return;
}
/*
* Read IEEE Extended Precision Numbers
*/
double
ieee754_read_extended(uint8_t* in)
{
int sgn, exp;
unsigned long low, high;
double out;
/* Extract the components from the big endian buffer */
sgn = (int) (in[0] >> 7);
exp = ((int) (in[0] & 0x7F) << 8) | ((int) in[1]);
low = (((unsigned long) in[2]) << 24)
| (((unsigned long) in[3]) << 16)
| (((unsigned long) in[4]) << 8) | (unsigned long) in[5];
high = (((unsigned long) in[6]) << 24)
| (((unsigned long) in[7]) << 16)
| (((unsigned long) in[8]) << 8) | (unsigned long) in[9];
if (exp == 0 && low == 0 && high == 0)
return (sgn ? -0.0 : 0.0);
switch (exp) {
case 32767:
if (low == 0 && high == 0)
return (sgn ? -INFINITE_VALUE : INFINITE_VALUE);
else
return (sgn ? -NAN_VALUE : NAN_VALUE);
default:
exp -= 16383; /* unbias exponent */
}
out = ldexp((double) low, -31 + exp);
out += ldexp((double) high, -63 + exp);
return (sgn ? -out : out);
}

830
tools/aif2pcm/main.c Normal file
View File

@ -0,0 +1,830 @@
// Copyright(c) 2016 huderlem
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
/* extended.c */
void ieee754_write_extended (double, uint8_t*);
double ieee754_read_extended (uint8_t*);
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
typedef struct {
unsigned long num_samples;
uint8_t *samples;
uint8_t midi_note;
bool has_loop;
unsigned long loop_offset;
double sample_rate;
unsigned long real_num_samples;
} AifData;
struct Bytes {
unsigned long length;
uint8_t *data;
};
struct Bytes *read_bytearray(const char *filename)
{
struct Bytes *bytes = malloc(sizeof(struct Bytes));
FILE *f = fopen(filename, "rb");
if (!f)
{
FATAL_ERROR("Failed to open '%s' for reading!\n", filename);
}
fseek(f, 0, SEEK_END);
bytes->length = ftell(f);
fseek(f, 0, SEEK_SET);
bytes->data = malloc(bytes->length);
unsigned long read = fread(bytes->data, bytes->length, 1, f);
fclose(f);
if (read <= 0)
{
FATAL_ERROR("Failed to read data from '%s'!\n", filename);
}
return bytes;
}
void write_bytearray(const char *filename, struct Bytes *bytes)
{
FILE *f = fopen(filename, "wb");
if (!f)
{
FATAL_ERROR("Failed to open '%s' for writing!\n", filename);
}
fwrite(bytes->data, bytes->length, 1, f);
fclose(f);
}
void free_bytearray(struct Bytes *bytes)
{
free(bytes->data);
free(bytes);
}
char *get_file_extension(char *filename)
{
char *index = strrchr(filename, '.');
if (!index || index == filename)
{
return NULL;
}
return index + 1;
}
char *new_file_extension(char *filename, char *ext)
{
char *index = strrchr(filename, '.');
if (!index || index == filename)
{
index = filename + strlen(filename);
}
int length = index - filename;
char *new_filename = malloc(length + 1 + strlen(ext) + 1);
if (new_filename)
{
strcpy(new_filename, filename);
new_filename[length] = '.';
strcpy(new_filename + length + 1, ext);
}
return new_filename;
}
void read_aif(struct Bytes *aif, AifData *aif_data)
{
aif_data->has_loop = false;
aif_data->num_samples = 0;
unsigned long pos = 0;
char chunk_name[5]; chunk_name[4] = '\0';
char chunk_type[5]; chunk_type[4] = '\0';
// Check for FORM Chunk
memcpy(chunk_name, &aif->data[pos], 4);
pos += 4;
if (strcmp(chunk_name, "FORM") != 0)
{
FATAL_ERROR("Input .aif file has invalid header Chunk '%s'!\n", chunk_name);
}
// Read size of whole file.
unsigned long whole_chunk_size = aif->data[pos++] << 24;
whole_chunk_size |= (aif->data[pos++] << 16);
whole_chunk_size |= (aif->data[pos++] << 8);
whole_chunk_size |= (uint8_t)aif->data[pos++];
unsigned long expected_whole_chunk_size = aif->length - 8;
if (whole_chunk_size != expected_whole_chunk_size)
{
FATAL_ERROR("FORM Chunk ckSize '%lu' doesn't match actual size '%lu'!\n", whole_chunk_size, expected_whole_chunk_size);
}
// Check for AIFF Form Type
memcpy(chunk_type, &aif->data[pos], 4);
pos += 4;
if (strcmp(chunk_type, "AIFF") != 0)
{
FATAL_ERROR("FORM Type is '%s', but it must be AIFF!", chunk_type);
}
unsigned long num_sample_frames = 0;
// Read all the Chunks to populate the AifData struct.
while ((pos + 8) < aif->length)
{
// Read Chunk id
memcpy(chunk_name, &aif->data[pos], 4);
pos += 4;
unsigned long chunk_size = (aif->data[pos++] << 24);
chunk_size |= (aif->data[pos++] << 16);
chunk_size |= (aif->data[pos++] << 8);
chunk_size |= aif->data[pos++];
if ((pos + chunk_size) > aif->length)
{
FATAL_ERROR("%s chunk at 0x%lx reached end of file before finishing\n", chunk_name, pos);
}
if (strcmp(chunk_name, "COMM") == 0)
{
short num_channels = (aif->data[pos++] << 8);
num_channels |= (uint8_t)aif->data[pos++];
if (num_channels != 1)
{
FATAL_ERROR("numChannels (%d) in the COMM Chunk must be 1!\n", num_channels);
}
num_sample_frames = (aif->data[pos++] << 24);
num_sample_frames |= (aif->data[pos++] << 16);
num_sample_frames |= (aif->data[pos++] << 8);
num_sample_frames |= (uint8_t)aif->data[pos++];
short sample_size = (aif->data[pos++] << 8);
sample_size |= (uint8_t)aif->data[pos++];
if (sample_size != 8)
{
FATAL_ERROR("sampleSize (%d) in the COMM Chunk must be 8!\n", sample_size);
}
double sample_rate = ieee754_read_extended((uint8_t*)(aif->data + pos));
pos += 10;
aif_data->sample_rate = sample_rate;
if (aif_data->num_samples == 0)
{
aif_data->num_samples = num_sample_frames;
}
}
else if (strcmp(chunk_name, "MARK") == 0)
{
unsigned short num_markers = (aif->data[pos++] << 8);
num_markers |= (uint8_t)aif->data[pos++];
// Read each marker and look for the "START" marker.
for (int i = 0; i < num_markers; i++)
{
unsigned short marker_id = (aif->data[pos++] << 8);
marker_id |= (uint8_t)aif->data[pos++];
unsigned long marker_position = (aif->data[pos++] << 24);
marker_position |= (aif->data[pos++] << 16);
marker_position |= (aif->data[pos++] << 8);
marker_position |= (uint8_t)aif->data[pos++];
// Marker id is a pascal-style string.
uint8_t marker_name_size = aif->data[pos++];
char *marker_name = (char *)malloc((marker_name_size + 1) * sizeof(char));
memcpy(marker_name, &aif->data[pos], marker_name_size);
marker_name[marker_name_size] = '\0';
pos += marker_name_size;
if (strcmp(marker_name, "START") == 0)
{
aif_data->loop_offset = marker_position;
aif_data->has_loop = true;
}
else if (strcmp(marker_name, "END") == 0)
{
if (!aif_data->has_loop) {
aif_data->loop_offset = marker_position;
aif_data->has_loop = true;
}
aif_data->num_samples = marker_position;
}
free(marker_name);
}
}
else if (strcmp(chunk_name, "INST") == 0)
{
uint8_t midi_note = (uint8_t)aif->data[pos++];
aif_data->midi_note = midi_note;
// Skip over data we don't need.
pos += 19;
}
else if (strcmp(chunk_name, "SSND") == 0)
{
// SKip offset and blockSize
pos += 8;
unsigned long num_samples = chunk_size - 8;
uint8_t *sample_data = (uint8_t *)malloc(num_samples * sizeof(uint8_t));
memcpy(sample_data, &aif->data[pos], num_samples);
aif_data->samples = sample_data;
aif_data->real_num_samples = num_samples;
pos += chunk_size - 8;
}
else
{
// Skip over unsupported chunks.
pos += chunk_size;
}
}
}
// This is a table of deltas between sample values in compressed PCM data.
const int gDeltaEncodingTable[] = {
0, 1, 4, 9, 16, 25, 36, 49,
-64, -49, -36, -25, -16, -9, -4, -1,
};
struct Bytes *delta_decompress(struct Bytes *delta, unsigned int expected_length)
{
struct Bytes *pcm = malloc(sizeof(struct Bytes));
pcm->length = expected_length;
pcm->data = malloc(pcm->length + 0x40);
uint8_t hi, lo;
unsigned int i = 0;
unsigned int j = 0;
int k;
int8_t base;
while (i < delta->length)
{
base = (int8_t)delta->data[i++];
pcm->data[j++] = (uint8_t)base;
if (i >= delta->length)
{
break;
}
if (j >= pcm->length)
{
break;
}
lo = delta->data[i] & 0xf;
base += gDeltaEncodingTable[lo];
pcm->data[j++] = base;
i++;
if (i >= delta->length)
{
break;
}
if (j >= pcm->length)
{
break;
}
for (k = 0; k < 31; k++)
{
hi = (delta->data[i] >> 4) & 0xf;
base += gDeltaEncodingTable[hi];
pcm->data[j++] = base;
if (j >= pcm->length)
{
break;
}
lo = delta->data[i] & 0xf;
base += gDeltaEncodingTable[lo];
pcm->data[j++] = base;
i++;
if (i >= delta->length)
{
break;
}
if (j >= pcm->length)
{
break;
}
}
if (j >= pcm->length)
{
break;
}
}
pcm->length = j;
return pcm;
}
int get_delta_index(uint8_t sample, uint8_t prev_sample)
{
int best_error = INT_MAX;
int best_index = -1;
for (int i = 0; i < 16; i++)
{
uint8_t new_sample = prev_sample + gDeltaEncodingTable[i];
int error = sample > new_sample ? sample - new_sample : new_sample - sample;
if (error < best_error)
{
best_error = error;
best_index = i;
}
}
return best_index;
}
struct Bytes *delta_compress(struct Bytes *pcm)
{
struct Bytes *delta = malloc(sizeof(struct Bytes));
// estimate the length so we can malloc
int num_blocks = pcm->length / 64;
delta->length = num_blocks * 33;
int extra = pcm->length % 64;
if (extra)
{
delta->length += 1;
extra -= 1;
}
if (extra)
{
delta->length += 1;
extra -= 1;
}
if (extra)
{
delta->length += (extra + 1) / 2;
}
delta->data = malloc(delta->length + 33);
unsigned int i = 0;
unsigned int j = 0;
int k;
uint8_t base;
int delta_index;
while (i < pcm->length)
{
base = pcm->data[i++];
delta->data[j++] = base;
if (i >= pcm->length)
{
break;
}
delta_index = get_delta_index(pcm->data[i++], base);
base += gDeltaEncodingTable[delta_index];
delta->data[j++] = delta_index;
for (k = 0; k < 31; k++)
{
if (i >= pcm->length)
{
break;
}
delta_index = get_delta_index(pcm->data[i++], base);
base += gDeltaEncodingTable[delta_index];
delta->data[j] = (delta_index << 4);
if (i >= pcm->length)
{
break;
}
delta_index = get_delta_index(pcm->data[i++], base);
base += gDeltaEncodingTable[delta_index];
delta->data[j++] |= delta_index;
}
}
delta->length = j;
return delta;
}
#define STORE_U32_LE(dest, value) \
do { \
*(dest) = (value) & 0xff; \
*((dest) + 1) = ((value) >> 8) & 0xff; \
*((dest) + 2) = ((value) >> 16) & 0xff; \
*((dest) + 3) = ((value) >> 24) & 0xff; \
} while (0)
#define LOAD_U32_LE(var, src) \
do { \
(var) = *(src); \
(var) |= (*((src) + 1) << 8); \
(var) |= (*((src) + 2) << 16); \
(var) |= (*((src) + 3) << 24); \
} while (0)
// Reads an .aif file and produces a .pcm file containing an array of 8-bit samples.
void aif2pcm(const char *aif_filename, const char *pcm_filename, bool compress)
{
struct Bytes *aif = read_bytearray(aif_filename);
AifData aif_data = {0,0,0,0,0,0,0};
read_aif(aif, &aif_data);
int header_size = 0x10;
struct Bytes *pcm;
struct Bytes output = {0,0};
if (compress)
{
struct Bytes *input = malloc(sizeof(struct Bytes));
input->data = aif_data.samples;
input->length = aif_data.real_num_samples;
pcm = delta_compress(input);
free(input);
}
else
{
pcm = malloc(sizeof(struct Bytes));
pcm->data = aif_data.samples;
pcm->length = aif_data.real_num_samples;
}
output.length = header_size + pcm->length;
output.data = malloc(output.length);
uint32_t pitch_adjust = (uint32_t)(aif_data.sample_rate * 1024);
uint32_t loop_offset = (uint32_t)(aif_data.loop_offset);
uint32_t adjusted_num_samples = (uint32_t)(aif_data.num_samples - 1);
uint32_t flags = 0;
if (aif_data.has_loop) flags |= 0x40000000;
if (compress) flags |= 1;
STORE_U32_LE(output.data + 0, flags);
STORE_U32_LE(output.data + 4, pitch_adjust);
STORE_U32_LE(output.data + 8, loop_offset);
STORE_U32_LE(output.data + 12, adjusted_num_samples);
memcpy(&output.data[header_size], pcm->data, pcm->length);
write_bytearray(pcm_filename, &output);
free(aif->data);
free(aif);
free(pcm);
free(output.data);
free(aif_data.samples);
}
// Reads a .pcm file containing an array of 8-bit samples and produces an .aif file.
// See http://www-mmsp.ece.mcgill.ca/documents/audioformats/aiff/Docs/AIFF-1.3.pdf for .aif file specification.
void pcm2aif(const char *pcm_filename, const char *aif_filename, uint32_t base_note)
{
struct Bytes *pcm = read_bytearray(pcm_filename);
AifData *aif_data = malloc(sizeof(AifData));
uint32_t flags;
LOAD_U32_LE(flags, pcm->data + 0);
aif_data->has_loop = flags & 0x40000000;
bool compressed = flags & 1;
uint32_t pitch_adjust;
LOAD_U32_LE(pitch_adjust, pcm->data + 4);
aif_data->sample_rate = pitch_adjust / 1024.0;
LOAD_U32_LE(aif_data->loop_offset, pcm->data + 8);
LOAD_U32_LE(aif_data->num_samples, pcm->data + 12);
aif_data->num_samples += 1;
if (compressed)
{
struct Bytes *delta = pcm;
uint8_t *pcm_data = pcm->data;
delta->length -= 0x10;
delta->data += 0x10;
pcm = delta_decompress(delta, aif_data->num_samples);
free(pcm_data);
free(delta);
}
else
{
pcm->length -= 0x10;
pcm->data += 0x10;
}
aif_data->samples = malloc(pcm->length);
memcpy(aif_data->samples, pcm->data, pcm->length);
struct Bytes *aif = malloc(sizeof(struct Bytes));
aif->length = 54 + 60 + pcm->length;
aif->data = malloc(aif->length);
long pos = 0;
// First, write the FORM header chunk.
// FORM Chunk ckID
aif->data[pos++] = 'F';
aif->data[pos++] = 'O';
aif->data[pos++] = 'R';
aif->data[pos++] = 'M';
// FORM Chunk ckSize
unsigned long form_size = pos;
unsigned long data_size = aif->length - 8;
aif->data[pos++] = ((data_size >> 24) & 0xFF);
aif->data[pos++] = ((data_size >> 16) & 0xFF);
aif->data[pos++] = ((data_size >> 8) & 0xFF);
aif->data[pos++] = (data_size & 0xFF);
// FORM Chunk formType
aif->data[pos++] = 'A';
aif->data[pos++] = 'I';
aif->data[pos++] = 'F';
aif->data[pos++] = 'F';
// Next, write the Common Chunk
// Common Chunk ckID
aif->data[pos++] = 'C';
aif->data[pos++] = 'O';
aif->data[pos++] = 'M';
aif->data[pos++] = 'M';
// Common Chunk ckSize
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 18;
// Common Chunk numChannels
aif->data[pos++] = 0;
aif->data[pos++] = 1; // 1 channel
// Common Chunk numSampleFrames
aif->data[pos++] = ((aif_data->num_samples >> 24) & 0xFF);
aif->data[pos++] = ((aif_data->num_samples >> 16) & 0xFF);
aif->data[pos++] = ((aif_data->num_samples >> 8) & 0xFF);
aif->data[pos++] = (aif_data->num_samples & 0xFF);
// Common Chunk sampleSize
aif->data[pos++] = 0;
aif->data[pos++] = 8; // 8 bits per sample
// Common Chunk sampleRate
//double sample_rate = pitch_adjust / 1024.0;
uint8_t sample_rate_buffer[10];
ieee754_write_extended(aif_data->sample_rate, sample_rate_buffer);
for (int i = 0; i < 10; i++)
{
aif->data[pos++] = sample_rate_buffer[i];
}
if (aif_data->has_loop)
{
// Marker Chunk ckID
aif->data[pos++] = 'M';
aif->data[pos++] = 'A';
aif->data[pos++] = 'R';
aif->data[pos++] = 'K';
// Marker Chunk ckSize
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 12 + (aif_data->has_loop ? 12 : 0);
// Marker Chunk numMarkers
aif->data[pos++] = 0;
aif->data[pos++] = (aif_data->has_loop ? 2 : 1);
// Marker loop start
aif->data[pos++] = 0;
aif->data[pos++] = 1; // id = 1
long loop_start = aif_data->loop_offset;
aif->data[pos++] = ((loop_start >> 24) & 0xFF);
aif->data[pos++] = ((loop_start >> 16) & 0xFF);
aif->data[pos++] = ((loop_start >> 8) & 0xFF);
aif->data[pos++] = (loop_start & 0xFF); // position
aif->data[pos++] = 5; // pascal-style string length
aif->data[pos++] = 'S';
aif->data[pos++] = 'T';
aif->data[pos++] = 'A';
aif->data[pos++] = 'R';
aif->data[pos++] = 'T'; // markerName
// Marker loop end
aif->data[pos++] = 0;
aif->data[pos++] = (aif_data->has_loop ? 2 : 1); // id = 2
long loop_end = aif_data->num_samples;
aif->data[pos++] = ((loop_end >> 24) & 0xFF);
aif->data[pos++] = ((loop_end >> 16) & 0xFF);
aif->data[pos++] = ((loop_end >> 8) & 0xFF);
aif->data[pos++] = (loop_end & 0xFF); // position
aif->data[pos++] = 3; // pascal-style string length
aif->data[pos++] = 'E';
aif->data[pos++] = 'N';
aif->data[pos++] = 'D';
}
// Instrument Chunk ckID
aif->data[pos++] = 'I';
aif->data[pos++] = 'N';
aif->data[pos++] = 'S';
aif->data[pos++] = 'T';
// Instrument Chunk ckSize
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 20;
aif->data[pos++] = base_note; // baseNote
aif->data[pos++] = 0; // detune
aif->data[pos++] = 0; // lowNote
aif->data[pos++] = 127; // highNote
aif->data[pos++] = 1; // lowVelocity
aif->data[pos++] = 127; // highVelocity
aif->data[pos++] = 0; // gain (hi)
aif->data[pos++] = 0; // gain (lo)
// Instrument Chunk sustainLoop
aif->data[pos++] = 0;
aif->data[pos++] = 1; // playMode = ForwardLooping
aif->data[pos++] = 0;
aif->data[pos++] = 1; // beginLoop marker id
aif->data[pos++] = 0;
aif->data[pos++] = 2; // endLoop marker id
// Instrument Chunk releaseLoop
aif->data[pos++] = 0;
aif->data[pos++] = 1; // playMode = ForwardLooping
aif->data[pos++] = 0;
aif->data[pos++] = 1; // beginLoop marker id
aif->data[pos++] = 0;
aif->data[pos++] = 2; // endLoop marker id
// Finally, write the Sound Data Chunk
// Sound Data Chunk ckID
aif->data[pos++] = 'S';
aif->data[pos++] = 'S';
aif->data[pos++] = 'N';
aif->data[pos++] = 'D';
// Sound Data Chunk ckSize
unsigned long sound_data_size = pcm->length + 8;
aif->data[pos++] = ((sound_data_size >> 24) & 0xFF);
aif->data[pos++] = ((sound_data_size >> 16) & 0xFF);
aif->data[pos++] = ((sound_data_size >> 8) & 0xFF);
aif->data[pos++] = (sound_data_size & 0xFF);
// Sound Data Chunk offset
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
// Sound Data Chunk blockSize
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
// Sound Data Chunk soundData
for (unsigned int i = 0; i < aif_data->loop_offset; i++)
{
aif->data[pos++] = aif_data->samples[i];
}
int j = 0;
for (unsigned int i = aif_data->loop_offset; i < pcm->length; i++)
{
int pcm_index = aif_data->loop_offset + (j++ % (pcm->length - aif_data->loop_offset));
aif->data[pos++] = aif_data->samples[pcm_index];
}
aif->length = pos;
// Go back and rewrite ckSize
data_size = aif->length - 8;
aif->data[form_size + 0] = ((data_size >> 24) & 0xFF);
aif->data[form_size + 1] = ((data_size >> 16) & 0xFF);
aif->data[form_size + 2] = ((data_size >> 8) & 0xFF);
aif->data[form_size + 3] = (data_size & 0xFF);
write_bytearray(aif_filename, aif);
free(aif->data);
free(aif);
}
void usage(void)
{
fprintf(stderr, "Usage: aif2pcm bin_file [aif_file]\n");
fprintf(stderr, " aif2pcm aif_file [bin_file] [--compress]\n");
}
int main(int argc, char **argv)
{
if (argc < 2)
{
usage();
exit(1);
}
char *input_file = argv[1];
char *extension = get_file_extension(input_file);
char *output_file;
bool compressed = false;
if (argc > 3)
{
for (int i = 3; i < argc; i++)
{
if (strcmp(argv[i], "--compress") == 0)
{
compressed = true;
}
}
}
if (strcmp(extension, "aif") == 0 || strcmp(extension, "aiff") == 0)
{
if (argc >= 3)
{
output_file = argv[2];
aif2pcm(input_file, output_file, compressed);
}
else
{
output_file = new_file_extension(input_file, "bin");
aif2pcm(input_file, output_file, compressed);
free(output_file);
}
}
else if (strcmp(extension, "bin") == 0)
{
if (argc >= 3)
{
output_file = argv[2];
pcm2aif(input_file, output_file, 60);
}
else
{
output_file = new_file_extension(input_file, "aif");
pcm2aif(input_file, output_file, 60);
free(output_file);
}
}
else
{
FATAL_ERROR("Input file must be .aif or .bin: '%s'\n", input_file);
}
return 0;
}

1
tools/bin2c/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
bin2c

19
tools/bin2c/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2016 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

16
tools/bin2c/Makefile Normal file
View File

@ -0,0 +1,16 @@
CC = gcc
CFLAGS = -Wall -Wextra -Werror -std=c11 -O2
.PHONY: all clean
SRCS = bin2c.c
all: bin2c
@:
bin2c: $(SRCS)
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) bin2c bin2c.exe

201
tools/bin2c/bin2c.c Normal file
View File

@ -0,0 +1,201 @@
// Copyright(c) 2015-2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
unsigned char *ReadWholeFile(char *path, int *size)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = malloc(*size);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
int ExtractData(unsigned char *buffer, int offset, int size)
{
switch (size)
{
case 1:
return buffer[offset];
case 2:
return (buffer[offset + 1] << 8)
| buffer[offset];
case 4:
return (buffer[offset + 3] << 24)
| (buffer[offset + 2] << 16)
| (buffer[offset + 1] << 8)
| buffer[offset];
default:
FATAL_ERROR("Invalid size passed to ExtractData.\n");
}
}
int main(int argc, char **argv)
{
if (argc < 3)
FATAL_ERROR("Usage: bin2c INPUT_FILE VAR_NAME [OPTIONS...]\n");
int fileSize;
unsigned char *buffer = ReadWholeFile(argv[1], &fileSize);
char *var_name = argv[2];
int col = 1;
int pad = 0;
int size = 1;
bool isSigned = false;
bool isStatic = false;
bool isDecimal = false;
for (int i = 3; i < argc; i++)
{
if (!strcmp(argv[i], "-col"))
{
i++;
if (i >= argc)
FATAL_ERROR("Missing argument after '-col'.\n");
col = atoi(argv[i]);
}
else if (!strcmp(argv[i], "-pad"))
{
i++;
if (i >= argc)
FATAL_ERROR("Missing argument after '-pad'.\n");
pad = atoi(argv[i]);
}
else if (!strcmp(argv[i], "-size"))
{
i++;
if (i >= argc)
FATAL_ERROR("Missing argument after '-size'.\n");
size = atoi(argv[i]);
if (size != 1 && size != 2 && size != 4)
FATAL_ERROR("Size must be 1, 2, or 4.\n");
}
else if (!strcmp(argv[i], "-signed"))
{
isSigned = true;
isDecimal = true;
}
else if (!strcmp(argv[i], "-static"))
{
isStatic = true;
}
else if (!strcmp(argv[i], "-decimal"))
{
isDecimal = true;
}
else
{
FATAL_ERROR("Unrecognized option '%s'.\n", argv[i]);
}
}
if ((fileSize & (size - 1)) != 0)
FATAL_ERROR("Size %d doesn't evenly divide file size %d.\n", size, fileSize);
printf("// Generated file. Do not edit.\n\n");
if (isStatic)
printf("static ");
printf("const ");
if (isSigned)
printf("s%d ", 8 * size);
else
printf("u%d ", 8 * size);
printf("%s[] =\n{", var_name);
int count = fileSize / size;
int offset = 0;
for (int i = 0; i < count; i++)
{
if (i % col == 0)
printf("\n ");
int data = ExtractData(buffer, offset, size);
offset += size;
if (isDecimal)
{
if (isSigned)
printf("%*d, ", pad, data);
else
printf("%*uu, ", pad, data);
}
else
{
printf("%#*xu, ", pad, data);
}
}
printf("\n};\n");
return 0;
}

2
tools/gbafix/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
gbafix
README

674
tools/gbafix/COPYING Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

13
tools/gbafix/Makefile Normal file
View File

@ -0,0 +1,13 @@
CC = gcc
.PHONY: all clean
SRCS = gbafix.c
all: gbafix
@:
gbafix: $(SRCS)
$(CC) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) gbafix gbafix.exe

3147
tools/gbafix/elf.h Normal file

File diff suppressed because it is too large Load Diff

317
tools/gbafix/gbafix.c Normal file
View File

@ -0,0 +1,317 @@
/*
"$Id: gbafix.c,v 1.2 2008-07-30 17:12:51 wntrmute Exp $"
DevkitPro GBA ROM fix utility
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
Please report all bugs and problems through the bug tracker at
"http://sourceforge.net/tracker/?group_id=114505&atid=668551".
"$Header: /lvm/shared/ds/ds/cvs/devkitpro-cvsbackup/tools/gba/gbatools/gbafix.c,v 1.2 2008-07-30 17:12:51 wntrmute Exp $"
*/
//---------------------------------------------------------------------------------
// gbafix.c
//---------------------------------------------------------------------------------
/*
Gameboy Advance ROM fixer (by Dark Fader / BlackThunder / WinterMute / Diegoisawesome)
Validates header of GBA roms.
History
-------
v1.07 - added support for ELF input, (PikalaxALT)
v1.06 - added output silencing, (Diegoisawesome)
v1.05 - added debug offset argument, (Diegoisawesome)
v1.04 - converted to plain C, (WinterMute)
v1.03 - header.fixed, header.device_type
v1.02 - redefined the options (rgbfix style), checksum=0
v1.01 - fix in parameters
v1.00 - logo, complement
*/
#pragma pack(1)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "elf.h"
#define VER "1.07"
#define ARGV argv[arg]
#define VALUE (ARGV+2)
#define NUMBER strtoul(VALUE, NULL, 0)
typedef struct
{
uint32_t start_code; // B instruction
uint8_t logo[0xA0-0x04]; // logo data
uint8_t title[0xC]; // game title name
uint32_t game_code; //
uint16_t maker_code; //
uint8_t fixed; // 0x96
uint8_t unit_code; // 0x00
uint8_t device_type; // 0x00
uint8_t unused[7]; //
uint8_t game_version; // 0x00
uint8_t complement; // 800000A0..800000BC
uint16_t checksum; // 0x0000
} Header;
Header header;
unsigned short checksum_without_header = 0;
const Header good_header =
{
// start_code
0xEA00002E,
// logo
{ 0x24,0xFF,0xAE,0x51,0x69,0x9A,0xA2,0x21,0x3D,0x84,0x82,0x0A,0x84,0xE4,0x09,0xAD,
0x11,0x24,0x8B,0x98,0xC0,0x81,0x7F,0x21,0xA3,0x52,0xBE,0x19,0x93,0x09,0xCE,0x20,
0x10,0x46,0x4A,0x4A,0xF8,0x27,0x31,0xEC,0x58,0xC7,0xE8,0x33,0x82,0xE3,0xCE,0xBF,
0x85,0xF4,0xDF,0x94,0xCE,0x4B,0x09,0xC1,0x94,0x56,0x8A,0xC0,0x13,0x72,0xA7,0xFC,
0x9F,0x84,0x4D,0x73,0xA3,0xCA,0x9A,0x61,0x58,0x97,0xA3,0x27,0xFC,0x03,0x98,0x76,
0x23,0x1D,0xC7,0x61,0x03,0x04,0xAE,0x56,0xBF,0x38,0x84,0x00,0x40,0xA7,0x0E,0xFD,
0xFF,0x52,0xFE,0x03,0x6F,0x95,0x30,0xF1,0x97,0xFB,0xC0,0x85,0x60,0xD6,0x80,0x25,
0xA9,0x63,0xBE,0x03,0x01,0x4E,0x38,0xE2,0xF9,0xA2,0x34,0xFF,0xBB,0x3E,0x03,0x44,
0x78,0x00,0x90,0xCB,0x88,0x11,0x3A,0x94,0x65,0xC0,0x7C,0x63,0x87,0xF0,0x3C,0xAF,
0xD6,0x25,0xE4,0x8B,0x38,0x0A,0xAC,0x72,0x21,0xD4,0xF8,0x07 } ,
// title
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
// game code
0x00000000,
// maker code
0x3130,
// fixed
0x96,
// unit_code
0x00,
// device type
0x00,
// unused
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
// game version
0x00,
// complement
0x00,
// checksum
0x0000
};
//---------------------------------------------------------------------------------
char HeaderComplement()
/*---------------------------------------------------------------------------------
Calculate Header complement check
---------------------------------------------------------------------------------*/
{
int n;
char c = 0;
char *p = (char *)&header + 0xA0;
for (n=0; n<0xBD-0xA0; n++)
{
c += *p++;
}
return -(0x19+c);
}
//---------------------------------------------------------------------------------
int main(int argc, char *argv[])
//---------------------------------------------------------------------------------
{
int arg;
char *argfile = 0;
FILE *infile;
int silent = 0;
int schedule_pad = 0;
int size,bit;
// show syntax
if (argc <= 1)
{
printf("GBA ROM fixer v"VER" by Dark Fader / BlackThunder / WinterMute / Diegoisawesome \n");
printf("Syntax: gbafix <rom.gba> [-p] [-t[title]] [-c<game_code>] [-m<maker_code>] [-r<version>] [-d<debug>] [--silent]\n");
printf("\n");
printf("parameters:\n");
printf(" -p Pad to next exact power of 2. No minimum size!\n");
printf(" -t[<title>] Patch title. Stripped filename if none given.\n");
printf(" -c<game_code> Patch game code (four characters)\n");
printf(" -m<maker_code> Patch maker code (two characters)\n");
printf(" -r<version> Patch game version (number)\n");
printf(" -d<debug> Enable debugging handler and set debug entry point (0 or 1)\n");
printf(" --silent Silence non-error output\n");
return -1;
}
// get filename
for (arg=1; arg<argc; arg++)
{
if (ARGV[0] != '-') { argfile=ARGV; }
if (strncmp("--silent", &ARGV[0], 7) == 0) { silent = 1; }
}
// check filename
if (!argfile)
{
fprintf(stderr, "Filename needed!\n");
return -1;
}
uint32_t sh_offset = 0;
// read file
infile = fopen(argfile, "r+b");
if (!infile) { fprintf(stderr, "Error opening input file!\n"); return -1; }
fseek(infile, sh_offset, SEEK_SET);
fread(&header, sizeof(header), 1, infile);
// elf check
Elf32_Shdr secHeader;
if (memcmp(&header, ELFMAG, 4) == 0) {
Elf32_Ehdr *elfHeader = (Elf32_Ehdr *)&header;
fseek(infile, elfHeader->e_shoff, SEEK_SET);
int i;
for (i = 0; i < elfHeader->e_shnum; i++) {
fread(&secHeader, sizeof(Elf32_Shdr), 1, infile);
if (secHeader.sh_type == SHT_PROGBITS && secHeader.sh_addr == elfHeader->e_entry) break;
}
if (i == elfHeader->e_shnum) { fprintf(stderr, "Error finding entry point!\n"); return 1; }
fseek(infile, secHeader.sh_offset, SEEK_SET);
sh_offset = secHeader.sh_offset;
fread(&header, sizeof(header), 1, infile);
}
// fix some data
memcpy(header.logo, good_header.logo, sizeof(header.logo));
memcpy(&header.fixed, &good_header.fixed, sizeof(header.fixed));
memcpy(&header.device_type, &good_header.device_type, sizeof(header.device_type));
// parse command line
for (arg=1; arg<argc; arg++)
{
if ((ARGV[0] == '-'))
{
switch (ARGV[1])
{
case 'p': // pad
{
schedule_pad = 1;
break;
}
case 't': // title
{
char title[256];
memset(title, 0, sizeof(title));
if (VALUE[0])
{
strncpy(title, VALUE, sizeof(header.title));
}
else
{
// use filename
char s[256], *begin=s, *t; strcpy(s, argfile);
t = strrchr(s, '\\'); if (t) begin = t+1;
t = strrchr(s, '/'); if (t) begin = t+1;
t = strrchr(s, '.'); if (t) *t = 0;
strncpy(title, begin, sizeof(header.title));
if (!silent) printf("%s\n",begin);
}
memcpy(header.title, title, sizeof(header.title)); // copy
break;
}
case 'c': // game code
{
//if (!VALUE[0]) { fprintf(stderr, "Need value for %s\n", ARGV); break; }
//header.game_code = NUMBER;
header.game_code = VALUE[0] | VALUE[1]<<8 | VALUE[2]<<16 | VALUE[3]<<24;
break;
}
case 'm': // maker code
{
//if (!VALUE[0]) { fprintf(stderr, "Need value for %s\n", ARGV); break; }
//header.maker_code = (unsigned short)NUMBER;
header.maker_code = VALUE[0] | VALUE[1]<<8;
break;
}
case 'v': // ignored, compatability with other gbafix
{
break;
}
case 'r': // version
{
if (!VALUE[0]) { fprintf(stderr, "Need value for %s\n", ARGV); break; }
header.game_version = (unsigned char)NUMBER;
break;
}
case 'd': // debug
{
if (!VALUE[0]) { fprintf(stderr, "Need value for %s\n", ARGV); break; }
header.logo[0x9C-0x04] = 0xA5; // debug enable
header.device_type = (unsigned char)((NUMBER & 1) << 7); // debug handler entry point
break;
}
case '-': // long arguments
{
if (strncmp("silent", &ARGV[2], 6) == 0) { continue; }
break;
}
default:
{
printf("Invalid option: %s\n", ARGV);
}
}
}
}
// update complement check & total checksum
header.complement = 0;
header.checksum = 0; // must be 0
header.complement = HeaderComplement();
//header.checksum = checksum_without_header + HeaderChecksum();
if (schedule_pad) {
if (sh_offset != 0) {
fprintf(stderr, "Warning: Cannot safely pad an ELF\n");
} else {
fseek(infile, 0, SEEK_END);
size = ftell(infile);
for (bit=31; bit>=0; bit--) if (size & (1<<bit)) break;
if (size != (1<<bit))
{
int todo = (1<<(bit+1)) - size;
while (todo--) fputc(0xFF, infile);
}
}
}
fseek(infile, sh_offset, SEEK_SET);
fwrite(&header, sizeof(header), 1, infile);
fclose(infile);
if (!silent) printf("ROM fixed!\n");
return 0;
}

1
tools/gbagfx/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
gbagfx

19
tools/gbagfx/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2015 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

18
tools/gbagfx/Makefile Normal file
View File

@ -0,0 +1,18 @@
CC = gcc
CFLAGS = -Wall -Wextra -Werror -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK
LIBS = -lpng -lz
SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c rl.c util.c font.c
.PHONY: all clean
all: gbagfx
@:
gbagfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
clean:
$(RM) gbagfx gbagfx.exe

254
tools/gbagfx/convert_png.c Normal file
View File

@ -0,0 +1,254 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <setjmp.h>
#include <png.h>
#include "global.h"
#include "convert_png.h"
#include "gfx.h"
static FILE *PngReadOpen(char *path, png_structp *pngStruct, png_infop *pngInfo)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
unsigned char sig[8];
if (fread(sig, 8, 1, fp) != 1)
FATAL_ERROR("Failed to read PNG signature from \"%s\".\n", path);
if (png_sig_cmp(sig, 0, 8))
FATAL_ERROR("\"%s\" does not have a valid PNG signature.\n", path);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
FATAL_ERROR("Failed to create PNG read struct.\n");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
FATAL_ERROR("Failed to create PNG info struct.\n");
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Failed to init I/O for reading \"%s\".\n", path);
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
*pngStruct = png_ptr;
*pngInfo = info_ptr;
return fp;
}
static unsigned char *ConvertBitDepth(unsigned char *src, int srcBitDepth, int destBitDepth, int numPixels)
{
// Round the number of bits up to the next 8 and divide by 8 to get the number of bytes.
int srcSize = ((numPixels * srcBitDepth + 7) & ~7) / 8;
int destSize = ((numPixels * destBitDepth + 7) & ~7) / 8;
unsigned char *output = calloc(destSize, 1);
unsigned char *dest = output;
int i;
int j;
int destBit = 8 - destBitDepth;
for (i = 0; i < srcSize; i++)
{
unsigned char srcByte = src[i];
for (j = 8 - srcBitDepth; j >= 0; j -= srcBitDepth)
{
unsigned char pixel = (srcByte >> j) % (1 << srcBitDepth);
if (pixel >= (1 << destBitDepth))
FATAL_ERROR("Image exceeds the maximum color value for a %ibpp image.\n", destBitDepth);
*dest |= pixel << destBit;
destBit -= destBitDepth;
if (destBit < 0)
{
dest++;
destBit = 8 - destBitDepth;
}
}
}
return output;
}
void ReadPng(char *path, struct Image *image)
{
png_structp png_ptr;
png_infop info_ptr;
FILE *fp = PngReadOpen(path, &png_ptr, &info_ptr);
int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
int color_type = png_get_color_type(png_ptr, info_ptr);
if (color_type != PNG_COLOR_TYPE_GRAY && color_type != PNG_COLOR_TYPE_PALETTE)
FATAL_ERROR("\"%s\" has an unsupported color type.\n", path);
// Check if the image has a palette so that we can tell if the colors need to be inverted later.
// Don't read the palette because it's not needed for now.
image->hasPalette = (color_type == PNG_COLOR_TYPE_PALETTE);
image->width = png_get_image_width(png_ptr, info_ptr);
image->height = png_get_image_height(png_ptr, info_ptr);
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
image->pixels = malloc(image->height * rowbytes);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate pixel buffer.\n");
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
if (row_pointers == NULL)
FATAL_ERROR("Failed to allocate row pointers.\n");
for (int i = 0; i < image->height; i++)
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error reading from \"%s\".\n", path);
png_read_image(png_ptr, row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(row_pointers);
fclose(fp);
if (bit_depth != image->bitDepth)
{
unsigned char *src = image->pixels;
if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && bit_depth != 8)
FATAL_ERROR("Bit depth of image must be 1, 2, 4, or 8.\n");
image->pixels = ConvertBitDepth(image->pixels, bit_depth, image->bitDepth, image->width * image->height);
free(src);
image->bitDepth = bit_depth;
}
}
void ReadPngPalette(char *path, struct Palette *palette)
{
png_structp png_ptr;
png_infop info_ptr;
png_colorp colors;
int numColors;
FILE *fp = PngReadOpen(path, &png_ptr, &info_ptr);
if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE)
FATAL_ERROR("The image \"%s\" does not contain a palette.\n", path);
if (png_get_PLTE(png_ptr, info_ptr, &colors, &numColors) != PNG_INFO_PLTE)
FATAL_ERROR("Failed to retrieve palette from \"%s\".\n", path);
if (numColors > 256)
FATAL_ERROR("Images with more than 256 colors are not supported.\n");
palette->numColors = numColors;
for (int i = 0; i < numColors; i++) {
palette->colors[i].red = colors[i].red;
palette->colors[i].green = colors[i].green;
palette->colors[i].blue = colors[i].blue;
}
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
}
void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *palette)
{
png_colorp colors = malloc(palette->numColors * sizeof(png_color));
if (colors == NULL)
FATAL_ERROR("Failed to allocate PNG palette.\n");
for (int i = 0; i < palette->numColors; i++) {
colors[i].red = palette->colors[i].red;
colors[i].green = palette->colors[i].green;
colors[i].blue = palette->colors[i].blue;
}
png_set_PLTE(png_ptr, info_ptr, colors, palette->numColors);
free(colors);
}
void WritePng(char *path, struct Image *image)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
FATAL_ERROR("Failed to create PNG write struct.\n");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
FATAL_ERROR("Failed to create PNG info struct.\n");
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Failed to init I/O for writing \"%s\".\n", path);
png_init_io(png_ptr, fp);
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error writing header for \"%s\".\n", path);
int color_type = image->hasPalette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY;
png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
image->bitDepth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
if (image->hasPalette) {
SetPngPalette(png_ptr, info_ptr, &image->palette);
if (image->hasTransparency) {
png_byte trans = 0;
png_set_tRNS(png_ptr, info_ptr, &trans, 1, 0);
}
}
png_write_info(png_ptr, info_ptr);
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
if (row_pointers == NULL)
FATAL_ERROR("Failed to allocate row pointers.\n");
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
for (int i = 0; i < image->height; i++)
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error writing \"%s\".\n", path);
png_write_image(png_ptr, row_pointers);
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error ending write of \"%s\".\n", path);
png_write_end(png_ptr, NULL);
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
free(row_pointers);
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2015 YamaArashi
#ifndef CONVERT_PNG_H
#define CONVERT_PNG_H
#include "gfx.h"
void ReadPng(char *path, struct Image *image);
void WritePng(char *path, struct Image *image);
void ReadPngPalette(char *path, struct Palette *palette);
#endif // CONVERT_PNG_H

326
tools/gbagfx/font.c Normal file
View File

@ -0,0 +1,326 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "global.h"
#include "font.h"
#include "gfx.h"
#include "util.h"
unsigned char gFontPalette[][3] = {
{0x90, 0xC8, 0xFF}, // bg (saturated blue that contrasts well with the shadow color)
{0x38, 0x38, 0x38}, // fg (dark grey)
{0xD8, 0xD8, 0xD8}, // shadow (light grey)
{0xFF, 0xFF, 0xFF} // box (white)
};
static void ConvertFromLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
unsigned int srcPixelsOffset = 0;
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2;
}
}
}
}
}
static void ConvertToLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
unsigned int destPixelsOffset = 0;
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2;
}
}
}
}
}
static void ConvertFromHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) {
unsigned int pixelsX = column * 8;
unsigned int srcPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile;
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 32) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2;
}
}
}
}
}
static void ConvertToHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) {
unsigned int pixelsX = column * 8;
unsigned int destPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile;
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 32) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2;
}
}
}
}
}
static void ConvertFromFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
unsigned int srcPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1);
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2;
}
}
}
}
}
static void ConvertToFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
unsigned int destPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1);
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2;
}
}
}
}
}
static void SetFontPalette(struct Image *image)
{
image->hasPalette = true;
image->palette.numColors = 4;
for (int i = 0; i < image->palette.numColors; i++) {
image->palette.colors[i].red = gFontPalette[i][0];
image->palette.colors[i].green = gFontPalette[i][1];
image->palette.colors[i].blue = gFontPalette[i][2];
}
image->hasTransparency = false;
}
void ReadLatinFont(char *path, struct Image *image)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numGlyphs = fileSize / 64;
if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16;
image->width = 256;
image->height = numRows * 16;
image->bitDepth = 2;
image->pixels = malloc(fileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromLatinFont(buffer, image->pixels, numRows);
free(buffer);
SetFontPalette(image);
}
void WriteLatinFont(char *path, struct Image *image)
{
if (image->width != 256)
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16;
int bufferSize = numRows * 16 * 64;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToLatinFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}
void ReadHalfwidthJapaneseFont(char *path, struct Image *image)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int glyphSize = 32;
if (fileSize % glyphSize != 0)
FATAL_ERROR("The file size (%d) is not a multiple of %d.\n", fileSize, glyphSize);
int numGlyphs = fileSize / glyphSize;
if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16;
image->width = 128;
image->height = numRows * 16;
image->bitDepth = 2;
image->pixels = malloc(fileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows);
free(buffer);
SetFontPalette(image);
}
void WriteHalfwidthJapaneseFont(char *path, struct Image *image)
{
if (image->width != 128)
FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width);
if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16;
int bufferSize = numRows * 16 * 32;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}
void ReadFullwidthJapaneseFont(char *path, struct Image *image)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numGlyphs = fileSize / 64;
if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16;
image->width = 256;
image->height = numRows * 16;
image->bitDepth = 2;
image->pixels = malloc(fileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows);
free(buffer);
SetFontPalette(image);
}
void WriteFullwidthJapaneseFont(char *path, struct Image *image)
{
if (image->width != 256)
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16;
int bufferSize = numRows * 16 * 64;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToFullwidthJapaneseFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}

16
tools/gbagfx/font.h Normal file
View File

@ -0,0 +1,16 @@
// Copyright (c) 2015 YamaArashi
#ifndef FONT_H
#define FONT_H
#include <stdbool.h>
#include "gfx.h"
void ReadLatinFont(char *path, struct Image *image);
void WriteLatinFont(char *path, struct Image *image);
void ReadHalfwidthJapaneseFont(char *path, struct Image *image);
void WriteHalfwidthJapaneseFont(char *path, struct Image *image);
void ReadFullwidthJapaneseFont(char *path, struct Image *image);
void WriteFullwidthJapaneseFont(char *path, struct Image *image);
#endif // FONT_H

344
tools/gbagfx/gfx.c Normal file
View File

@ -0,0 +1,344 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "global.h"
#include "gfx.h"
#include "util.h"
#define GET_GBA_PAL_RED(x) (((x) >> 0) & 0x1F)
#define GET_GBA_PAL_GREEN(x) (((x) >> 5) & 0x1F)
#define GET_GBA_PAL_BLUE(x) (((x) >> 10) & 0x1F)
#define SET_GBA_PAL(r, g, b) (((b) << 10) | ((g) << 5) | (r))
#define UPCONVERT_BIT_DEPTH(x) (((x) * 255) / 31)
#define DOWNCONVERT_BIT_DEPTH(x) ((x) / 8)
static void AdvanceMetatilePosition(int *subTileX, int *subTileY, int *metatileX, int *metatileY, int metatilesWide, int metatileWidth, int metatileHeight)
{
(*subTileX)++;
if (*subTileX == metatileWidth) {
*subTileX = 0;
(*subTileY)++;
if (*subTileY == metatileHeight) {
*subTileY = 0;
(*metatileX)++;
if (*metatileX == metatilesWide) {
*metatileX = 0;
(*metatileY)++;
}
}
}
}
static void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = metatilesWide * metatileWidth;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
int destX = metatileX * metatileWidth + subTileX;
unsigned char srcPixelOctet = *src++;
unsigned char *destPixelOctet = &dest[destY * pitch + destX];
for (int k = 0; k < 8; k++) {
*destPixelOctet <<= 1;
*destPixelOctet |= (srcPixelOctet & 1) ^ invertColors;
srcPixelOctet >>= 1;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = (metatilesWide * metatileWidth) * 4;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
for (int k = 0; k < 4; k++) {
int destX = (metatileX * metatileWidth + subTileX) * 4 + k;
unsigned char srcPixelPair = *src++;
unsigned char leftPixel = srcPixelPair & 0xF;
unsigned char rightPixel = srcPixelPair >> 4;
if (invertColors) {
leftPixel = 15 - leftPixel;
rightPixel = 15 - rightPixel;
}
dest[destY * pitch + destX] = (leftPixel << 4) | rightPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = (metatilesWide * metatileWidth) * 8;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
for (int k = 0; k < 8; k++) {
int destX = (metatileX * metatileWidth + subTileX) * 8 + k;
unsigned char srcPixel = *src++;
if (invertColors)
srcPixel = 255 - srcPixel;
dest[destY * pitch + destX] = srcPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = metatilesWide * metatileWidth;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
int srcX = metatileX * metatileWidth + subTileX;
unsigned char srcPixelOctet = src[srcY * pitch + srcX];
unsigned char *destPixelOctet = dest++;
for (int k = 0; k < 8; k++) {
*destPixelOctet <<= 1;
*destPixelOctet |= (srcPixelOctet & 1) ^ invertColors;
srcPixelOctet >>= 1;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = (metatilesWide * metatileWidth) * 4;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
for (int k = 0; k < 4; k++) {
int srcX = (metatileX * metatileWidth + subTileX) * 4 + k;
unsigned char srcPixelPair = src[srcY * pitch + srcX];
unsigned char leftPixel = srcPixelPair >> 4;
unsigned char rightPixel = srcPixelPair & 0xF;
if (invertColors) {
leftPixel = 15 - leftPixel;
rightPixel = 15 - rightPixel;
}
*dest++ = (rightPixel << 4) | leftPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = (metatilesWide * metatileWidth) * 8;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
for (int k = 0; k < 8; k++) {
int srcX = (metatileX * metatileWidth + subTileX) * 8 + k;
unsigned char srcPixel = src[srcY * pitch + srcX];
if (invertColors)
srcPixel = 255 - srcPixel;
*dest++ = srcPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
{
int tileSize = bitDepth * 8;
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numTiles = fileSize / tileSize;
int tilesHeight = (numTiles + tilesWidth - 1) / tilesWidth;
if (tilesWidth % metatileWidth != 0)
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", tilesWidth, metatileWidth);
if (tilesHeight % metatileHeight != 0)
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", tilesHeight, metatileHeight);
image->width = tilesWidth * 8;
image->height = tilesHeight * 8;
image->bitDepth = bitDepth;
image->pixels = calloc(tilesWidth * tilesHeight, tileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for pixels.\n");
int metatilesWide = tilesWidth / metatileWidth;
switch (bitDepth) {
case 1:
ConvertFromTiles1Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
case 4:
ConvertFromTiles4Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
case 8:
ConvertFromTiles8Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
}
free(buffer);
}
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
{
int tileSize = bitDepth * 8;
if (image->width % 8 != 0)
FATAL_ERROR("The width in pixels (%d) isn't a multiple of 8.\n", image->width);
if (image->height % 8 != 0)
FATAL_ERROR("The height in pixels (%d) isn't a multiple of 8.\n", image->height);
int tilesWidth = image->width / 8;
int tilesHeight = image->height / 8;
if (tilesWidth % metatileWidth != 0)
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", tilesWidth, metatileWidth);
if (tilesHeight % metatileHeight != 0)
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", tilesHeight, metatileHeight);
int maxNumTiles = tilesWidth * tilesHeight;
if (numTiles == 0)
numTiles = maxNumTiles;
else if (numTiles > maxNumTiles)
FATAL_ERROR("The specified number of tiles (%d) is greater than the maximum possible value (%d).\n", numTiles, maxNumTiles);
int bufferSize = numTiles * tileSize;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for pixels.\n");
int metatilesWide = tilesWidth / metatileWidth;
switch (bitDepth) {
case 1:
ConvertToTiles1Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
case 4:
ConvertToTiles4Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
case 8:
ConvertToTiles8Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
}
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}
void FreeImage(struct Image *image)
{
free(image->pixels);
image->pixels = NULL;
}
void ReadGbaPalette(char *path, struct Palette *palette)
{
int fileSize;
unsigned char *data = ReadWholeFile(path, &fileSize);
if (fileSize % 2 != 0)
FATAL_ERROR("The file size (%d) is not a multiple of 2.\n", fileSize);
palette->numColors = fileSize / 2;
for (int i = 0; i < palette->numColors; i++) {
uint16_t paletteEntry = (data[i * 2 + 1] << 8) | data[i * 2];
palette->colors[i].red = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_RED(paletteEntry));
palette->colors[i].green = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_GREEN(paletteEntry));
palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry));
}
free(data);
}
void WriteGbaPalette(char *path, struct Palette *palette)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
for (int i = 0; i < palette->numColors; i++) {
unsigned char red = DOWNCONVERT_BIT_DEPTH(palette->colors[i].red);
unsigned char green = DOWNCONVERT_BIT_DEPTH(palette->colors[i].green);
unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue);
uint16_t paletteEntry = SET_GBA_PAL(red, green, blue);
fputc(paletteEntry & 0xFF, fp);
fputc(paletteEntry >> 8, fp);
}
fclose(fp);
}

36
tools/gbagfx/gfx.h Normal file
View File

@ -0,0 +1,36 @@
// Copyright (c) 2015 YamaArashi
#ifndef GFX_H
#define GFX_H
#include <stdint.h>
#include <stdbool.h>
struct Color {
unsigned char red;
unsigned char green;
unsigned char blue;
};
struct Palette {
struct Color colors[256];
int numColors;
};
struct Image {
int width;
int height;
int bitDepth;
unsigned char *pixels;
bool hasPalette;
struct Palette palette;
bool hasTransparency;
};
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
void FreeImage(struct Image *image);
void ReadGbaPalette(char *path, struct Palette *palette);
void WriteGbaPalette(char *path, struct Palette *palette);
#endif // GFX_H

31
tools/gbagfx/global.h Normal file
View File

@ -0,0 +1,31 @@
// Copyright (c) 2015 YamaArashi
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdio.h>
#include <stdlib.h>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#define UNUSED
#else
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#define UNUSED __attribute__((__unused__))
#endif // _MSC_VER
#endif // GLOBAL_H

172
tools/gbagfx/jasc_pal.c Normal file
View File

@ -0,0 +1,172 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <string.h>
#include "global.h"
#include "gfx.h"
#include "util.h"
// Read/write Paint Shop Pro palette files.
// Format of a Paint Shop Pro palette file, line by line:
// "JASC-PAL\r\n" (signature)
// "0100\r\n" (version; seems to always be "0100")
// "<NUMBER_OF_COLORS>\r\n" (number of colors in decimal)
//
// <NUMBER_OF_COLORS> times:
// "<RED> <GREEN> <BLUE>\r\n" (color entry)
//
// Each color component is a decimal number from 0 to 255.
// Examples:
// Black - "0 0 0\r\n"
// Blue - "0 0 255\r\n"
// Brown - "150 75 0\r\n"
#define MAX_LINE_LENGTH 11
void ReadJascPaletteLine(FILE *fp, char *line)
{
int c;
int length = 0;
for (;;)
{
c = fgetc(fp);
if (c == '\r')
{
c = fgetc(fp);
if (c != '\n')
FATAL_ERROR("CR line endings aren't supported.\n");
line[length] = 0;
return;
}
if (c == '\n')
FATAL_ERROR("LF line endings aren't supported.\n");
if (c == EOF)
FATAL_ERROR("Unexpected EOF. No CRLF at end of file.\n");
if (c == 0)
FATAL_ERROR("NUL character in file.\n");
if (length == MAX_LINE_LENGTH)
{
line[length] = 0;
FATAL_ERROR("The line \"%s\" is too long.\n", line);
}
line[length++] = c;
}
}
void ReadJascPalette(char *path, struct Palette *palette)
{
char line[MAX_LINE_LENGTH + 1];
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open JASC-PAL file \"%s\" for reading.\n", path);
ReadJascPaletteLine(fp, line);
if (strcmp(line, "JASC-PAL") != 0)
FATAL_ERROR("Invalid JASC-PAL signature.\n");
ReadJascPaletteLine(fp, line);
if (strcmp(line, "0100") != 0)
FATAL_ERROR("Unsuported JASC-PAL version.\n");
ReadJascPaletteLine(fp, line);
if (!ParseNumber(line, NULL, 10, &palette->numColors))
FATAL_ERROR("Failed to parse number of colors.\n");
if (palette->numColors < 1 || palette->numColors > 256)
FATAL_ERROR("%d is an invalid number of colors. The number of colors must be in the range [1, 256].\n", palette->numColors);
for (int i = 0; i < palette->numColors; i++)
{
ReadJascPaletteLine(fp, line);
char *s = line;
char *end;
int red;
int green;
int blue;
if (!ParseNumber(s, &end, 10, &red))
FATAL_ERROR("Failed to parse red color component.\n");
s = end;
if (*s != ' ')
FATAL_ERROR("Expected a space after red color component.\n");
s++;
if (*s < '0' || *s > '9')
FATAL_ERROR("Expected only a space between red and green color components.\n");
if (!ParseNumber(s, &end, 10, &green))
FATAL_ERROR("Failed to parse green color component.\n");
s = end;
if (*s != ' ')
FATAL_ERROR("Expected a space after green color component.\n");
s++;
if (*s < '0' || *s > '9')
FATAL_ERROR("Expected only a space between green and blue color components.\n");
if (!ParseNumber(s, &end, 10, &blue))
FATAL_ERROR("Failed to parse blue color component.\n");
if (*end != 0)
FATAL_ERROR("Garbage after blue color component.\n");
if (red < 0 || red > 255)
FATAL_ERROR("Red color component (%d) is outside the range [0, 255].\n", red);
if (green < 0 || green > 255)
FATAL_ERROR("Green color component (%d) is outside the range [0, 255].\n", green);
if (blue < 0 || blue > 255)
FATAL_ERROR("Blue color component (%d) is outside the range [0, 255].\n", blue);
palette->colors[i].red = red;
palette->colors[i].green = green;
palette->colors[i].blue = blue;
}
if (fgetc(fp) != EOF)
FATAL_ERROR("Garbage after color data.\n");
fclose(fp);
}
void WriteJascPalette(char *path, struct Palette *palette)
{
FILE *fp = fopen(path, "wb");
fputs("JASC-PAL\r\n", fp);
fputs("0100\r\n", fp);
fprintf(fp, "%d\r\n", palette->numColors);
for (int i = 0; i < palette->numColors; i++)
{
struct Color *color = &palette->colors[i];
fprintf(fp, "%d %d %d\r\n", color->red, color->green, color->blue);
}
fclose(fp);
}

9
tools/gbagfx/jasc_pal.h Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) 2015 YamaArashi
#ifndef JASC_PAL_H
#define JASC_PAL_H
void ReadJascPalette(char *path, struct Palette *palette);
void WriteJascPalette(char *path, struct Palette *palette);
#endif // JASC_PAL_H

155
tools/gbagfx/lz.c Normal file
View File

@ -0,0 +1,155 @@
// Copyright (c) 2015 YamaArashi
#include <stdlib.h>
#include <stdbool.h>
#include "global.h"
#include "lz.h"
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
{
if (srcSize < 4)
goto fail;
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
unsigned char *dest = malloc(destSize);
if (dest == NULL)
goto fail;
int srcPos = 4;
int destPos = 0;
for (;;) {
if (srcPos >= srcSize)
goto fail;
unsigned char flags = src[srcPos++];
for (int i = 0; i < 8; i++) {
if (flags & 0x80) {
if (srcPos + 1 >= srcSize)
goto fail;
int blockSize = (src[srcPos] >> 4) + 3;
int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1;
srcPos += 2;
int blockPos = destPos - blockDistance;
// Some Ruby/Sapphire tilesets overflow.
if (destPos + blockSize > destSize) {
blockSize = destSize - destPos;
fprintf(stderr, "Destination buffer overflow.\n");
}
if (blockPos < 0)
goto fail;
for (int j = 0; j < blockSize; j++)
dest[destPos++] = dest[blockPos + j];
} else {
if (srcPos >= srcSize || destPos >= destSize)
goto fail;
dest[destPos++] = src[srcPos++];
}
if (destPos == destSize) {
*uncompressedSize = destSize;
return dest;
}
flags <<= 1;
}
}
fail:
FATAL_ERROR("Fatal error while decompressing LZ file.\n");
}
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize)
{
const int minDistance = 2; // for compatibility with LZ77UnCompVram()
if (srcSize <= 0)
goto fail;
int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8);
// Round up to the next multiple of four.
worstCaseDestSize = (worstCaseDestSize + 3) & ~3;
unsigned char *dest = malloc(worstCaseDestSize);
if (dest == NULL)
goto fail;
// header
dest[0] = 0x10; // LZ compression type
dest[1] = (unsigned char)srcSize;
dest[2] = (unsigned char)(srcSize >> 8);
dest[3] = (unsigned char)(srcSize >> 16);
int srcPos = 0;
int destPos = 4;
for (;;) {
unsigned char *flags = &dest[destPos++];
*flags = 0;
for (int i = 0; i < 8; i++) {
int bestBlockDistance = 0;
int bestBlockSize = 0;
int blockDistance = minDistance;
while (blockDistance <= srcPos && blockDistance <= 0x1000) {
int blockStart = srcPos - blockDistance;
int blockSize = 0;
while (blockSize < 18
&& srcPos + blockSize < srcSize
&& src[blockStart + blockSize] == src[srcPos + blockSize])
blockSize++;
if (blockSize > bestBlockSize) {
bestBlockDistance = blockDistance;
bestBlockSize = blockSize;
if (blockSize == 18)
break;
}
blockDistance++;
}
if (bestBlockSize >= 3) {
*flags |= (0x80 >> i);
srcPos += bestBlockSize;
bestBlockSize -= 3;
bestBlockDistance--;
dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8);
dest[destPos++] = (unsigned char)bestBlockDistance;
} else {
dest[destPos++] = src[srcPos++];
}
if (srcPos == srcSize) {
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (remainder != 0) {
for (int i = 0; i < 4 - remainder; i++)
dest[destPos++] = 0;
}
*compressedSize = destPos;
return dest;
}
}
}
fail:
FATAL_ERROR("Fatal error while compressing LZ file.\n");
}

9
tools/gbagfx/lz.h Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) 2015 YamaArashi
#ifndef LZ_H
#define LZ_H
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize);
#endif // LZ_H

465
tools/gbagfx/main.c Normal file
View File

@ -0,0 +1,465 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "global.h"
#include "util.h"
#include "options.h"
#include "gfx.h"
#include "convert_png.h"
#include "jasc_pal.h"
#include "lz.h"
#include "rl.h"
#include "font.h"
struct CommandHandler
{
const char *inputFileExtension;
const char *outputFileExtension;
void(*function)(char *inputPath, char *outputPath, int argc, char **argv);
};
void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *options)
{
struct Image image;
if (options->paletteFilePath != NULL)
{
ReadGbaPalette(options->paletteFilePath, &image.palette);
image.hasPalette = true;
}
else
{
image.hasPalette = false;
}
ReadImage(inputPath, options->width, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
image.hasTransparency = options->hasTransparency;
WritePng(outputPath, &image);
FreeImage(&image);
}
void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *options)
{
struct Image image;
image.bitDepth = options->bitDepth;
ReadPng(inputPath, &image);
WriteImage(outputPath, options->numTiles, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
FreeImage(&image);
}
void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
char *inputFileExtension = GetFileExtension(inputPath);
struct GbaToPngOptions options;
options.paletteFilePath = NULL;
options.bitDepth = inputFileExtension[0] - '0';
options.hasTransparency = false;
options.width = 1;
options.metatileWidth = 1;
options.metatileHeight = 1;
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-palette") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No palette file path following \"-palette\".\n");
i++;
options.paletteFilePath = argv[i];
}
else if (strcmp(option, "-object") == 0)
{
options.hasTransparency = true;
}
else if (strcmp(option, "-width") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No width following \"-width\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.width))
FATAL_ERROR("Failed to parse width.\n");
if (options.width < 1)
FATAL_ERROR("Width must be positive.\n");
}
else if (strcmp(option, "-mwidth") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No metatile width value following \"-mwidth\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.metatileWidth))
FATAL_ERROR("Failed to parse metatile width.\n");
if (options.metatileWidth < 1)
FATAL_ERROR("metatile width must be positive.\n");
}
else if (strcmp(option, "-mheight") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No metatile height value following \"-mheight\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.metatileHeight))
FATAL_ERROR("Failed to parse metatile height.\n");
if (options.metatileHeight < 1)
FATAL_ERROR("metatile height must be positive.\n");
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
if (options.metatileWidth > options.width)
options.width = options.metatileWidth;
ConvertGbaToPng(inputPath, outputPath, &options);
}
void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
char *outputFileExtension = GetFileExtension(outputPath);
int bitDepth = outputFileExtension[0] - '0';
struct PngToGbaOptions options;
options.numTiles = 0;
options.bitDepth = bitDepth;
options.metatileWidth = 1;
options.metatileHeight = 1;
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-num_tiles") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No number of tiles following \"-num_tiles\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.numTiles))
FATAL_ERROR("Failed to parse number of tiles.\n");
if (options.numTiles < 1)
FATAL_ERROR("Number of tiles must be positive.\n");
}
else if (strcmp(option, "-mwidth") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No metatile width value following \"-mwidth\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.metatileWidth))
FATAL_ERROR("Failed to parse metatile width.\n");
if (options.metatileWidth < 1)
FATAL_ERROR("metatile width must be positive.\n");
}
else if (strcmp(option, "-mheight") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No metatile height value following \"-mheight\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.metatileHeight))
FATAL_ERROR("Failed to parse metatile height.\n");
if (options.metatileHeight < 1)
FATAL_ERROR("metatile height must be positive.\n");
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
ConvertPngToGba(inputPath, outputPath, &options);
}
void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette;
ReadPngPalette(inputPath, &palette);
WriteGbaPalette(outputPath, &palette);
}
void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette;
ReadGbaPalette(inputPath, &palette);
WriteJascPalette(outputPath, &palette);
}
void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
int numColors = 0;
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-num_colors") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No number of colors following \"-num_colors\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &numColors))
FATAL_ERROR("Failed to parse number of colors.\n");
if (numColors < 1)
FATAL_ERROR("Number of colors must be positive.\n");
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
struct Palette palette;
ReadJascPalette(inputPath, &palette);
if (numColors != 0)
palette.numColors = numColors;
WriteGbaPalette(outputPath, &palette);
}
void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
ReadLatinFont(inputPath, &image);
WritePng(outputPath, &image);
FreeImage(&image);
}
void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
image.bitDepth = 2;
ReadPng(inputPath, &image);
WriteLatinFont(outputPath, &image);
FreeImage(&image);
}
void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
ReadHalfwidthJapaneseFont(inputPath, &image);
WritePng(outputPath, &image);
FreeImage(&image);
}
void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
image.bitDepth = 2;
ReadPng(inputPath, &image);
WriteHalfwidthJapaneseFont(outputPath, &image);
FreeImage(&image);
}
void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
ReadFullwidthJapaneseFont(inputPath, &image);
WritePng(outputPath, &image);
FreeImage(&image);
}
void HandlePngToFullwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
image.bitDepth = 2;
ReadPng(inputPath, &image);
WriteFullwidthJapaneseFont(outputPath, &image);
FreeImage(&image);
}
void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
int overflowSize = 0;
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-overflow") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No size following \"-overflow\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &overflowSize))
FATAL_ERROR("Failed to parse overflow size.\n");
if (overflowSize < 1)
FATAL_ERROR("Overflow size must be positive.\n");
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
// The overflow option allows a quirk in some of Ruby/Sapphire's tilesets
// to be reproduced. It works by appending a number of zeros to the data
// before compressing it and then amending the LZ header's size field to
// reflect the expected size. This will cause an overflow when decompressing
// the data.
int fileSize;
unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize);
int compressedSize;
unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize);
compressedData[1] = (unsigned char)fileSize;
compressedData[2] = (unsigned char)(fileSize >> 8);
compressedData[3] = (unsigned char)(fileSize >> 16);
free(buffer);
WriteWholeFile(outputPath, compressedData, compressedSize);
free(compressedData);
}
void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
int uncompressedSize;
unsigned char *uncompressedData = LZDecompress(buffer, fileSize, &uncompressedSize);
free(buffer);
WriteWholeFile(outputPath, uncompressedData, uncompressedSize);
free(uncompressedData);
}
void HandleRLCompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
int compressedSize;
unsigned char *compressedData = RLCompress(buffer, fileSize, &compressedSize);
free(buffer);
WriteWholeFile(outputPath, compressedData, compressedSize);
free(compressedData);
}
void HandleRLDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
int uncompressedSize;
unsigned char *uncompressedData = RLDecompress(buffer, fileSize, &uncompressedSize);
free(buffer);
WriteWholeFile(outputPath, uncompressedData, uncompressedSize);
free(uncompressedData);
}
int main(int argc, char **argv)
{
if (argc < 3)
FATAL_ERROR("Usage: gbagfx INPUT_PATH OUTPUT_PATH [options...]\n");
struct CommandHandler handlers[] =
{
{ "1bpp", "png", HandleGbaToPngCommand },
{ "4bpp", "png", HandleGbaToPngCommand },
{ "8bpp", "png", HandleGbaToPngCommand },
{ "png", "1bpp", HandlePngToGbaCommand },
{ "png", "4bpp", HandlePngToGbaCommand },
{ "png", "8bpp", HandlePngToGbaCommand },
{ "png", "gbapal", HandlePngToGbaPaletteCommand },
{ "gbapal", "pal", HandleGbaToJascPaletteCommand },
{ "pal", "gbapal", HandleJascToGbaPaletteCommand },
{ "latfont", "png", HandleLatinFontToPngCommand },
{ "png", "latfont", HandlePngToLatinFontCommand },
{ "hwjpnfont", "png", HandleHalfwidthJapaneseFontToPngCommand },
{ "png", "hwjpnfont", HandlePngToHalfwidthJapaneseFontCommand },
{ "fwjpnfont", "png", HandleFullwidthJapaneseFontToPngCommand },
{ "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand },
{ NULL, "lz", HandleLZCompressCommand },
{ "lz", NULL, HandleLZDecompressCommand },
{ NULL, "rl", HandleRLCompressCommand },
{ "rl", NULL, HandleRLDecompressCommand },
{ NULL, NULL, NULL }
};
char *inputPath = argv[1];
char *outputPath = argv[2];
char *inputFileExtension = GetFileExtension(inputPath);
char *outputFileExtension = GetFileExtension(outputPath);
if (inputFileExtension == NULL)
FATAL_ERROR("Input file \"%s\" has no extension.\n", inputPath);
if (outputFileExtension == NULL)
FATAL_ERROR("Output file \"%s\" has no extension.\n", outputPath);
for (int i = 0; handlers[i].function != NULL; i++)
{
if ((handlers[i].inputFileExtension == NULL || strcmp(handlers[i].inputFileExtension, inputFileExtension) == 0)
&& (handlers[i].outputFileExtension == NULL || strcmp(handlers[i].outputFileExtension, outputFileExtension) == 0))
{
handlers[i].function(inputPath, outputPath, argc, argv);
return 0;
}
}
FATAL_ERROR("Don't know how to convert \"%s\" to \"%s\".\n", inputPath, outputPath);
}

24
tools/gbagfx/options.h Normal file
View File

@ -0,0 +1,24 @@
// Copyright (c) 2018 huderlem
#ifndef OPTIONS_H
#define OPTIONS_H
#include <stdbool.h>
struct GbaToPngOptions {
char *paletteFilePath;
int bitDepth;
bool hasTransparency;
int width;
int metatileWidth;
int metatileHeight;
};
struct PngToGbaOptions {
int numTiles;
int bitDepth;
int metatileWidth;
int metatileHeight;
};
#endif // OPTIONS_H

149
tools/gbagfx/rl.c Normal file
View File

@ -0,0 +1,149 @@
// Copyright (c) 2016 YamaArashi
#include <stdlib.h>
#include <stdbool.h>
#include "global.h"
#include "rl.h"
unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
{
if (srcSize < 4)
goto fail;
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
unsigned char *dest = malloc(destSize);
if (dest == NULL)
goto fail;
int srcPos = 4;
int destPos = 0;
for (;;)
{
if (srcPos >= srcSize)
goto fail;
unsigned char flags = src[srcPos++];
bool compressed = ((flags & 0x80) != 0);
if (compressed)
{
int length = (flags & 0x7F) + 3;
unsigned char data = src[srcPos++];
if (destPos + length > destSize)
goto fail;
for (int i = 0; i < length; i++)
dest[destPos++] = data;
}
else
{
int length = (flags & 0x7F) + 1;
if (destPos + length > destSize)
goto fail;
for (int i = 0; i < length; i++)
dest[destPos++] = src[srcPos++];
}
if (destPos == destSize)
{
*uncompressedSize = destSize;
return dest;
}
}
fail:
FATAL_ERROR("Fatal error while decompressing RL file.\n");
}
unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize)
{
if (srcSize <= 0)
goto fail;
int worstCaseDestSize = 4 + srcSize * 2;
// Round up to the next multiple of four.
worstCaseDestSize = (worstCaseDestSize + 3) & ~3;
unsigned char *dest = malloc(worstCaseDestSize);
if (dest == NULL)
goto fail;
// header
dest[0] = 0x30; // RL compression type
dest[1] = (unsigned char)srcSize;
dest[2] = (unsigned char)(srcSize >> 8);
dest[3] = (unsigned char)(srcSize >> 16);
int srcPos = 0;
int destPos = 4;
for (;;)
{
bool compress = false;
int uncompressedStart = srcPos;
int uncompressedLength = 0;
while (srcPos < srcSize && uncompressedLength < (0x7F + 1))
{
compress = (srcPos + 2 < srcSize && src[srcPos] == src[srcPos + 1] && src[srcPos] == src[srcPos + 2]);
if (compress)
break;
srcPos++;
uncompressedLength++;
}
if (uncompressedLength > 0)
{
dest[destPos++] = uncompressedLength - 1;
for (int i = 0; i < uncompressedLength; i++)
dest[destPos++] = src[uncompressedStart + i];
}
if (compress)
{
unsigned char data = src[srcPos];
int compressedLength = 0;
while (compressedLength < (0x7F + 3)
&& srcPos + compressedLength < srcSize
&& src[srcPos + compressedLength] == data)
{
compressedLength++;
}
dest[destPos++] = 0x80 | (compressedLength - 3);
dest[destPos++] = data;
srcPos += compressedLength;
}
if (srcPos == srcSize)
{
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (remainder != 0)
{
for (int i = 0; i < 4 - remainder; i++)
dest[destPos++] = 0;
}
*compressedSize = destPos;
return dest;
}
}
fail:
FATAL_ERROR("Fatal error while compressing RL file.\n");
}

9
tools/gbagfx/rl.h Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) 2016 YamaArashi
#ifndef RL_H
#define RL_H
unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize);
#endif // RL_H

124
tools/gbagfx/util.c Normal file
View File

@ -0,0 +1,124 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <limits.h>
#include "global.h"
#include "util.h"
bool ParseNumber(char *s, char **end, int radix, int *intValue)
{
char *localEnd;
if (end == NULL)
end = &localEnd;
errno = 0;
const long longValue = strtol(s, end, radix);
if (*end == s)
return false; // not a number
if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE)
return false;
if (longValue > INT_MAX)
return false;
if (longValue < INT_MIN)
return false;
*intValue = (int)longValue;
return true;
}
char *GetFileExtension(char *path)
{
char *extension = path;
while (*extension != 0)
extension++;
while (extension > path && *extension != '.')
extension--;
if (extension == path)
return NULL;
extension++;
if (*extension == 0)
return NULL;
return extension;
}
unsigned char *ReadWholeFile(char *path, int *size)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = malloc(*size);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = calloc(*size + padAmount, 1);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
void WriteWholeFile(char *path, void *buffer, int bufferSize)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
if (fwrite(buffer, bufferSize, 1, fp) != 1)
FATAL_ERROR("Failed to write to \"%s\".\n", path);
fclose(fp);
}

14
tools/gbagfx/util.h Normal file
View File

@ -0,0 +1,14 @@
// Copyright (c) 2015 YamaArashi
#ifndef UTIL_H
#define UTIL_H
#include <stdbool.h>
bool ParseNumber(char *s, char **end, int radix, int *intValue);
char *GetFileExtension(char *path);
unsigned char *ReadWholeFile(char *path, int *size);
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount);
void WriteWholeFile(char *path, void *buffer, int bufferSize);
#endif // UTIL_H

1
tools/jsonproc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
jsonproc

20
tools/jsonproc/Makefile Normal file
View File

@ -0,0 +1,20 @@
CXX := g++
CXXFLAGS := -Wall -std=c++11 -O2
INCLUDES := -I .
SRCS := jsonproc.cpp
HEADERS := jsonproc.h inja.hpp nlohmann/json.hpp
.PHONY: all clean
all: jsonproc
@:
jsonproc: $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(INCLUDES) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) jsonproc jsonproc.exe

3396
tools/jsonproc/inja.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
// jsonproc.cpp
#include "jsonproc.h"
#include <map>
#include <string>
using std::string;
#include <inja.hpp>
using namespace inja;
using json = nlohmann::json;
std::map<string, string> customVars;
void set_custom_var(string key, string value)
{
customVars[key] = value;
}
string get_custom_var(string key)
{
return customVars[key];
}
int main(int argc, char *argv[])
{
if (argc != 4)
FATAL_ERROR("USAGE: jsonproc <json-filepath> <template-filepath> <output-filepath>\n");
string jsonfilepath = argv[1];
string templateFilepath = argv[2];
string outputFilepath = argv[3];
Environment env;
// Add custom command callbacks.
env.add_callback("doNotModifyHeader", 0, [jsonfilepath, templateFilepath](Arguments& args) {
return "//\n// DO NOT MODIFY THIS FILE! IT IS AUTO-GENERATED FROM " + jsonfilepath +" and Inja template " + templateFilepath + "\n//\n";
});
env.add_callback("setVar", 2, [=](Arguments& args) {
string key = args.at(0)->get<string>();
string value = args.at(1)->get<string>();
set_custom_var(key, value);
return "";
});
env.add_callback("getVar", 1, [=](Arguments& args) {
string key = args.at(0)->get<string>();
return get_custom_var(key);
});
env.add_callback("concat", 2, [](Arguments& args) {
string first = args.at(0)->get<string>();
string second = args.at(1)->get<string>();
return first + second;
});
env.add_callback("removePrefix", 2, [](Arguments& args) {
string rawValue = args.at(0)->get<string>();
string prefix = args.at(1)->get<string>();
string::size_type i = rawValue.find(prefix);
if (i != 0)
return rawValue;
return rawValue.erase(0, prefix.length());
});
// Add custom command callbacks.
env.add_callback("removeSuffix", 2, [](Arguments& args) {
string rawValue = args.at(0)->get<string>();
string suffix = args.at(1)->get<string>();
string::size_type i = rawValue.rfind(suffix);
if (i == string::npos)
return rawValue;
return rawValue.substr(0, i);
});
try
{
env.write_with_json_file(templateFilepath, jsonfilepath, outputFilepath);
}
catch (const std::exception& e)
{
FATAL_ERROR("JSONPROC_ERROR: %s\n", e.what());
}
return 0;
}

32
tools/jsonproc/jsonproc.h Normal file
View File

@ -0,0 +1,32 @@
// jsonproc.h
#ifndef JSONPROC_H
#define JSONPROC_H
#include <cstdlib>
#include <cstdio>
using std::fprintf; using std::exit;
#include <cstdlib>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
#endif // JSONPROC_H

File diff suppressed because it is too large Load Diff

1
tools/mapjson/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
mapjson

18
tools/mapjson/Makefile Normal file
View File

@ -0,0 +1,18 @@
CXX := g++
CXXFLAGS := -Wall -std=c++11 -O2
SRCS := json11.cpp mapjson.cpp
HEADERS := mapjson.h
.PHONY: all clean
all: mapjson
@:
mapjson: $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) mapjson mapjson.exe

786
tools/mapjson/json11.cpp Normal file
View File

@ -0,0 +1,786 @@
/* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "json11.h"
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <limits>
namespace json11 {
static const int max_depth = 200;
using std::string;
using std::vector;
using std::map;
using std::make_shared;
using std::initializer_list;
using std::move;
/* Helper for representing null - just a do-nothing struct, plus comparison
* operators so the helpers in JsonValue work. We can't use nullptr_t because
* it may not be orderable.
*/
struct NullStruct {
bool operator==(NullStruct) const { return true; }
bool operator<(NullStruct) const { return false; }
};
/* * * * * * * * * * * * * * * * * * * *
* Serialization
*/
static void dump(NullStruct, string &out) {
out += "null";
}
static void dump(double value, string &out) {
if (std::isfinite(value)) {
char buf[32];
snprintf(buf, sizeof buf, "%.17g", value);
out += buf;
} else {
out += "null";
}
}
static void dump(int value, string &out) {
char buf[32];
snprintf(buf, sizeof buf, "%d", value);
out += buf;
}
static void dump(bool value, string &out) {
out += value ? "true" : "false";
}
static void dump(const string &value, string &out) {
out += '"';
for (size_t i = 0; i < value.length(); i++) {
const char ch = value[i];
if (ch == '\\') {
out += "\\\\";
} else if (ch == '"') {
out += "\\\"";
} else if (ch == '\b') {
out += "\\b";
} else if (ch == '\f') {
out += "\\f";
} else if (ch == '\n') {
out += "\\n";
} else if (ch == '\r') {
out += "\\r";
} else if (ch == '\t') {
out += "\\t";
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
char buf[8];
snprintf(buf, sizeof buf, "\\u%04x", ch);
out += buf;
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
out += "\\u2028";
i += 2;
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
out += "\\u2029";
i += 2;
} else {
out += ch;
}
}
out += '"';
}
static void dump(const Json::array &values, string &out) {
bool first = true;
out += "[";
for (const auto &value : values) {
if (!first)
out += ", ";
value.dump(out);
first = false;
}
out += "]";
}
static void dump(const Json::object &values, string &out) {
bool first = true;
out += "{";
for (const auto &kv : values) {
if (!first)
out += ", ";
dump(kv.first, out);
out += ": ";
kv.second.dump(out);
first = false;
}
out += "}";
}
void Json::dump(string &out) const {
m_ptr->dump(out);
}
/* * * * * * * * * * * * * * * * * * * *
* Value wrappers
*/
template <Json::Type tag, typename T>
class Value : public JsonValue {
protected:
// Constructors
explicit Value(const T &value) : m_value(value) {}
explicit Value(T &&value) : m_value(move(value)) {}
// Get type tag
Json::Type type() const override {
return tag;
}
// Comparisons
bool equals(const JsonValue * other) const override {
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
}
bool less(const JsonValue * other) const override {
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
}
const T m_value;
void dump(string &out) const override { json11::dump(m_value, out); }
};
class JsonDouble final : public Value<Json::NUMBER, double> {
double number_value() const override { return m_value; }
int int_value() const override { return static_cast<int>(m_value); }
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
public:
explicit JsonDouble(double value) : Value(value) {}
};
class JsonInt final : public Value<Json::NUMBER, int> {
double number_value() const override { return m_value; }
int int_value() const override { return m_value; }
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
public:
explicit JsonInt(int value) : Value(value) {}
};
class JsonBoolean final : public Value<Json::BOOL, bool> {
bool bool_value() const override { return m_value; }
public:
explicit JsonBoolean(bool value) : Value(value) {}
};
class JsonString final : public Value<Json::STRING, string> {
const string &string_value() const override { return m_value; }
public:
explicit JsonString(const string &value) : Value(value) {}
explicit JsonString(string &&value) : Value(move(value)) {}
};
class JsonArray final : public Value<Json::ARRAY, Json::array> {
const Json::array &array_items() const override { return m_value; }
const Json & operator[](size_t i) const override;
public:
explicit JsonArray(const Json::array &value) : Value(value) {}
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
};
class JsonObject final : public Value<Json::OBJECT, Json::object> {
const Json::object &object_items() const override { return m_value; }
const Json & operator[](const string &key) const override;
public:
explicit JsonObject(const Json::object &value) : Value(value) {}
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
};
class JsonNull final : public Value<Json::NUL, NullStruct> {
public:
JsonNull() : Value({}) {}
};
/* * * * * * * * * * * * * * * * * * * *
* Static globals - static-init-safe
*/
struct Statics {
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
const string empty_string;
const vector<Json> empty_vector;
const map<string, Json> empty_map;
Statics() {}
};
static const Statics & statics() {
static const Statics s {};
return s;
}
static const Json & static_null() {
// This has to be separate, not in Statics, because Json() accesses statics().null.
static const Json json_null;
return json_null;
}
/* * * * * * * * * * * * * * * * * * * *
* Constructors
*/
Json::Json() noexcept : m_ptr(statics().null) {}
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
/* * * * * * * * * * * * * * * * * * * *
* Accessors
*/
Json::Type Json::type() const { return m_ptr->type(); }
double Json::number_value() const { return m_ptr->number_value(); }
int Json::int_value() const { return m_ptr->int_value(); }
bool Json::bool_value() const { return m_ptr->bool_value(); }
const string & Json::string_value() const { return m_ptr->string_value(); }
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
double JsonValue::number_value() const { return 0; }
int JsonValue::int_value() const { return 0; }
bool JsonValue::bool_value() const { return false; }
const string & JsonValue::string_value() const { return statics().empty_string; }
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
const Json & JsonObject::operator[] (const string &key) const {
auto iter = m_value.find(key);
return (iter == m_value.end()) ? static_null() : iter->second;
}
const Json & JsonArray::operator[] (size_t i) const {
if (i >= m_value.size()) return static_null();
else return m_value[i];
}
/* * * * * * * * * * * * * * * * * * * *
* Comparison
*/
bool Json::operator== (const Json &other) const {
if (m_ptr == other.m_ptr)
return true;
if (m_ptr->type() != other.m_ptr->type())
return false;
return m_ptr->equals(other.m_ptr.get());
}
bool Json::operator< (const Json &other) const {
if (m_ptr == other.m_ptr)
return false;
if (m_ptr->type() != other.m_ptr->type())
return m_ptr->type() < other.m_ptr->type();
return m_ptr->less(other.m_ptr.get());
}
/* * * * * * * * * * * * * * * * * * * *
* Parsing
*/
/* esc(c)
*
* Format char c suitable for printing in an error message.
*/
static inline string esc(char c) {
char buf[12];
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
} else {
snprintf(buf, sizeof buf, "(%d)", c);
}
return string(buf);
}
static inline bool in_range(long x, long lower, long upper) {
return (x >= lower && x <= upper);
}
namespace {
/* JsonParser
*
* Object that tracks all state of an in-progress parse.
*/
struct JsonParser final {
/* State
*/
const string &str;
size_t i;
string &err;
bool failed;
const JsonParse strategy;
/* fail(msg, err_ret = Json())
*
* Mark this parse as failed.
*/
Json fail(string &&msg) {
return fail(move(msg), Json());
}
template <typename T>
T fail(string &&msg, const T err_ret) {
if (!failed)
err = std::move(msg);
failed = true;
return err_ret;
}
/* consume_whitespace()
*
* Advance until the current character is non-whitespace.
*/
void consume_whitespace() {
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
i++;
}
/* consume_comment()
*
* Advance comments (c-style inline and multiline).
*/
bool consume_comment() {
bool comment_found = false;
if (str[i] == '/') {
i++;
if (i == str.size())
return fail("unexpected end of input after start of comment", false);
if (str[i] == '/') { // inline comment
i++;
// advance until next line, or end of input
while (i < str.size() && str[i] != '\n') {
i++;
}
comment_found = true;
}
else if (str[i] == '*') { // multiline comment
i++;
if (i > str.size()-2)
return fail("unexpected end of input inside multi-line comment", false);
// advance until closing tokens
while (!(str[i] == '*' && str[i+1] == '/')) {
i++;
if (i > str.size()-2)
return fail(
"unexpected end of input inside multi-line comment", false);
}
i += 2;
comment_found = true;
}
else
return fail("malformed comment", false);
}
return comment_found;
}
/* consume_garbage()
*
* Advance until the current character is non-whitespace and non-comment.
*/
void consume_garbage() {
consume_whitespace();
if(strategy == JsonParse::COMMENTS) {
bool comment_found = false;
do {
comment_found = consume_comment();
if (failed) return;
consume_whitespace();
}
while(comment_found);
}
}
/* get_next_token()
*
* Return the next non-whitespace character. If the end of the input is reached,
* flag an error and return 0.
*/
char get_next_token() {
consume_garbage();
if (failed) return static_cast<char>(0);
if (i == str.size())
return fail("unexpected end of input", static_cast<char>(0));
return str[i++];
}
/* encode_utf8(pt, out)
*
* Encode pt as UTF-8 and add it to out.
*/
void encode_utf8(long pt, string & out) {
if (pt < 0)
return;
if (pt < 0x80) {
out += static_cast<char>(pt);
} else if (pt < 0x800) {
out += static_cast<char>((pt >> 6) | 0xC0);
out += static_cast<char>((pt & 0x3F) | 0x80);
} else if (pt < 0x10000) {
out += static_cast<char>((pt >> 12) | 0xE0);
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
out += static_cast<char>((pt & 0x3F) | 0x80);
} else {
out += static_cast<char>((pt >> 18) | 0xF0);
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
out += static_cast<char>((pt & 0x3F) | 0x80);
}
}
/* parse_string()
*
* Parse a string, starting at the current position.
*/
string parse_string() {
string out;
long last_escaped_codepoint = -1;
while (true) {
if (i == str.size())
return fail("unexpected end of input in string", "");
char ch = str[i++];
if (ch == '"') {
encode_utf8(last_escaped_codepoint, out);
return out;
}
if (in_range(ch, 0, 0x1f))
return fail("unescaped " + esc(ch) + " in string", "");
// The usual case: non-escaped characters
if (ch != '\\') {
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = -1;
out += ch;
continue;
}
// Handle escapes
if (i == str.size())
return fail("unexpected end of input in string", "");
ch = str[i++];
if (ch == 'u') {
// Extract 4-byte escape sequence
string esc = str.substr(i, 4);
// Explicitly check length of the substring. The following loop
// relies on std::string returning the terminating NUL when
// accessing str[length]. Checking here reduces brittleness.
if (esc.length() < 4) {
return fail("bad \\u escape: " + esc, "");
}
for (size_t j = 0; j < 4; j++) {
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
&& !in_range(esc[j], '0', '9'))
return fail("bad \\u escape: " + esc, "");
}
long codepoint = strtol(esc.data(), nullptr, 16);
// JSON specifies that characters outside the BMP shall be encoded as a pair
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
// whether we're in the middle of such a beast: the previous codepoint was an
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
// Reassemble the two surrogate pairs into one astral-plane character, per
// the UTF-16 algorithm.
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
| (codepoint - 0xDC00)) + 0x10000, out);
last_escaped_codepoint = -1;
} else {
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = codepoint;
}
i += 4;
continue;
}
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = -1;
if (ch == 'b') {
out += '\b';
} else if (ch == 'f') {
out += '\f';
} else if (ch == 'n') {
out += '\n';
} else if (ch == 'r') {
out += '\r';
} else if (ch == 't') {
out += '\t';
} else if (ch == '"' || ch == '\\' || ch == '/') {
out += ch;
} else {
return fail("invalid escape character " + esc(ch), "");
}
}
}
/* parse_number()
*
* Parse a double.
*/
Json parse_number() {
size_t start_pos = i;
if (str[i] == '-')
i++;
// Integer part
if (str[i] == '0') {
i++;
if (in_range(str[i], '0', '9'))
return fail("leading 0s not permitted in numbers");
} else if (in_range(str[i], '1', '9')) {
i++;
while (in_range(str[i], '0', '9'))
i++;
} else {
return fail("invalid " + esc(str[i]) + " in number");
}
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
return std::atoi(str.c_str() + start_pos);
}
// Decimal part
if (str[i] == '.') {
i++;
if (!in_range(str[i], '0', '9'))
return fail("at least one digit required in fractional part");
while (in_range(str[i], '0', '9'))
i++;
}
// Exponent part
if (str[i] == 'e' || str[i] == 'E') {
i++;
if (str[i] == '+' || str[i] == '-')
i++;
if (!in_range(str[i], '0', '9'))
return fail("at least one digit required in exponent");
while (in_range(str[i], '0', '9'))
i++;
}
return std::strtod(str.c_str() + start_pos, nullptr);
}
/* expect(str, res)
*
* Expect that 'str' starts at the character that was just read. If it does, advance
* the input and return res. If not, flag an error.
*/
Json expect(const string &expected, Json res) {
assert(i != 0);
i--;
if (str.compare(i, expected.length(), expected) == 0) {
i += expected.length();
return res;
} else {
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
}
}
/* parse_json()
*
* Parse a JSON object.
*/
Json parse_json(int depth) {
if (depth > max_depth) {
return fail("exceeded maximum nesting depth");
}
char ch = get_next_token();
if (failed)
return Json();
if (ch == '-' || (ch >= '0' && ch <= '9')) {
i--;
return parse_number();
}
if (ch == 't')
return expect("true", true);
if (ch == 'f')
return expect("false", false);
if (ch == 'n')
return expect("null", Json());
if (ch == '"')
return parse_string();
if (ch == '{') {
map<string, Json> data;
ch = get_next_token();
if (ch == '}')
return data;
while (1) {
if (ch != '"')
return fail("expected '\"' in object, got " + esc(ch));
string key = parse_string();
if (failed)
return Json();
ch = get_next_token();
if (ch != ':')
return fail("expected ':' in object, got " + esc(ch));
data[std::move(key)] = parse_json(depth + 1);
if (failed)
return Json();
ch = get_next_token();
if (ch == '}')
break;
if (ch != ',')
return fail("expected ',' in object, got " + esc(ch));
ch = get_next_token();
}
return data;
}
if (ch == '[') {
vector<Json> data;
ch = get_next_token();
if (ch == ']')
return data;
while (1) {
i--;
data.push_back(parse_json(depth + 1));
if (failed)
return Json();
ch = get_next_token();
if (ch == ']')
break;
if (ch != ',')
return fail("expected ',' in list, got " + esc(ch));
ch = get_next_token();
(void)ch;
}
return data;
}
return fail("expected value, got " + esc(ch));
}
};
}//namespace {
Json Json::parse(const string &in, string &err, JsonParse strategy) {
JsonParser parser { in, 0, err, false, strategy };
Json result = parser.parse_json(0);
// Check for any trailing garbage
parser.consume_garbage();
if (parser.failed)
return Json();
if (parser.i != in.size())
return parser.fail("unexpected trailing " + esc(in[parser.i]));
return result;
}
// Documented in json11.hpp
vector<Json> Json::parse_multi(const string &in,
std::string::size_type &parser_stop_pos,
string &err,
JsonParse strategy) {
JsonParser parser { in, 0, err, false, strategy };
parser_stop_pos = 0;
vector<Json> json_vec;
while (parser.i != in.size() && !parser.failed) {
json_vec.push_back(parser.parse_json(0));
if (parser.failed)
break;
// Check for another object
parser.consume_garbage();
if (parser.failed)
break;
parser_stop_pos = parser.i;
}
return json_vec;
}
/* * * * * * * * * * * * * * * * * * * *
* Shape-checking
*/
bool Json::has_shape(const shape & types, string & err) const {
if (!is_object()) {
err = "expected JSON object, got " + dump();
return false;
}
for (auto & item : types) {
if ((*this)[item.first].type() != item.second) {
err = "bad type for " + item.first + " in " + dump();
return false;
}
}
return true;
}
} // namespace json11

230
tools/mapjson/json11.h Normal file
View File

@ -0,0 +1,230 @@
/* json11
*
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
*
* The core object provided by the library is json11::Json. A Json object represents any JSON
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
* object (std::map).
*
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
* Json::parse (static) to parse a std::string as a Json object.
*
* Internally, the various types of Json object are represented by the JsonValue class
* hierarchy.
*
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
* so some JSON implementations distinguish between integers and floating-point numbers, while
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
* to JSON that will be *silently* changed by a round-trip through those implementations.
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
* provides integer helpers.
*
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
* will be exact for +/- 275 years.)
*/
/* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <initializer_list>
#ifdef _MSC_VER
#if _MSC_VER <= 1800 // VS 2013
#ifndef noexcept
#define noexcept throw()
#endif
#ifndef snprintf
#define snprintf _snprintf_s
#endif
#endif
#endif
namespace json11 {
enum JsonParse {
STANDARD, COMMENTS
};
class JsonValue;
class Json final {
public:
// Types
enum Type {
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
};
// Array and object typedefs
typedef std::vector<Json> array;
typedef std::map<std::string, Json> object;
// Constructors for the various types of JSON value.
Json() noexcept; // NUL
Json(std::nullptr_t) noexcept; // NUL
Json(double value); // NUMBER
Json(int value); // NUMBER
Json(bool value); // BOOL
Json(const std::string &value); // STRING
Json(std::string &&value); // STRING
Json(const char * value); // STRING
Json(const array &values); // ARRAY
Json(array &&values); // ARRAY
Json(const object &values); // OBJECT
Json(object &&values); // OBJECT
// Implicit constructor: anything with a to_json() function.
template <class T, class = decltype(&T::to_json)>
Json(const T & t) : Json(t.to_json()) {}
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
template <class M, typename std::enable_if<
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
int>::type = 0>
Json(const M & m) : Json(object(m.begin(), m.end())) {}
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
template <class V, typename std::enable_if<
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
int>::type = 0>
Json(const V & v) : Json(array(v.begin(), v.end())) {}
// This prevents Json(some_pointer) from accidentally producing a bool. Use
// Json(bool(some_pointer)) if that behavior is desired.
Json(void *) = delete;
// Accessors
Type type() const;
bool is_null() const { return type() == NUL; }
bool is_number() const { return type() == NUMBER; }
bool is_bool() const { return type() == BOOL; }
bool is_string() const { return type() == STRING; }
bool is_array() const { return type() == ARRAY; }
bool is_object() const { return type() == OBJECT; }
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
// distinguish between integer and non-integer numbers - number_value() and int_value()
// can both be applied to a NUMBER-typed object.
double number_value() const;
int int_value() const;
// Return the enclosed value if this is a boolean, false otherwise.
bool bool_value() const;
// Return the enclosed string if this is a string, "" otherwise.
const std::string &string_value() const;
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
const array &array_items() const;
// Return the enclosed std::map if this is an object, or an empty map otherwise.
const object &object_items() const;
// Return a reference to arr[i] if this is an array, Json() otherwise.
const Json & operator[](size_t i) const;
// Return a reference to obj[key] if this is an object, Json() otherwise.
const Json & operator[](const std::string &key) const;
// Serialize.
void dump(std::string &out) const;
std::string dump() const {
std::string out;
dump(out);
return out;
}
// Parse. If parse fails, return Json() and assign an error message to err.
static Json parse(const std::string & in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD);
static Json parse(const char * in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD) {
if (in) {
return parse(std::string(in), err, strategy);
} else {
err = "null input";
return nullptr;
}
}
// Parse multiple objects, concatenated or separated by whitespace
static std::vector<Json> parse_multi(
const std::string & in,
std::string::size_type & parser_stop_pos,
std::string & err,
JsonParse strategy = JsonParse::STANDARD);
static inline std::vector<Json> parse_multi(
const std::string & in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD) {
std::string::size_type parser_stop_pos;
return parse_multi(in, parser_stop_pos, err, strategy);
}
bool operator== (const Json &rhs) const;
bool operator< (const Json &rhs) const;
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
bool operator> (const Json &rhs) const { return (rhs < *this); }
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
/* has_shape(types, err)
*
* Return true if this is a JSON object and, for each item in types, has a field of
* the given type. If not, return false and set err to a descriptive message.
*/
typedef std::initializer_list<std::pair<std::string, Type>> shape;
bool has_shape(const shape & types, std::string & err) const;
private:
std::shared_ptr<JsonValue> m_ptr;
};
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
class JsonValue {
protected:
friend class Json;
friend class JsonInt;
friend class JsonDouble;
virtual Json::Type type() const = 0;
virtual bool equals(const JsonValue * other) const = 0;
virtual bool less(const JsonValue * other) const = 0;
virtual void dump(std::string &out) const = 0;
virtual double number_value() const;
virtual int int_value() const;
virtual bool bool_value() const;
virtual const std::string &string_value() const;
virtual const Json::array &array_items() const;
virtual const Json &operator[](size_t i) const;
virtual const Json::object &object_items() const;
virtual const Json &operator[](const std::string &key) const;
virtual ~JsonValue() {}
};
} // namespace json11

538
tools/mapjson/mapjson.cpp Normal file
View File

@ -0,0 +1,538 @@
// mapjson.cpp
#include <iostream>
using std::cout; using std::endl;
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <algorithm>
using std::sort; using std::find;
#include <map>
using std::map;
#include <fstream>
using std::ofstream; using std::ifstream;
#include <sstream>
using std::ostringstream;
#include <limits>
using std::numeric_limits;
#include "json11.h"
using json11::Json;
#include "mapjson.h"
string read_text_file(string filepath) {
ifstream in_file(filepath);
if (!in_file.is_open())
FATAL_ERROR("Cannot open file %s for reading.\n", filepath.c_str());
string text;
in_file.seekg(0, std::ios::end);
text.resize(in_file.tellg());
in_file.seekg(0, std::ios::beg);
in_file.read(&text[0], text.size());
in_file.close();
return text;
}
void write_text_file(string filepath, string text) {
ofstream out_file(filepath, std::ofstream::binary);
if (!out_file.is_open())
FATAL_ERROR("Cannot open file %s for writing.\n", filepath.c_str());
out_file << text;
out_file.close();
}
string generate_map_header_text(Json map_data, Json layouts_data, string version) {
string map_layout_id = map_data["layout"].string_value();
vector<Json> matched;
for (auto &field : layouts_data["layouts"].array_items()) {
if (map_layout_id == field["id"].string_value())
matched.push_back(field);
}
if (matched.size() != 1)
FATAL_ERROR("Failed to find matching layout for %s.\n", map_layout_id.c_str());
Json layout = matched[0];
ostringstream text;
text << map_data["name"].string_value() << ":\n"
<< "\t.4byte " << layout["name"].string_value() << "\n";
if (map_data.object_items().find("shared_events_map") != map_data.object_items().end())
text << "\t.4byte " << map_data["shared_events_map"].string_value() << "_MapEvents\n";
else
text << "\t.4byte " << map_data["name"].string_value() << "_MapEvents\n";
if (map_data.object_items().find("shared_scripts_map") != map_data.object_items().end())
text << "\t.4byte " << map_data["shared_scripts_map"].string_value() << "_MapScripts\n";
else
text << "\t.4byte " << map_data["name"].string_value() << "_MapScripts\n";
if (map_data.object_items().find("connections") != map_data.object_items().end()
&& map_data["connections"].array_items().size() > 0)
text << "\t.4byte " << map_data["name"].string_value() << "_MapConnections\n";
else
text << "\t.4byte 0x0\n";
text << "\t.2byte " << map_data["music"].string_value() << "\n"
<< "\t.2byte " << layout["id"].string_value() << "\n"
<< "\t.byte " << map_data["region_map_section"].string_value() << "\n"
<< "\t.byte " << map_data["requires_flash"].bool_value() << "\n"
<< "\t.byte " << map_data["weather"].string_value() << "\n"
<< "\t.byte " << map_data["map_type"].string_value() << "\n"
<< "\t.2byte 0\n";
if (version == "ruby")
text << "\t.byte " << map_data["show_map_name"].bool_value() << "\n";
else if (version == "emerald")
text << "\tmap_header_flags "
<< "allow_bike=" << map_data["allow_bike"].bool_value() << ", "
<< "allow_escape_rope=" << map_data["allow_escape_rope"].bool_value() << ", "
<< "allow_run=" << map_data["allow_running"].bool_value() << ", "
<< "show_map_name=" << map_data["show_map_name"].bool_value() << "\n";
text << "\t.byte " << map_data["battle_scene"].string_value() << "\n\n";
return text.str();
}
string generate_map_connections_text(Json map_data) {
if (map_data["connections"] == Json())
return string("\n");
ostringstream text;
text << map_data["name"].string_value() << "_MapConnectionsList:\n";
for (auto &connection : map_data["connections"].array_items()) {
text << "\tconnection "
<< connection["direction"].string_value() << ", "
<< connection["offset"].int_value() << ", "
<< connection["map"].string_value() << "\n";
}
text << "\n" << map_data["name"].string_value() << "_MapConnections:\n"
<< "\t.4byte " << map_data["connections"].array_items().size() << "\n"
<< "\t.4byte " << map_data["name"].string_value() << "_MapConnectionsList\n\n";
return text.str();
}
string generate_map_events_text(Json map_data) {
if (map_data.object_items().find("shared_events_map") != map_data.object_items().end())
return string("\n");
ostringstream text;
string objects_label, warps_label, coords_label, bgs_label;
if (map_data["object_events"].array_items().size() > 0) {
objects_label = map_data["name"].string_value() + "_EventObjects";
text << objects_label << ":\n";
for (unsigned int i = 0; i < map_data["object_events"].array_items().size(); i++) {
auto obj_event = map_data["object_events"].array_items()[i];
text << "\tobject_event " << i + 1 << ", "
<< obj_event["graphics_id"].string_value() << ", 0, "
<< obj_event["x"].int_value() << ", "
<< obj_event["y"].int_value() << ", "
<< obj_event["elevation"].int_value() << ", "
<< obj_event["movement_type"].string_value() << ", "
<< obj_event["movement_range_x"].int_value() << ", "
<< obj_event["movement_range_y"].int_value() << ", "
<< obj_event["trainer_type"].string_value() << ", "
<< obj_event["trainer_sight_or_berry_tree_id"].string_value() << ", "
<< obj_event["script"].string_value() << ", "
<< obj_event["flag"].string_value() << "\n";
}
text << "\n";
} else {
objects_label = "0x0";
}
if (map_data["warp_events"].array_items().size() > 0) {
warps_label = map_data["name"].string_value() + "_MapWarps";
text << warps_label << ":\n";
for (auto &warp_event : map_data["warp_events"].array_items()) {
text << "\twarp_def "
<< warp_event["x"].int_value() << ", "
<< warp_event["y"].int_value() << ", "
<< warp_event["elevation"].int_value() << ", "
<< warp_event["dest_warp_id"].int_value() << ", "
<< warp_event["dest_map"].string_value() << "\n";
}
text << "\n";
} else {
warps_label = "0x0";
}
if (map_data["coord_events"].array_items().size() > 0) {
coords_label = map_data["name"].string_value() + "_MapCoordEvents";
text << coords_label << ":\n";
for (auto &coord_event : map_data["coord_events"].array_items()) {
if (coord_event["type"].string_value() == "trigger") {
text << "\tcoord_event "
<< coord_event["x"].int_value() << ", "
<< coord_event["y"].int_value() << ", "
<< coord_event["elevation"].int_value() << ", "
<< coord_event["var"].string_value() << ", "
<< coord_event["var_value"].string_value() << ", "
<< coord_event["script"].string_value() << "\n";
}
else if (coord_event["type"] == "weather") {
text << "\tcoord_weather_event "
<< coord_event["x"].int_value() << ", "
<< coord_event["y"].int_value() << ", "
<< coord_event["elevation"].int_value() << ", "
<< coord_event["weather"].string_value() << "\n";
}
}
text << "\n";
} else {
coords_label = "0x0";
}
if (map_data["bg_events"].array_items().size() > 0) {
bgs_label = map_data["name"].string_value() + "_MapBGEvents";
text << bgs_label << ":\n";
for (auto &bg_event : map_data["bg_events"].array_items()) {
if (bg_event["type"] == "sign") {
text << "\tbg_event "
<< bg_event["x"].int_value() << ", "
<< bg_event["y"].int_value() << ", "
<< bg_event["elevation"].int_value() << ", "
<< bg_event["player_facing_dir"].string_value() << ", "
<< bg_event["script"].string_value() << "\n";
}
else if (bg_event["type"] == "hidden_item") {
text << "\tbg_hidden_item_event "
<< bg_event["x"].int_value() << ", "
<< bg_event["y"].int_value() << ", "
<< bg_event["elevation"].int_value() << ", "
<< bg_event["item"].string_value() << ", "
<< bg_event["flag"].string_value() << "\n";
}
else if (bg_event["type"] == "secret_base") {
text << "\tbg_secret_base_event "
<< bg_event["x"].int_value() << ", "
<< bg_event["y"].int_value() << ", "
<< bg_event["elevation"].int_value() << ", "
<< bg_event["secret_base_id"].string_value() << "\n";
}
}
text << "\n";
} else {
bgs_label = "0x0";
}
text << map_data["name"].string_value() << "_MapEvents::\n"
<< "\tmap_events " << objects_label << ", " << warps_label << ", "
<< coords_label << ", " << bgs_label << "\n\n";
return text.str();
}
string get_directory_name(string filename) {
size_t dir_pos = filename.find_last_of("/\\");
return filename.substr(0, dir_pos + 1);
}
void process_map(string map_filepath, string layouts_filepath, string version) {
string mapdata_err, layouts_err;
string mapdata_json_text = read_text_file(map_filepath);
string layouts_json_text = read_text_file(layouts_filepath);
Json map_data = Json::parse(mapdata_json_text, mapdata_err);
if (map_data == Json())
FATAL_ERROR("%s\n", mapdata_err.c_str());
Json layouts_data = Json::parse(layouts_json_text, layouts_err);
if (layouts_data == Json())
FATAL_ERROR("%s\n", layouts_err.c_str());
string header_text = generate_map_header_text(map_data, layouts_data, version);
string events_text = generate_map_events_text(map_data);
string connections_text = generate_map_connections_text(map_data);
string files_dir = get_directory_name(map_filepath);
write_text_file(files_dir + "header.inc", header_text);
write_text_file(files_dir + "events.inc", events_text);
write_text_file(files_dir + "connections.inc", connections_text);
}
string generate_groups_text(Json groups_data) {
ostringstream text;
for (auto &key : groups_data["group_order"].array_items()) {
string group = key.string_value();
text << group << "::\n";
auto maps = groups_data[group].array_items();
for (Json &map_name : maps)
text << "\t.4byte " << map_name.string_value() << "\n";
text << "\n";
}
text << "\t.align 2\n" << "gMapGroups::\n";
for (auto &group : groups_data["group_order"].array_items())
text << "\t.4byte " << group.string_value() << "\n";
text << "\n";
return text.str();
}
string generate_connections_text(Json groups_data) {
vector<Json> map_names;
for (auto &group : groups_data["group_order"].array_items())
for (auto map_name : groups_data[group.string_value()].array_items())
map_names.push_back(map_name);
vector<Json> connections_include_order = groups_data["connections_include_order"].array_items();
if (connections_include_order.size() > 0)
sort(map_names.begin(), map_names.end(), [connections_include_order](const Json &a, const Json &b) {
auto iter_a = find(connections_include_order.begin(), connections_include_order.end(), a);
if (iter_a == connections_include_order.end())
iter_a = connections_include_order.begin() + numeric_limits<int>::max();
auto iter_b = find(connections_include_order.begin(), connections_include_order.end(), b);
if (iter_b == connections_include_order.end())
iter_b = connections_include_order.begin() + numeric_limits<int>::max();
return iter_a < iter_b;
});
ostringstream text;
for (Json map_name : map_names)
text << "\t.include \"data/maps/" << map_name.string_value() << "/connections.inc\"\n";
return text.str();
}
string generate_headers_text(Json groups_data) {
vector<string> map_names;
for (auto &group : groups_data["group_order"].array_items())
for (auto map_name : groups_data[group.string_value()].array_items())
map_names.push_back(map_name.string_value());
ostringstream text;
for (string map_name : map_names)
text << "\t.include \"data/maps/" << map_name << "/header.inc\"\n";
return text.str();
}
string generate_events_text(Json groups_data) {
vector<string> map_names;
for (auto &group : groups_data["group_order"].array_items())
for (auto map_name : groups_data[group.string_value()].array_items())
map_names.push_back(map_name.string_value());
ostringstream text;
for (string map_name : map_names)
text << "\t.include \"data/maps/" << map_name << "/events.inc\"\n";
return text.str();
}
string generate_map_constants_text(string groups_filepath, Json groups_data) {
string file_dir = get_directory_name(groups_filepath);
char dir_separator = file_dir.back();
ostringstream text;
text << "#ifndef GUARD_CONSTANTS_MAP_GROUPS_H\n"
<< "#define GUARD_CONSTANTS_MAP_GROUPS_H\n\n";
int group_num = 0;
for (auto &group : groups_data["group_order"].array_items()) {
text << "// Map Group " << group_num << "\n";
vector<Json> map_ids;
size_t max_length = 0;
for (auto &map_name : groups_data[group.string_value()].array_items()) {
string header_filepath = file_dir + map_name.string_value() + dir_separator + "map.json";
string err_str;
Json map_data = Json::parse(read_text_file(header_filepath), err_str);
map_ids.push_back(map_data["id"]);
if (map_data["id"].string_value().length() > max_length)
max_length = map_data["id"].string_value().length();
}
int map_id_num = 0;
for (Json map_id : map_ids) {
text << "#define " << map_id.string_value() << string((max_length - map_id.string_value().length() + 1), ' ')
<< "(" << map_id_num++ << " | (" << group_num << " << 8))\n";
}
text << "\n";
group_num++;
}
text << "#define MAP_GROUPS_COUNT " << group_num << "\n\n";
text << "#endif // GUARD_CONSTANTS_MAP_GROUPS_H\n";
return text.str();
}
void process_groups(string groups_filepath) {
string err;
Json groups_data = Json::parse(read_text_file(groups_filepath), err);
if (groups_data == Json())
FATAL_ERROR("%s\n", err.c_str());
string groups_text = generate_groups_text(groups_data);
string connections_text = generate_connections_text(groups_data);
string headers_text = generate_headers_text(groups_data);
string events_text = generate_events_text(groups_data);
string map_header_text = generate_map_constants_text(groups_filepath, groups_data);
string file_dir = get_directory_name(groups_filepath);
char s = file_dir.back();
write_text_file(file_dir + "groups.inc", groups_text);
write_text_file(file_dir + "connections.inc", connections_text);
write_text_file(file_dir + "headers.inc", headers_text);
write_text_file(file_dir + "events.inc", events_text);
write_text_file(file_dir + ".." + s + ".." + s + "include" + s + "constants" + s + "map_groups.h", map_header_text);
}
string generate_layout_headers_text(Json layouts_data) {
ostringstream text;
for (auto &layout : layouts_data["layouts"].array_items()) {
string border_label = layout["name"].string_value() + "_Border";
string blockdata_label = layout["name"].string_value() + "_Blockdata";
text << border_label << "::\n"
<< "\t.incbin \"" << layout["border_filepath"].string_value() << "\"\n\n"
<< blockdata_label << "::\n"
<< "\t.incbin \"" << layout["blockdata_filepath"].string_value() << "\"\n\n"
<< "\t.align 2\n"
<< layout["name"].string_value() << "::\n"
<< "\t.4byte " << layout["width"].int_value() << "\n"
<< "\t.4byte " << layout["height"].int_value() << "\n"
<< "\t.4byte " << border_label << "\n"
<< "\t.4byte " << blockdata_label << "\n"
<< "\t.4byte " << layout["primary_tileset"].string_value() << "\n"
<< "\t.4byte " << layout["secondary_tileset"].string_value() << "\n\n";
}
return text.str();
}
string generate_layouts_table_text(Json layouts_data) {
ostringstream text;
text << "\t.align 2\n"
<< layouts_data["layouts_table_label"].string_value() << "::\n";
for (auto &layout : layouts_data["layouts"].array_items())
text << "\t.4byte " << layout["name"].string_value() << "\n";
return text.str();
}
string generate_layouts_constants_text(Json layouts_data) {
ostringstream text;
text << "#ifndef GUARD_CONSTANTS_LAYOUTS_H\n"
<< "#define GUARD_CONSTANTS_LAYOUTS_H\n\n";
int i = 0;
for (auto &layout : layouts_data["layouts"].array_items())
text << "#define " << layout["id"].string_value() << " " << ++i << "\n";
text << "\n#endif // GUARD_CONSTANTS_LAYOUTS_H\n";
return text.str();
}
void process_layouts(string layouts_filepath) {
string err;
Json layouts_data = Json::parse(read_text_file(layouts_filepath), err);
if (layouts_data == Json())
FATAL_ERROR("%s\n", err.c_str());
string layout_headers_text = generate_layout_headers_text(layouts_data);
string layouts_table_text = generate_layouts_table_text(layouts_data);
string layouts_constants_text = generate_layouts_constants_text(layouts_data);
string file_dir = get_directory_name(layouts_filepath);
char s = file_dir.back();
write_text_file(file_dir + "layouts.inc", layout_headers_text);
write_text_file(file_dir + "layouts_table.inc", layouts_table_text);
write_text_file(file_dir + ".." + s + ".." + s + "include" + s + "constants" + s + "layouts.h", layouts_constants_text);
}
int main(int argc, char *argv[]) {
if (argc < 3)
FATAL_ERROR("USAGE: mapjson <mode> <game-version> [options]\n");
char *version_arg = argv[2];
string version(version_arg);
if (version != "emerald" && version != "ruby")
FATAL_ERROR("ERROR: <game-version> must be 'emerald' or 'ruby'.\n");
char *mode_arg = argv[1];
string mode(mode_arg);
if (mode != "layouts" && mode != "map" && mode != "groups")
FATAL_ERROR("ERROR: <mode> must be 'layouts', 'map', or 'groups'.\n");
if (mode == "map") {
if (argc != 5)
FATAL_ERROR("USAGE: mapjson map <game-version> <map_file> <layouts_file>\n");
string filepath(argv[3]);
string layouts_filepath(argv[4]);
process_map(filepath, layouts_filepath, version);
}
else if (mode == "groups") {
if (argc != 4)
FATAL_ERROR("USAGE: mapjson groups <game-version> <groups_file>\n");
string filepath(argv[3]);
process_groups(filepath);
}
else if (mode == "layouts") {
if (argc != 4)
FATAL_ERROR("USAGE: mapjson layouts <game-version> <layouts_file>\n");
string filepath(argv[3]);
process_layouts(filepath);
}
return 0;
}

31
tools/mapjson/mapjson.h Normal file
View File

@ -0,0 +1,31 @@
// mapjson.h
#ifndef MAPJSON_H
#define MAPJSON_H
#include <cstdio>
using std::fprintf; using std::exit;
#include <cstdlib>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
#endif // MAPJSON_H

1
tools/mid2agb/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
mid2agb

19
tools/mid2agb/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2016 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

18
tools/mid2agb/Makefile Normal file
View File

@ -0,0 +1,18 @@
CXX := g++
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
SRCS := agb.cpp error.cpp main.cpp midi.cpp tables.cpp
HEADERS := agb.h error.h main.h midi.h tables.h
.PHONY: all clean
all: mid2agb
@:
mid2agb: $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) mid2agb mid2agb.exe

547
tools/mid2agb/agb.cpp Normal file
View File

@ -0,0 +1,547 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <vector>
#include "agb.h"
#include "main.h"
#include "midi.h"
#include "tables.h"
int g_agbTrack;
static std::string s_lastOpName;
static int s_blockNum;
static bool s_keepLastOpName;
static int s_lastNote;
static int s_lastVelocity;
static bool s_noteChanged;
static bool s_velocityChanged;
static bool s_inPattern;
static int s_extendedCommand;
static int s_memaccOp;
static int s_memaccParam1;
static int s_memaccParam2;
void PrintAgbHeader()
{
std::fprintf(g_outputFile, "\t.include \"MPlayDef.s\"\n\n");
std::fprintf(g_outputFile, "\t.equ\t%s_grp, voicegroup%03u\n", g_asmLabel.c_str(), g_voiceGroup);
std::fprintf(g_outputFile, "\t.equ\t%s_pri, %u\n", g_asmLabel.c_str(), g_priority);
if (g_reverb >= 0)
std::fprintf(g_outputFile, "\t.equ\t%s_rev, reverb_set+%u\n", g_asmLabel.c_str(), g_reverb);
else
std::fprintf(g_outputFile, "\t.equ\t%s_rev, 0\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\t.equ\t%s_mvl, %u\n", g_asmLabel.c_str(), g_masterVolume);
std::fprintf(g_outputFile, "\t.equ\t%s_key, %u\n", g_asmLabel.c_str(), 0);
std::fprintf(g_outputFile, "\t.equ\t%s_tbs, %u\n", g_asmLabel.c_str(), g_clocksPerBeat);
std::fprintf(g_outputFile, "\t.equ\t%s_exg, %u\n", g_asmLabel.c_str(), g_exactGateTime);
std::fprintf(g_outputFile, "\t.equ\t%s_cmp, %u\n", g_asmLabel.c_str(), g_compressionEnabled);
std::fprintf(g_outputFile, "\n\t.section .rodata\n");
std::fprintf(g_outputFile, "\t.global\t%s\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\t.align\t2\n");
}
void ResetTrackVars()
{
s_lastVelocity = -1;
s_lastNote = -1;
s_velocityChanged = false;
s_noteChanged = false;
s_keepLastOpName = false;
s_lastOpName = "";
s_inPattern = false;
}
void PrintWait(int wait)
{
if (wait > 0)
{
std::fprintf(g_outputFile, "\t.byte\tW%02d\n", wait);
s_velocityChanged = true;
s_noteChanged = true;
s_keepLastOpName = true;
}
}
void PrintOp(int wait, std::string name, const char *format, ...)
{
std::va_list args;
va_start(args, format);
std::fprintf(g_outputFile, "\t.byte\t\t");
if (format != nullptr)
{
if (!g_compressionEnabled || s_lastOpName != name)
{
std::fprintf(g_outputFile, "%s, ", name.c_str());
s_lastOpName = name;
}
else
{
std::fprintf(g_outputFile, " ");
}
std::vfprintf(g_outputFile, format, args);
}
else
{
std::fputs(name.c_str(), g_outputFile);
s_lastOpName = name;
}
std::fprintf(g_outputFile, "\n");
va_end(args);
PrintWait(wait);
}
void PrintByte(const char *format, ...)
{
std::va_list args;
va_start(args, format);
std::fprintf(g_outputFile, "\t.byte\t");
std::vfprintf(g_outputFile, format, args);
std::fprintf(g_outputFile, "\n");
s_velocityChanged = true;
s_noteChanged = true;
s_keepLastOpName = true;
va_end(args);
}
void PrintWord(const char *format, ...)
{
std::va_list args;
va_start(args, format);
std::fprintf(g_outputFile, "\t .word\t");
std::vfprintf(g_outputFile, format, args);
std::fprintf(g_outputFile, "\n");
va_end(args);
}
void PrintNote(const Event& event)
{
int note = event.note;
int velocity = g_noteVelocityLUT[event.param1];
int duration = -1;
if (event.param2 != -1)
duration = g_noteDurationLUT[event.param2];
int gateTimeParam = 0;
if (g_exactGateTime && duration != -1)
gateTimeParam = event.param2 - duration;
char gtpBuf[16];
if (gateTimeParam > 0)
std::snprintf(gtpBuf, sizeof(gtpBuf), ", gtp%u", gateTimeParam);
else
gtpBuf[0] = 0;
char opName[16];
if (duration == -1)
std::strcpy(opName, "TIE ");
else
std::snprintf(opName, sizeof(opName), "N%02u ", duration);
bool noteChanged = true;
bool velocityChanged = true;
if (g_compressionEnabled)
{
noteChanged = (note != s_lastNote);
velocityChanged = (velocity != s_lastVelocity);
}
if (s_keepLastOpName)
s_keepLastOpName = false;
else
s_lastOpName = "";
if (noteChanged || velocityChanged || (gateTimeParam > 0))
{
s_lastNote = note;
char noteBuf[16];
if (note >= 24)
std::snprintf(noteBuf, sizeof(noteBuf), g_noteTable[note % 12], note / 12 - 2);
else
std::snprintf(noteBuf, sizeof(noteBuf), g_minusNoteTable[note % 12], note / -12 + 2);
char velocityBuf[16];
if (velocityChanged || (gateTimeParam > 0))
{
s_lastVelocity = velocity;
std::snprintf(velocityBuf, sizeof(velocityBuf), ", v%03u", velocity);
}
else
{
velocityBuf[0] = 0;
}
PrintOp(event.time, opName, "%s%s%s", noteBuf, velocityBuf, gtpBuf);
}
else
{
PrintOp(event.time, opName, 0);
}
s_noteChanged = noteChanged;
s_velocityChanged = velocityChanged;
}
void PrintEndOfTieOp(const Event& event)
{
int note = event.note;
bool noteChanged = (note != s_lastNote);
if (!noteChanged || !s_noteChanged)
s_lastOpName = "";
if (!noteChanged && g_compressionEnabled)
{
PrintOp(event.time, "EOT ", nullptr);
}
else
{
s_lastNote = note;
if (note >= 24)
PrintOp(event.time, "EOT ", g_noteTable[note % 12], note / 12 - 2);
else
PrintOp(event.time, "EOT ", g_minusNoteTable[note % 12], note / -12 + 2);
}
s_noteChanged = noteChanged;
}
void PrintSeqLoopLabel(const Event& event)
{
s_blockNum = event.param1 + 1;
std::fprintf(g_outputFile, "%s_%u_B%u:\n", g_asmLabel.c_str(), g_agbTrack, s_blockNum);
PrintWait(event.time);
ResetTrackVars();
}
void PrintMemAcc(const Event& event)
{
switch (s_memaccOp)
{
case 0x00:
PrintByte("MEMACC, mem_set, 0x%02X, %u", s_memaccParam1, event.param2);
break;
case 0x01:
PrintByte("MEMACC, mem_add, 0x%02X, %u", s_memaccParam1, event.param2);
break;
case 0x02:
PrintByte("MEMACC, mem_sub, 0x%02X, %u", s_memaccParam1, event.param2);
break;
case 0x03:
PrintByte("MEMACC, mem_mem_set, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
break;
case 0x04:
PrintByte("MEMACC, mem_mem_add, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
break;
case 0x05:
PrintByte("MEMACC, mem_mem_sub, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
break;
// TODO: everything else
case 0x06:
break;
case 0x07:
break;
case 0x08:
break;
case 0x09:
break;
case 0x0A:
break;
case 0x0B:
break;
case 0x0C:
break;
case 0x0D:
break;
case 0x0E:
break;
case 0x0F:
break;
case 0x10:
break;
case 0x11:
break;
case 0x46:
break;
case 0x47:
break;
case 0x48:
break;
case 0x49:
break;
case 0x4A:
break;
case 0x4B:
break;
case 0x4C:
break;
case 0x4D:
break;
case 0x4E:
break;
case 0x4F:
break;
case 0x50:
break;
case 0x51:
break;
default:
break;
}
PrintWait(event.time);
}
void PrintExtendedOp(const Event& event)
{
// TODO: support for other extended commands
switch (s_extendedCommand)
{
case 0x08:
PrintOp(event.time, "XCMD ", "xIECV , %u", event.param2);
break;
case 0x09:
PrintOp(event.time, "XCMD ", "xIECL , %u", event.param2);
break;
default:
PrintWait(event.time);
break;
}
}
void PrintControllerOp(const Event& event)
{
switch (event.param1)
{
case 0x01:
PrintOp(event.time, "MOD ", "%u", event.param2);
break;
case 0x07:
PrintOp(event.time, "VOL ", "%u*%s_mvl/mxv", event.param2, g_asmLabel.c_str());
break;
case 0x0A:
PrintOp(event.time, "PAN ", "c_v%+d", event.param2 - 64);
break;
case 0x0C:
case 0x10:
PrintMemAcc(event);
break;
case 0x0D:
s_memaccOp = event.param2;
PrintWait(event.time);
break;
case 0x0E:
s_memaccParam1 = event.param2;
PrintWait(event.time);
break;
case 0x0F:
s_memaccParam2 = event.param2;
PrintWait(event.time);
break;
case 0x11:
std::fprintf(g_outputFile, "%s_%u_L%u:\n", g_asmLabel.c_str(), g_agbTrack, event.param2);
PrintWait(event.time);
ResetTrackVars();
break;
case 0x14:
PrintOp(event.time, "BENDR ", "%u", event.param2);
break;
case 0x15:
PrintOp(event.time, "LFOS ", "%u", event.param2);
break;
case 0x16:
PrintOp(event.time, "MODT ", "%u", event.param2);
break;
case 0x18:
PrintOp(event.time, "TUNE ", "c_v%+d", event.param2 - 64);
break;
case 0x1A:
PrintOp(event.time, "LFODL ", "%u", event.param2);
break;
case 0x1D:
case 0x1F:
PrintExtendedOp(event);
break;
case 0x1E:
s_extendedCommand = event.param2;
// TODO: loop op
break;
case 0x21:
case 0x27:
PrintByte("PRIO , %u", event.param2);
PrintWait(event.time);
break;
default:
PrintWait(event.time);
break;
}
}
void PrintAgbTrack(std::vector<Event>& events)
{
std::fprintf(g_outputFile, "\n@**************** Track %u (Midi-Chn.%u) ****************@\n\n", g_agbTrack, g_midiChan + 1);
std::fprintf(g_outputFile, "%s_%u:\n", g_asmLabel.c_str(), g_agbTrack);
int wholeNoteCount = 0;
int loopEndBlockNum = 0;
ResetTrackVars();
bool foundVolBeforeNote = false;
for (const Event& event : events)
{
if (event.type == EventType::Note)
break;
if (event.type == EventType::Controller && event.param1 == 0x07)
{
foundVolBeforeNote = true;
break;
}
}
if (!foundVolBeforeNote)
PrintByte("\tVOL , 127*%s_mvl/mxv", g_asmLabel.c_str());
PrintWait(g_initialWait);
PrintByte("KEYSH , %s_key%+d", g_asmLabel.c_str(), 0);
for (unsigned i = 0; events[i].type != EventType::EndOfTrack; i++)
{
const Event& event = events[i];
if (IsPatternBoundary(event.type))
{
if (s_inPattern)
PrintByte("PEND");
s_inPattern = false;
}
if (event.type == EventType::WholeNoteMark || event.type == EventType::Pattern)
std::fprintf(g_outputFile, "@ %03d ----------------------------------------\n", wholeNoteCount++);
switch (event.type)
{
case EventType::Note:
PrintNote(event);
break;
case EventType::EndOfTie:
PrintEndOfTieOp(event);
break;
case EventType::Label:
PrintSeqLoopLabel(event);
break;
case EventType::LoopEnd:
PrintByte("GOTO");
PrintWord("%s_%u_B%u", g_asmLabel.c_str(), g_agbTrack, loopEndBlockNum);
PrintSeqLoopLabel(event);
break;
case EventType::LoopEndBegin:
PrintByte("GOTO");
PrintWord("%s_%u_B%u", g_asmLabel.c_str(), g_agbTrack, loopEndBlockNum);
PrintSeqLoopLabel(event);
loopEndBlockNum = s_blockNum;
break;
case EventType::LoopBegin:
PrintSeqLoopLabel(event);
loopEndBlockNum = s_blockNum;
break;
case EventType::WholeNoteMark:
if (event.param2 & 0x80000000)
{
std::fprintf(g_outputFile, "%s_%u_%03lu:\n", g_asmLabel.c_str(), g_agbTrack, (unsigned long)(event.param2 & 0x7FFFFFFF));
ResetTrackVars();
s_inPattern = true;
}
PrintWait(event.time);
break;
case EventType::Pattern:
PrintByte("PATT");
PrintWord("%s_%u_%03lu", g_asmLabel.c_str(), g_agbTrack, event.param2);
while (!IsPatternBoundary(events[i + 1].type))
i++;
ResetTrackVars();
break;
case EventType::Tempo:
PrintByte("TEMPO , %u*%s_tbs/2", 60000000 / event.param2, g_asmLabel.c_str());
PrintWait(event.time);
break;
case EventType::InstrumentChange:
PrintOp(event.time, "VOICE ", "%u", event.param1);
break;
case EventType::PitchBend:
PrintOp(event.time, "BEND ", "c_v%+d", event.param2 - 64);
break;
case EventType::Controller:
PrintControllerOp(event);
break;
default:
PrintWait(event.time);
break;
}
}
PrintByte("FINE");
}
void PrintAgbFooter()
{
int trackCount = g_agbTrack - 1;
std::fprintf(g_outputFile, "\n@******************************************************@\n");
std::fprintf(g_outputFile, "\t.align\t2\n");
std::fprintf(g_outputFile, "\n%s:\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\t.byte\t%u\t@ NumTrks\n", trackCount);
std::fprintf(g_outputFile, "\t.byte\t%u\t@ NumBlks\n", 0);
std::fprintf(g_outputFile, "\t.byte\t%s_pri\t@ Priority\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\t.byte\t%s_rev\t@ Reverb.\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\n");
std::fprintf(g_outputFile, "\t.word\t%s_grp\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\n");
// track pointers
for (int i = 1; i <= trackCount; i++)
std::fprintf(g_outputFile, "\t.word\t%s_%u\n", g_asmLabel.c_str(), i);
std::fprintf(g_outputFile, "\n\t.end\n");
}

Some files were not shown because too many files have changed in this diff Show More