diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/common/patch.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/common/patch.asm
new file mode 100644
index 000000000..eb4a40ccb
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/common/patch.asm
@@ -0,0 +1,407 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky All Versions
+; ------------------------------------------------------------------------------
+; Adds a menu to choose the starter after the quiz
+; ------------------------------------------------------------------------------
+
+
+ .org BegSwitch
+ .area 0x4
+ cmp r1,#0x48
+ .endarea
+ .org BegSwitch+0xC
+ .area 0x4
+ b move_beg_switch
+ .endarea
+
+ .org EndSwitch
+ .area 11*0x4
+ b SwitchCase42
+ b SwitchCase43
+ b SwitchCase44
+ b SwitchCase45
+ b SwitchCase46
+ b SwitchCase47
+ b SwitchCase48
+ .fill 11*0x4+EndSwitch-., 0xCC
+ .endarea
+
+ .org HookEventSeq
+ .area 0xC
+ mov r0,#0x42 ; 0x26 Originally
+ .endarea
+
+ .org OldGetPersonalityResult
+ .area 0x48
+ stmdb r13!,{r14}
+ ldr r0,[player_id]
+ mvn r1,#0
+ cmp r0,r1
+ bne end_ogpr
+ bl GetPersonalityResult
+ str r0,[player_id]
+ end_ogpr:
+ ldmia r13!,{r15}
+ player_id:
+ .word 0xFFFFFFFF
+ .pool
+ .fill 0x48+OldGetPersonalityResult-., 0xCC
+ .endarea
+
+ .org OverlayStart+OrgSize
+ .area ExtendSize
+
+ GetPersonalityResult:
+ stmdb r13!,{r3,r14}
+ ldr r0,=GlobalStructPointer
+ mov r12,#0x0
+ ldr r14,[r0, #+0x0]
+ mov r0,r12
+ mov r3,r12
+ loop_players:
+ add r1,r14,r3
+ ldrb r2,[r1, #+0x34]
+ ldrb r1,[r1, #+0x44]
+ add r1,r2,r1
+ cmp r12,r1
+ movlt r0,r3
+ add r3,r3,#0x1
+ movlt r12,r1
+ cmp r3,#0x10
+ blt loop_players
+ ldmia r13!,{r3,r15}
+ move_beg_switch:
+ mov r0,#0x64
+ bl RandMax
+ cmp r0,#0x4B
+ blt case0_alt1
+ add r0,r13,#0x14
+ bl UnknownFuncCase0
+ ldrb r1,[r13, #+0x19]
+ ldr r0,[r15, #+0xe20]
+ ldr r0,[r0, #+0x0]
+ and r1,r1,#0xF
+ b case0_alt2
+ SwitchCase42:
+ add r0,r13,#0x128
+ bl PrepDBUnk1
+ bl OldGetPersonalityResult
+ bl GetPlayerPkmnID
+ str r0,[r13, #+0x128]
+ ldr r0,=GlobalStructPointer
+ ldr r1,[r0]
+ ldrsb r0,[r1, #+0x2]
+ bl ShowDB
+ ldr r0,=GlobalStructPointer
+ mov r1,#0x4
+ ldr r0,[r0, #+0x0]
+ add r3,r13,#0x128
+ ldrsb r0,[r0, #+0x2]
+ add r2,r1,#0x6C0
+ bl ShowMessageInDB
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1, #+0x0]
+ ldr r0,[r1, #+0x20]
+ add r0,r0,#0x1
+ str r0,[r1, #+0x20]
+ b EndCodeSwitch
+ SwitchCase43:
+ ldrsb r0,[r0, #+0x2]
+ bl IsDBActive
+ cmp r0,#0x0
+ bne EndCodeSwitch
+ ldr r0,=DBLayout5
+ mov r4,#0x2
+ ldr r1,=0x00300013
+ ldr r3,=QuizMenu1
+ mov r2,#0x0
+ str r4,[r13, #+0x0]
+ bl CreateNormalMenu
+ ldr r1,=GlobalStructPointer
+ ldr r2,[r1, #+0x0]
+ strb r0,[r2, #+0x3]
+ ldr r1,[r1, #+0x0]
+ ldr r0,[r1, #+0x20]
+ add r0,r0,#0x1
+ str r0,[r1, #+0x20]
+ b EndCodeSwitch
+ SwitchCase44:
+ ldrsb r0,[r0, #+0x3] ;r0+0x3=*(000f1e03)
+ bl GetNormalMenuResult
+ cmp r0,#0x1
+ beq case44_yes
+ cmp r0,#0x2
+ beq case44_no
+ b EndCodeSwitch
+ case44_yes:
+ ldr r0,=GlobalStructPointer
+ ldr r0,[r0, #+0x0]
+ ldrsb r0,[r0, #+0x3]
+ bl FreeNormalMenu
+ mvn r2,#0x1
+ ldr r0,=GlobalStructPointer
+ ldr r1,[r0, #+0x0]
+ strb r2,[r1, #+0x3]
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1, #+0x0]
+ mov r0,#0x26
+ str r0,[r1, #+0x20]
+ b EndCodeSwitch
+ case44_no:
+ ldr r0,=GlobalStructPointer
+ ldr r0,[r0, #+0x0]
+ ldrsb r0,[r0, #+0x3]
+ bl FreeNormalMenu
+ mvn r2,#0x1
+ ldr r0,=GlobalStructPointer
+ ldr r1,[r0, #+0x0]
+ strb r2,[r1, #+0x3]
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1, #+0x0]
+ mov r0,#0x45
+ str r0,[r1, #+0x20]
+ b EndCodeSwitch
+ SwitchCase45:
+ ldrsb r0,[r0, #+0x2]
+ bl ShowDB
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1, #+0x0]
+ ldrsb r0,[r1, #+0x5]
+ bl HidePortraitBox
+ ldr r1,=GlobalStructPointer
+ ldr r3,[r1, #+0x0]
+ ldrsb r0,[r3, #+0x2]
+ ldr r2,=SpecialStringID
+ mov r1,#0x8
+ mov r3,#0x0
+ bl ShowMessageInDB
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1, #+0x0]
+ mov r0,#0x46
+ str r0,[r1, #+0x20]
+ b EndCodeSwitch
+ SwitchCase46:
+ ldrsb r0,[r0, #+0x2]
+ bl IsDBActive
+ cmp r0,#0x0
+ bne EndCodeSwitch
+ ldr r1,=GlobalStructPointer
+ ldr r0,=DBLayout6
+ ldr r2,[r1, #+0x0]
+ ldr r1,=0x00001011
+ mov r2,#0x10
+ ldr r3,=PlayerMenuDisp
+ str r2,[r13, #+0x0]
+ mov r4,#0x6
+ mov r2,#0x0
+ str r4,[r13, #+0x4]
+ bl CreateAdvancedMenu
+ ldr r2,=GlobalStructPointer
+ ldr r1,[r2, #+0x0]
+ strb r0,[r1, #+0x3]
+ ldr r3,[r1, #+0x20]
+ add r3,r3,#0x1
+ str r3,[r1, #+0x20]
+
+ ;For Portraits
+ mov r0,#0x0
+ sub r1,r0,#0x2
+ str r0,[r2, #+0x4]
+ str r0,[r2, #+0x8]
+ ldr r2,[r2]
+ ldrsb r2,[r2, #+0x5]
+ cmp r2,r1
+ bne no_pt_box
+ mov r1,#0x3
+ mov r2,#0x1
+ bl CreatePortraitBox
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ strb r0,[r1, #+0x5]
+ no_pt_box:
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ add r4,r1,#0x3B4
+ mov r0,#0
+ bl GetPlayerPkmnID
+ mov r1,r0
+ mov r0,r4
+ bl SetPortraitPkmnID
+ mov r0,r4
+ mov r1,#0
+ bl SetPortraitExpressionID
+ mov r0,r4
+ mov r1,#4
+ bl SetPortraitUnknownAttr
+ mov r0,r4
+ ldr r1,=PortraitAttrStruct
+ bl SetPortraitAttrStruct
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ ldrsb r0,[r1, #+0x5]
+ mov r1,r4
+ bl ShowPortraitBox
+ b EndCodeSwitch
+ SwitchCase47:
+ ldrsb r0,[r0, #+0x3]
+ ldr r4,[r2, #+0x4]
+ bl GetAdvancedMenuCurrentOption
+ cmp r4,r0
+ beq case47_same_pkmn
+ ldr r0,=GlobalStructPointer
+ mov r1,#0x0
+ str r1,[r0, #+0x8]
+ ldr r0,[r0, #+0x0]
+ ldrsb r0,[r0, #+0x3]
+ bl GetAdvancedMenuCurrentOption
+ ldr r1,=GlobalStructPointer
+ str r0,[r1, #+0x4]
+ ldr r1,[r1]
+ add r4,r1,#0x3B4
+ bl GetPlayerPkmnID
+ mov r1,r0
+ mov r0,r4
+ bl SetPortraitPkmnID
+ mov r1,0x0
+ mov r0,r4
+ bl SetPortraitExpressionID
+ mov r1,0x4
+ mov r0,r4
+ bl SetPortraitUnknownAttr
+ ldr r1,=PortraitAttrStruct
+ mov r0,r4
+ bl SetPortraitAttrStruct
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ ldrsb r0,[r1, #+0x5]
+ mov r1,r4
+ bl ShowPortraitBox
+ b case47_after_portraits
+ case47_same_pkmn:
+ ldr r0,=GlobalStructPointer
+ ldr r1,[r0, #+0x8]
+ cmp r1,#0x20
+ addne r1,r1,#0x1
+ strne r1,[r0, #+0x8]
+ bne case47_after_portraits
+ ldr r1,[r0]
+ mov r0,r4
+ add r4,r1,#0x3B4
+ bl GetPlayerPkmnID
+ mov r1,r0
+ mov r0,r4
+ bl SetPortraitPkmnID
+ mov r1,0x1
+ mov r0,r4
+ bl SetPortraitExpressionID
+ mov r1,0x4
+ mov r0,r4
+ bl SetPortraitUnknownAttr
+ ldr r1,=PortraitAttrStruct
+ mov r0,r4
+ bl SetPortraitAttrStruct
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ ldrsb r0,[r1, #+0x5]
+ mov r1,r4
+ bl ShowPortraitBox
+ case47_after_portraits:
+ ldr r0,=GlobalStructPointer
+ ldr r0,[r0, #+0x0]
+ ldrsb r0,[r0, #+0x3]
+ bl IsAdvancedMenuActive
+ cmp r0,#0x0
+ bne EndCodeSwitch
+ ldr r0,=GlobalStructPointer
+ ldr r4,[r0, #+0x0]
+ ldrsb r0,[r4, #+0x3]
+ bl GetAdvancedMenuResult
+ ldr r1,=player_id
+ str r0,[r1]
+
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ add r4,r1,#0x3B4
+ bl GetPlayerPkmnID
+ mov r1,r0
+ mov r0,r4
+ bl SetPortraitPkmnID
+ mov r1,0x1
+ mov r0,r4
+ bl SetPortraitExpressionID
+ mov r1,0x4
+ mov r0,r4
+ bl SetPortraitUnknownAttr
+ ldr r1,=PortraitAttrStruct
+ mov r0,r4
+ bl SetPortraitAttrStruct
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ ldrsb r0,[r1, #+0x5]
+ mov r1,r4
+ bl ShowPortraitBox
+
+ ldr r0,=GlobalStructPointer
+ ldr r0,[r0, #+0x0]
+ ldrsb r0,[r0, #+0x3]
+ bl FreeAdvancedMenu
+ mvn r0,#0x1
+ ldr r2,=GlobalStructPointer
+ ldr r1,[r2, #+0x0]
+ strb r0,[r1, #+0x3]
+ ldr r1,[r2, #+0x0]
+ mov r0,#0x42
+ str r0,[r1, #+0x20]
+ b EndCodeSwitch
+ SwitchCase48: ;Special Case to skip the quiz
+ mov r0,#0x0
+ mov r1,#0x3
+ mov r2,#0x1
+ bl CreatePortraitBox
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ strb r0,[r1, #+0x5]
+ ldr r1,=GlobalStructPointer
+ ldr r0,[r1]
+ ldrb r1,[r0, #+0x5f]
+ ldr r0,=BorderColorTable
+ ldrb r0,[r0, +r1]
+ bl ChangeBorderColor
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ mov r0,#0x45
+ str r0,[r1, #+0x20]
+ b EndCodeSwitch
+ .pool
+ GetPlayerPkmnID:
+ ldr r1,=GlobalStructPointer
+ ldr r1,[r1]
+ ldrb r1,[r1, #+0x5f]
+ add r0,r1,r0,lsl #0x1
+ mov r0,r0,lsl #0x1
+ ldr r2,=PlayersListPkmnID
+ ldrsh r0,[r2, r0]
+ bx r14
+ PlayerMenuDisp:
+ stmdb r13!,{r3,r4,r14}
+ sub r13,r13,#0x54
+ add r12,r13,#0x4
+ mov r4,r0
+ mov r0,r1
+ bl GetPlayerPkmnID
+ ldr r3,=0x0000c402
+ orr r14,r0,#0x10000
+ str r0,[r13, #+0x4]
+ str r14,[r13, #+0x14]
+ mov r1,#0x400
+ ldr r2,=MenuOptionString
+ str r12,[r13, #+0x0]
+ mov r0,r4
+ bl MenuCreateOptionString
+ mov r0,r4
+ add r13,r13,#0x54
+ ldmia r13!,{r3,r4,r15}
+ .pool
+ .fill OverlayStart+OrgSize+ExtendSize-., 0xCC
+ .endarea
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/eu/offsets.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/eu/offsets.asm
new file mode 100644
index 000000000..dd59dc98d
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/eu/offsets.asm
@@ -0,0 +1,83 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky EU Only
+; ------------------------------------------------------------------------------
+; Adds a menu to choose the starter after the quiz
+; ------------------------------------------------------------------------------
+
+
+.relativeinclude on
+.nds
+.arm
+
+.definelabel SpecialStringID, 0xA35
+
+.definelabel OrgSize, 0x2E80
+.definelabel ExtendSize, 0x1000
+.definelabel OverlayStart, 0x0238AC80
+
+
+.definelabel RandMax, 0x02002274
+
+.definelabel MenuCreateOptionString, 0x020225EC
+
+.definelabel ChangeBorderColor, 0x02027D74
+
+.definelabel CreateNormalMenu, 0x0202B3E0
+.definelabel FreeNormalMenu, 0x0202B7B8
+.definelabel GetNormalMenuResult, 0x0202B870
+
+.definelabel CreateAdvancedMenu, 0x0202BD14
+.definelabel FreeAdvancedMenu, 0x0202BF38
+.definelabel IsAdvancedMenuActive, 0x0202BFD0
+.definelabel GetAdvancedMenuCurrentOption, 0x0202BFF0
+.definelabel GetAdvancedMenuResult, 0x0202C004
+
+.definelabel IsDBActive, 0x0202F474
+.definelabel ShowMessageInDB, 0x0202F4A8
+.definelabel ShowDB, 0x0202F698
+
+.definelabel CreatePortraitBox, 0x0202F8A0
+.definelabel ShowPortraitBox, 0x0202F984
+.definelabel HidePortraitBox, 0x0202F9D0
+
+.definelabel PrepDBUnk1, 0x020238B4
+
+.definelabel UnknownFuncCase0, 0x0204A4D0
+
+.definelabel SetPortraitPkmnID, 0x0204DB0C
+.definelabel SetPortraitExpressionID, 0x0204DB2C
+.definelabel SetPortraitUnknownAttr, 0x0204DB3C
+.definelabel SetPortraitAttrStruct, 0x0204DB80
+
+
+.definelabel BegSwitch, 0x0238B0E0
+.definelabel EndSwitch, 0x0238B1F4
+
+.definelabel case0_alt1, 0x0238B220
+.definelabel case0_alt2, 0x0238B234
+
+.definelabel HookEventSeq, 0x0238B908
+
+.definelabel EndCodeSwitch, 0x0238C8B4
+
+.definelabel WaitForNextStep, 0x0238C98C
+
+.definelabel OldGetPersonalityResult, 0x0238C8E8
+
+.definelabel BorderColorTable, 0x0238CB50
+.definelabel PortraitAttrStruct, 0x0238CB54
+.definelabel QuizMenu1, 0x0238CBB4
+.definelabel PlayersListPkmnID, 0x0238CBF8
+.definelabel MenuOptionString, 0x0238D9B0
+
+.definelabel GlobalStructPointer, 0x0238D9E0
+;0x2 = CurrentDialogueBoxID [0x1]
+;0x3 = CurrentMenuID [0x1]
+;0x5 = CurrentPortraitBoXID [0x1]
+;0x20 = NextSwitchCase [0x4]
+;0x30 = WaitingCase [0x4]
+;0x5F = Gender [0x1]
+
+.definelabel DBLayout5, 0x0238D9EC
+.definelabel DBLayout6, 0x0238D9FC
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/jp/offsets.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/jp/offsets.asm
new file mode 100644
index 000000000..1eab38a42
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/jp/offsets.asm
@@ -0,0 +1,84 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky JP Only
+; ------------------------------------------------------------------------------
+; Adds a menu to choose the starter after the quiz
+; ------------------------------------------------------------------------------
+
+; WARNING! Not tested!
+
+.relativeinclude on
+.nds
+.arm
+
+.definelabel SpecialStringID, 0xA35 ;Guess
+
+.definelabel OrgSize, 0x2E80
+.definelabel ExtendSize, 0x1000
+.definelabel OverlayStart, 0x0238B6A0
+
+
+.definelabel RandMax, 0x02002274
+
+.definelabel MenuCreateOptionString, 0x02022440
+
+.definelabel ChangeBorderColor, 0x02027DE0
+
+.definelabel CreateNormalMenu, 0x0202B444
+.definelabel FreeNormalMenu, 0x0202B81C
+.definelabel GetNormalMenuResult, 0x0202B8D4
+
+.definelabel CreateAdvancedMenu, 0x0202BD78
+.definelabel FreeAdvancedMenu, 0x0202BF9C
+.definelabel IsAdvancedMenuActive, 0x0202C034
+.definelabel GetAdvancedMenuCurrentOption, 0x0202C054
+.definelabel GetAdvancedMenuResult, 0x0202C068
+
+.definelabel IsDBActive, 0x0202F4C4
+.definelabel ShowMessageInDB, 0x0202F4F8
+.definelabel ShowDB, 0x0202F6E8
+
+.definelabel CreatePortraitBox, 0x0202F8F0
+.definelabel ShowPortraitBox, 0x0202F9D4
+.definelabel HidePortraitBox, 0x0202FA20
+
+.definelabel PrepDBUnk1, 0x020236E0
+
+.definelabel UnknownFuncCase0, 0x0204A500
+
+.definelabel SetPortraitPkmnID, 0x0204DB34
+.definelabel SetPortraitExpressionID, 0x0204DB54
+.definelabel SetPortraitUnknownAttr, 0x0204DB64
+.definelabel SetPortraitAttrStruct, 0x0204DBA8
+
+
+.definelabel BegSwitch, 0x0238BB00
+.definelabel EndSwitch, 0x0238BC14
+
+.definelabel case0_alt1, 0x0238BC40
+.definelabel case0_alt2, 0x0238BC54
+
+.definelabel HookEventSeq, 0x0238C328
+
+.definelabel EndCodeSwitch, 0x0238D2D8
+
+.definelabel WaitForNextStep, 0x0238D3B0
+
+.definelabel OldGetPersonalityResult, 0x0238D30C
+
+.definelabel BorderColorTable, 0x0238D578
+.definelabel PortraitAttrStruct, 0x0238D57C
+.definelabel QuizMenu1, 0x0238D5DC
+.definelabel PlayersListPkmnID, 0x0238D620
+.definelabel MenuOptionString, 0x0238E3D8
+
+.definelabel GlobalStructPointer, 0x0238E408
+;0x2 = CurrentDialogueBoxID [0x1]
+;0x3 = CurrentMenuID [0x1]
+;0x5 = CurrentPortraitBoXID [0x1]
+;0x20 = NextSwitchCase [0x4]
+;0x30 = WaitingCase [0x4]
+;0x5F = Gender [0x1]
+
+.definelabel DBLayout5, 0x0238E414
+.definelabel DBLayout6, 0x0238E424
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/na/offsets.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/na/offsets.asm
new file mode 100644
index 000000000..c85cab68e
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/na/offsets.asm
@@ -0,0 +1,83 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky NA Only
+; ------------------------------------------------------------------------------
+; Adds a menu to choose the starter after the quiz
+; ------------------------------------------------------------------------------
+
+
+.relativeinclude on
+.nds
+.arm
+
+.definelabel SpecialStringID, 0xA35
+
+.definelabel OrgSize, 0x2E80
+.definelabel ExtendSize, 0x1000
+.definelabel OverlayStart, 0x0238A140
+
+
+.definelabel RandMax, 0x02002274
+
+.definelabel MenuCreateOptionString, 0x020223F0
+
+.definelabel ChangeBorderColor, 0x02027A80
+
+.definelabel CreateNormalMenu, 0x0202B0EC
+.definelabel FreeNormalMenu, 0x0202B4C4
+.definelabel GetNormalMenuResult, 0x0202B57C
+
+.definelabel CreateAdvancedMenu, 0x0202BA20
+.definelabel FreeAdvancedMenu, 0x0202BC44
+.definelabel IsAdvancedMenuActive, 0x0202BCDC
+.definelabel GetAdvancedMenuCurrentOption, 0x0202BCFC
+.definelabel GetAdvancedMenuResult, 0x0202BD10
+
+.definelabel IsDBActive, 0x0202F180
+.definelabel ShowMessageInDB, 0x0202F1B4
+.definelabel ShowDB, 0x0202F3A4
+
+.definelabel CreatePortraitBox, 0x0202F5AC
+.definelabel ShowPortraitBox, 0x0202F690
+.definelabel HidePortraitBox, 0x0202F6DC
+
+.definelabel PrepDBUnk1, 0x02023690
+
+.definelabel UnknownFuncCase0, 0x0204A198
+
+.definelabel SetPortraitPkmnID, 0x0204D7D4
+.definelabel SetPortraitExpressionID, 0x0204D7F4
+.definelabel SetPortraitUnknownAttr, 0x0204D804
+.definelabel SetPortraitAttrStruct, 0x0204D848
+
+
+.definelabel BegSwitch, 0x0238A5A0
+.definelabel EndSwitch, 0x0238A6B4
+
+.definelabel case0_alt1, 0x0238A6E0
+.definelabel case0_alt2, 0x0238A6F4
+
+.definelabel HookEventSeq, 0x0238ADC8
+
+.definelabel EndCodeSwitch, 0x0238BD74
+
+.definelabel WaitForNextStep, 0x0238BE4C
+
+.definelabel OldGetPersonalityResult, 0x0238BDA8
+
+.definelabel BorderColorTable, 0x0238C010
+.definelabel PortraitAttrStruct, 0x0238C014
+.definelabel QuizMenu1, 0x0238C074
+.definelabel PlayersListPkmnID, 0x238C0B8
+.definelabel MenuOptionString, 0x0238CE70
+
+.definelabel GlobalStructPointer, 0x0238CEA0
+;0x2 = CurrentDialogueBoxID [0x1]
+;0x3 = CurrentMenuID [0x1]
+;0x5 = CurrentPortraitBoXID [0x1]
+;0x20 = NextSwitchCase [0x4]
+;0x30 = WaitingCase [0x4]
+;0x5F = Gender [0x1]
+
+.definelabel DBLayout5, 0x0238CEAC
+.definelabel DBLayout6, 0x0238CEBC
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/selector_overlay13.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/selector_overlay13.asm
new file mode 100644
index 000000000..7952b3319
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/choose_starter/selector_overlay13.asm
@@ -0,0 +1,22 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky All Versions
+; ------------------------------------------------------------------------------
+; Selects the correct version to use
+; ------------------------------------------------------------------------------
+
+.relativeinclude on
+
+; Selects the correct region to apply the patch
+.if PPMD_GameVer == GameVer_EoS_NA
+ .include "na/offsets.asm"
+ .include "common/patch.asm"
+.elseif PPMD_GameVer == GameVer_EoS_EU
+ .include "eu/offsets.asm"
+ .include "common/patch.asm"
+.elseif PPMD_GameVer == GameVer_EoS_JP
+ .include "jp/offsets.asm"
+ .include "common/patch.asm"
+.endif
+
+.relativeinclude off
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/common/patch.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/common/patch.asm
new file mode 100644
index 000000000..a1a40ff6b
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/common/patch.asm
@@ -0,0 +1,29 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky All Versions
+; ------------------------------------------------------------------------------
+; Gets rid of the personality quiz
+; ------------------------------------------------------------------------------
+
+ .org HookBeforeQuestions
+ .area 0x4*14
+ mov r1,#0xE
+ ldr r3,[r0, #+0x0]
+ mov r0,#0x0
+ strb r1,[r3, #+0x0]
+ mov r1,#9
+ str r1,[r3, #+0x24]
+ strb r0,[r3, #+0x5e]
+ strb r0,[r3, #+0x5f]
+ ldr r1,[r3, #+0x20]
+ add r1,r1,#0x1
+ str r1,[r3, #+0x20]
+ mov r1,#0x6F
+ mov r2,#0x1
+ bl SetGameVariable
+ .endarea
+
+ .org HookAfterQuestions
+ .area 0x4
+ mov r2,#0x48
+ .endarea
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/eu/offsets.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/eu/offsets.asm
new file mode 100644
index 000000000..97e77f2be
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/eu/offsets.asm
@@ -0,0 +1,15 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky EU Only
+; ------------------------------------------------------------------------------
+; Adds a menu to choose the starter after the quiz
+; ------------------------------------------------------------------------------
+
+
+.relativeinclude on
+.nds
+.arm
+
+.definelabel SetGameVariable, 0x0204BB58
+.definelabel HookBeforeQuestions, 0x0238B240
+.definelabel HookAfterQuestions, 0x0238B7BC
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/jp/offsets.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/jp/offsets.asm
new file mode 100644
index 000000000..3ce93365c
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/jp/offsets.asm
@@ -0,0 +1,16 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky JP Only
+; ------------------------------------------------------------------------------
+; Adds a menu to choose the starter after the quiz
+; ------------------------------------------------------------------------------
+
+; WARNING! Not tested!
+
+.relativeinclude on
+.nds
+.arm
+
+.definelabel SetGameVariable, 0x0204BB80
+.definelabel HookBeforeQuestions, 0x0238BC60
+.definelabel HookAfterQuestions, 0x0238C1DC
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/na/offsets.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/na/offsets.asm
new file mode 100644
index 000000000..e4ff2d690
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/na/offsets.asm
@@ -0,0 +1,15 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky NA Only
+; ------------------------------------------------------------------------------
+; Adds a menu to choose the starter after the quiz
+; ------------------------------------------------------------------------------
+
+
+.relativeinclude on
+.nds
+.arm
+
+.definelabel SetGameVariable, 0x0204B820
+.definelabel HookBeforeQuestions, 0x0238A700
+.definelabel HookAfterQuestions, 0x0238AC7C
diff --git a/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/selector_overlay13.asm b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/selector_overlay13.asm
new file mode 100644
index 000000000..7952b3319
--- /dev/null
+++ b/skytemple_files/_resources/patches/asm_patches/irdkwia_asm_mods/skip_quiz/selector_overlay13.asm
@@ -0,0 +1,22 @@
+; For use with ARMIPS
+; 2021/03/23
+; For Explorers of Sky All Versions
+; ------------------------------------------------------------------------------
+; Selects the correct version to use
+; ------------------------------------------------------------------------------
+
+.relativeinclude on
+
+; Selects the correct region to apply the patch
+.if PPMD_GameVer == GameVer_EoS_NA
+ .include "na/offsets.asm"
+ .include "common/patch.asm"
+.elseif PPMD_GameVer == GameVer_EoS_EU
+ .include "eu/offsets.asm"
+ .include "common/patch.asm"
+.elseif PPMD_GameVer == GameVer_EoS_JP
+ .include "jp/offsets.asm"
+ .include "common/patch.asm"
+.endif
+
+.relativeinclude off
diff --git a/skytemple_files/_resources/ppmdu_config/pmd2data.xml b/skytemple_files/_resources/ppmdu_config/pmd2data.xml
index b97b66752..7c20d7d13 100644
--- a/skytemple_files/_resources/ppmdu_config/pmd2data.xml
+++ b/skytemple_files/_resources/ppmdu_config/pmd2data.xml
@@ -962,6 +962,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/skytemple_files/common/i18n_util.py b/skytemple_files/common/i18n_util.py
index 0c93137ce..747ec69c0 100644
--- a/skytemple_files/common/i18n_util.py
+++ b/skytemple_files/common/i18n_util.py
@@ -14,13 +14,48 @@
#
# You should have received a copy of the GNU General Public License
# along with SkyTemple. If not, see .
-
+import gettext
+from abc import ABC, abstractmethod
from inspect import currentframe
-try:
- import builtins
- __ = builtins._
-except Exception:
- __ = lambda a: a
+
+
+class AbstractLocaleManager(ABC):
+ @abstractmethod
+ def translate(self, message, locale_code):
+ pass
+
+ @abstractmethod
+ def gettext(self, message):
+ pass
+
+
+class LocaleManager(AbstractLocaleManager):
+ def __init__(self, domain, localedir, main_languages):
+ self.domain = domain
+ self.localedir = localedir
+ self.main_languages = main_languages
+
+ self.main_translations = gettext.translation(domain, localedir=localedir, languages=main_languages)
+
+ def translate(self, message, locale_code):
+ try:
+ return gettext.translation(self.domain, localedir=self.localedir, languages=[locale_code]).gettext(message)
+ except Exception:
+ return message
+
+ def gettext(self, message):
+ return self.main_translations.gettext(message)
+
+
+class NullLocaleManager(AbstractLocaleManager):
+ def translate(self, message, locale_code):
+ return message
+
+ def gettext(self, message):
+ return message
+
+
+_locales: AbstractLocaleManager = NullLocaleManager()
def _(s):
@@ -29,13 +64,19 @@ def _(s):
We use a proxy, so when imported before the localization is ready, we can ensure
the reload()'ed function is actually called.
"""
- return __(s)
+ return _locales.gettext(s)
+
+
+def get_locales():
+ return _locales
-def reload_locale():
+def reload_locale(domain, localedir, main_languages):
+ global _locales
+ _locales = LocaleManager(domain, localedir, main_languages)
+ _locales.main_translations.install()
global __
import builtins
- __ = builtins._
try:
from explorerscript import util
util._ = builtins._
diff --git a/skytemple_files/patch/handler/choose_starter.py b/skytemple_files/patch/handler/choose_starter.py
new file mode 100644
index 000000000..9e7e3ae65
--- /dev/null
+++ b/skytemple_files/patch/handler/choose_starter.py
@@ -0,0 +1,109 @@
+# Copyright 2020-2021 Parakoopa and the SkyTemple Contributors
+#
+# This file is part of SkyTemple.
+#
+# SkyTemple 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.
+#
+# SkyTemple 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 SkyTemple. If not, see .
+from typing import Callable, Dict, List, Set
+
+from ndspy.code import loadOverlayTable, saveOverlayTable
+from ndspy.rom import NintendoDSRom
+
+from skytemple_files.common.util import *
+from skytemple_files.common.ppmdu_config.data import Pmd2Data, GAME_VERSION_EOS, GAME_REGION_US, GAME_REGION_EU, GAME_REGION_JP
+from skytemple_files.patch.handler.abstract import AbstractPatchHandler
+from skytemple_files.common.i18n_util import _, get_locales
+from skytemple_files.data.str.handler import StrHandler
+
+PATCH_CHECK_ADDR_APPLIED_US = 0xC88
+PATCH_CHECK_ADDR_APPLIED_EU = 0xC88
+PATCH_CHECK_ADDR_APPLIED_JP = 0xC88
+PATCH_CHECK_INSTR_APPLIED = 0xE3A00026
+
+OVERLAY13_INITAL_SIZE_US = 0x2E80
+OVERLAY13_INITAL_SIZE_EU = 0x2E80
+OVERLAY13_INITAL_SIZE_JP = 0x2E80
+
+OVERLAY13_ADD_SIZE = 0x1000
+
+STRING_ID_US = 2613
+STRING_ID_EU = 2613
+STRING_ID_JP = 2613 #Just a guess
+
+MESSAGE = "Then, who would you like to be?"
+# For xgettext scanning: _("Then, who would you like to be?")
+
+
+class ChooseStarterPatchHandler(AbstractPatchHandler):
+
+ @property
+ def name(self) -> str:
+ return 'ChooseStarter'
+
+ @property
+ def description(self) -> str:
+ return _("""Adds an extra menu during the personality test to choose the starter.
+Uses the supposedly unused string 2613 in the strings file. """)
+
+ @property
+ def author(self) -> str:
+ return 'irdkwia'
+
+ @property
+ def version(self) -> str:
+ return '0.0.1'
+
+ def is_applied(self, rom: NintendoDSRom, config: Pmd2Data) -> bool:
+ if config.game_version == GAME_VERSION_EOS:
+ if config.game_region == GAME_REGION_US:
+ return read_uintle(rom.loadArm9Overlays([13])[13].data, PATCH_CHECK_ADDR_APPLIED_US, 4)!=PATCH_CHECK_INSTR_APPLIED
+ if config.game_region == GAME_REGION_EU:
+ return read_uintle(rom.loadArm9Overlays([13])[13].data, PATCH_CHECK_ADDR_APPLIED_EU, 4)!=PATCH_CHECK_INSTR_APPLIED
+ if config.game_region == GAME_REGION_JP:
+ return read_uintle(rom.loadArm9Overlays([13])[13].data, PATCH_CHECK_ADDR_APPLIED_JP, 4)!=PATCH_CHECK_INSTR_APPLIED
+ raise NotImplementedError()
+
+ def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data):
+ if config.game_version == GAME_VERSION_EOS:
+ if config.game_region == GAME_REGION_US:
+ string_id = STRING_ID_US
+ overlay_size = OVERLAY13_INITAL_SIZE_US
+ if config.game_region == GAME_REGION_EU:
+ string_id = STRING_ID_EU
+ overlay_size = OVERLAY13_INITAL_SIZE_EU
+ if config.game_region == GAME_REGION_JP:
+ string_id = STRING_ID_JP
+ overlay_size = OVERLAY13_INITAL_SIZE_JP
+
+ # Change dialogue
+ for lang in config.string_index_data.languages:
+ filename = 'MESSAGE/' + lang.filename
+ bin_before = rom.getFileByName(filename)
+ strings = StrHandler.deserialize(bin_before)
+ strings.strings[string_id-1] = get_locales().translate(MESSAGE, lang.locale.replace('-', '_'))
+ bin_after = StrHandler.serialize(strings)
+ rom.setFileByName(filename, bin_after)
+
+ table = loadOverlayTable(rom.arm9OverlayTable, lambda x,y:bytes())
+ ov = table[13]
+ if ov.ramSize.
+from typing import Callable, Dict, List, Set
+
+from ndspy.rom import NintendoDSRom
+
+from skytemple_files.common.util import *
+from skytemple_files.common.ppmdu_config.data import Pmd2Data, GAME_VERSION_EOS, GAME_REGION_US, GAME_REGION_EU, GAME_REGION_JP
+from skytemple_files.patch.handler.abstract import AbstractPatchHandler, DependantPatch
+from skytemple_files.common.i18n_util import _, get_locales
+from skytemple_files.data.str.handler import StrHandler
+
+PATCH_CHECK_ADDR_APPLIED_US = 0xB3C
+PATCH_CHECK_ADDR_APPLIED_EU = 0xB3C
+PATCH_CHECK_ADDR_APPLIED_JP = 0xB3C
+PATCH_CHECK_INSTR_APPLIED = 0xE2822001
+
+STRING_ID_US = 2613
+STRING_ID_EU = 2613
+STRING_ID_JP = 2613 #Just a guess
+
+MESSAGE = "Who would you like to be?"
+# For xgettext scanning: _("Who would you like to be?")
+
+
+class SkipQuizPatchHandler(AbstractPatchHandler, DependantPatch):
+
+ @property
+ def name(self) -> str:
+ return 'SkipQuiz'
+
+ @property
+ def description(self) -> str:
+ return _("""Skips the quiz, only leaving the gender question.
+Needs ChooseStarter patch to be applied. """)
+
+ @property
+ def author(self) -> str:
+ return 'irdkwia'
+
+ @property
+ def version(self) -> str:
+ return '0.0.1'
+
+ def depends_on(self) -> List[str]:
+ return ['ChooseStarter']
+
+ def is_applied(self, rom: NintendoDSRom, config: Pmd2Data) -> bool:
+ if config.game_version == GAME_VERSION_EOS:
+ if config.game_region == GAME_REGION_US:
+ return read_uintle(rom.loadArm9Overlays([13])[13].data, PATCH_CHECK_ADDR_APPLIED_US, 4)!=PATCH_CHECK_INSTR_APPLIED
+ if config.game_region == GAME_REGION_EU:
+ return read_uintle(rom.loadArm9Overlays([13])[13].data, PATCH_CHECK_ADDR_APPLIED_EU, 4)!=PATCH_CHECK_INSTR_APPLIED
+ if config.game_region == GAME_REGION_JP:
+ return read_uintle(rom.loadArm9Overlays([13])[13].data, PATCH_CHECK_ADDR_APPLIED_JP, 4)!=PATCH_CHECK_INSTR_APPLIED
+ raise NotImplementedError()
+
+ def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data):
+ if config.game_version == GAME_VERSION_EOS:
+ if config.game_region == GAME_REGION_US:
+ string_id = STRING_ID_US
+ if config.game_region == GAME_REGION_EU:
+ string_id = STRING_ID_EU
+ if config.game_region == GAME_REGION_JP:
+ string_id = STRING_ID_JP
+
+ # Change dialogue
+ for lang in config.string_index_data.languages:
+ filename = 'MESSAGE/' + lang.filename
+ bin_before = rom.getFileByName(filename)
+ strings = StrHandler.deserialize(bin_before)
+ strings.strings[string_id-1] = get_locales().translate(MESSAGE, lang.locale.replace('-', '_'))
+ bin_after = StrHandler.serialize(strings)
+ rom.setFileByName(filename, bin_after)
+ try:
+ apply()
+ except RuntimeError as ex:
+ raise ex
+
+
+ def unapply(self, unapply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data):
+ raise NotImplementedError()
diff --git a/skytemple_files/patch/patches.py b/skytemple_files/patch/patches.py
index 1048f8aac..621b83bc5 100644
--- a/skytemple_files/patch/patches.py
+++ b/skytemple_files/patch/patches.py
@@ -45,6 +45,8 @@
from skytemple_files.patch.handler.fairy_gummies import ImplementFairyGummiesPatchHandler
from skytemple_files.patch.handler.extract_bar_list import ExtractBarItemListPatchHandler
from skytemple_files.patch.handler.exp_share import ExpSharePatchHandler
+from skytemple_files.patch.handler.choose_starter import ChooseStarterPatchHandler
+from skytemple_files.patch.handler.skip_quiz import SkipQuizPatchHandler
from skytemple_files.patch.handler.complete_team_control import CompleteTeamControl
from skytemple_files.patch.handler.far_off_pal_overdrive import FarOffPalOverdrive
from skytemple_files.patch.handler.partners_trigger_hidden_traps import PartnersTriggerHiddenTraps
@@ -67,6 +69,8 @@ class PatchType(Enum):
EXTRACT_DUNGEON_DATA = ExtractDungeonDataPatchHandler
FIX_EVOLUTION = FixEvolutionPatchHandler
EXP_SHARE = ExpSharePatchHandler
+ CHOOSE_STARTER = ChooseStarterPatchHandler
+ SKIP_QUIZ = SkipQuizPatchHandler
COMPLETE_TEAM_CONTROL = CompleteTeamControl
FAR_OFF_PAL_OVERDRIVE = FarOffPalOverdrive
PARTNERS_TRIGGER_HIDDEN_TRAPS = PartnersTriggerHiddenTraps