Decompile GenerateMission (0x0205D224)

864 instructions, a 19-step pipeline with five jump tables. No region
preprocessor directives -- all three regions assemble identical text, so one
source serves US/EU/JP.

asm/main_0205D1F4.s is split: GenerateMission merges into src/main_0205D11C.c
and the remainder becomes asm/main_0205DFAC.s. main.lsf swaps the object in
place. include/main_0205D11C.h gains struct unk_0205DFAC (the 6-byte record),
struct unk_0205D224 (the 0x22-byte template) and the prototype;
CheckItemForMissionType's second parameter is corrected to
union mission_subtype *.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cecilarmitais
2026-09-08 16:39:41 -07:00
parent 240bea143d
commit 2447a0200d
7 changed files with 675 additions and 1045 deletions

View File

@@ -1,37 +0,0 @@
#pragma once
.public CanDungeonBeUsedForMission
.public CanMonsterBeUsedForMission
.public CanMonsterBeUsedForMissionWrapper
.public CheckItemForMissionType
.public CheckMonsterForMissionType
.public ClearMissionData
.public GetAllPossibleMonsters
.public GetMaxItemsAllowed
.public GetMaxMembersAllowed
.public GetMissionSpecificFixedRoom
.public GetSecondFormIfValid
.public IsAvailableItem
.public IsForbiddenFloor
.public IsMissionValid
.public IsStorableItem
.public IsThrownItem
.public LoadMissionTemplates
.public MemAlloc
.public MemFree
.public MISSION_DELIVER_LIST_PTR
.public MtNext
.public RandInt
.public RandIntSafe
.public RollRandomItemReward
.public sub_0205CF58
.public sub_0205E090
.public sub_0205E1E8
.public sub_0205E258
.public sub_020627F4
.public sub_02062814
.public sub_0206282C
.public sub_02062900
.public sub_02062AD0
.public sub_02062C4C
.public sub_02062D40
.public sub_02063424

View File

@@ -0,0 +1,4 @@
#pragma once
.public CanMonsterBeUsedForMissionWrapper
.public MemAlloc
.public sub_020627F4

File diff suppressed because it is too large Load Diff

38
asm/main_0205DFAC.s Normal file
View File

@@ -0,0 +1,38 @@
.include "asm/macros.inc"
.include "main_0205DFAC.inc"
.text
arm_func_start CountAndPopulateValidMissionTableMonsters
CountAndPopulateValidMissionTableMonsters: ; 0x0205DFAC
stmdb sp!, {r4, r5, r6, r7, r8, sb, sl, lr}
ldrh r6, [r1, #2]
ldrh r8, [r1, #4]
mov sl, r0
mov r7, #0
bl sub_020627F4
mov r4, r0
mov r0, r6, lsl #1
mov r1, #0xf
bl MemAlloc
mov sb, r0
mov r5, r7
add r4, r4, r8, lsl #1
b _0205E008
_0205DFE4:
mov r0, r5, lsl #1
ldrsh r8, [r0, r4]
mov r0, r8
bl CanMonsterBeUsedForMissionWrapper
cmp r0, #0
movne r0, r7, lsl #1
strneh r8, [sb, r0]
addne r7, r7, #1
add r5, r5, #1
_0205E008:
cmp r5, r6
blt _0205DFE4
mov r0, r7
str sb, [sl]
ldmia sp!, {r4, r5, r6, r7, r8, sb, sl, pc}
arm_func_end CountAndPopulateValidMissionTableMonsters

View File

@@ -5,9 +5,23 @@
#include "util.h"
#include "mission.h"
struct unk_0205DFAC {
u16 field_0x0;
u16 field_0x2;
s16 field_0x4;
};
struct unk_0205D224 {
u16 field_0x0;
struct unk_0205DFAC field_0x2[5];
enum mission_type field_0x20;
union mission_subtype field_0x21;
};
bool8 sub_0205CF58(enum mission_type type, union mission_subtype* subtype, struct dungeon_floor_pair* df_pair);
bool8 CheckMonsterForMissionType(enum mission_type type, union mission_subtype* subtype, enum monster_id outlaw_backup_species, bool8 arg3);
bool8 CheckItemForMissionType(u32 r0, u32 r1, s16 r2);
bool8 CheckItemForMissionType(u32 r0, union mission_subtype *r1, s16 r2);
bool8 sub_0205D1F4(struct mission *mission);
s32 GenerateMission(struct unk_0205D224 *tmpl, struct mission *mission);
#endif //PMDSKY_MAIN_0205D11C_H

View File

@@ -348,7 +348,7 @@ Static main
Object src/main_0205C73C.o
Object asm/main_0205CA40.o
Object src/main_0205D11C.o
Object asm/main_0205D1F4.o
Object asm/main_0205DFAC.o
Object src/main_0205E01C.o
Object src/main_0205E288.o
Object asm/main_0205E48C.o

View File

@@ -5,6 +5,14 @@
#include "item_util.h"
#include "item_util_4.h"
#include "mission.h"
#include "item_util.h"
#include "main_02001188.h"
#include "main_0200224C.h"
#include "main_02054BE0.h"
#include "main_02062A58.h"
#include "main_0205E01C.h"
#include "main_0205E288.h"
#include "dungeon_recruitment_2.h"
extern u8 _020A39C0;
extern u8 _020A39D4;
@@ -18,6 +26,26 @@ extern const char _020A3AD4[];
extern const char _020A3AF4[];
extern const char _020A3B18[];
extern s32 RandIntSafe(s32 n);
extern void ClearMissionData(struct mission *mission);
extern void *MemAlloc(u32 len, u32 flags);
extern struct mission_deliver_list MISSION_DELIVER_LIST_PTR;
extern s32 CanDungeonBeUsedForMission(enum dungeon_id);
extern s32 CountAndPopulateValidMissionTableMonsters(s16 **out, struct unk_0205DFAC *spec);
extern s32 GetAllPossibleMonsters(s16 **out);
extern u8 GetMissionSpecificFixedRoom(enum mission_type, u32);
extern bool8 IsAvailableItem(s16 item_id);
extern void *LoadMissionTemplates(void);
extern s32 MtNext(void);
extern void RollRandomItemReward(struct dungeon_floor_pair *, enum mission_type, s16 *out);
extern void sub_02062900(u16, s16 *, void *out);
extern bool8 sub_02062AD0(enum monster_id);
extern bool8 sub_02062C4C(s32, u8 *, struct dungeon_floor_pair *, s32);
extern s32 sub_02062814(void **out);
extern s32 sub_0206282C(u8 *out);
extern bool8 sub_02062D40(struct dungeon_floor_pair *);
extern s16 *sub_020627F4(void);
extern enum monster_id GetBaseForm(enum monster_id);
extern s32 GetBodySize(enum monster_id);
extern bool8 IsForbiddenFloor(struct dungeon_floor_pair*);
@@ -93,7 +121,7 @@ bool8 CheckMonsterForMissionType(enum mission_type type, union mission_subtype*
return TRUE;
}
bool8 CheckItemForMissionType(u32 r0, u32 r1, s16 r2)
bool8 CheckItemForMissionType(u32 r0, union mission_subtype *r1, s16 r2)
{
if (r2 == 0) {
Debug_Print0(_020A3AD4, r2);
@@ -129,3 +157,591 @@ bool8 sub_0205D1F4(struct mission *mission) {
}
return TRUE;
}
s32 GenerateMission(struct unk_0205D224 *tmpl, struct mission *mission)
{
s16 *sp10;
void *sp0c;
s16 *sp08;
struct { u8 list[1]; struct dungeon_floor_pair df; } x;
s32 count;
s32 err;
s16 m;
count = GetAllPossibleMonsters(&sp10);
if (count == 0) {
ClearMissionData(mission);
return 2;
}
mission->status = MISSION_STATUS_SUSPENDED;
mission->type = tmpl->field_0x20;
mission->subtype = tmpl->field_0x21;
{
s32 e;
do {
switch (tmpl->field_0x2[1].field_0x0) {
case 0:
case 1:
x.list[0] = *(u16 *)&tmpl->field_0x2[1].field_0x4;
x.df.dungeon_id = (enum dungeon_id)x.list[0];
if (tmpl->field_0x2[1].field_0x0 == 0 && CanDungeonBeUsedForMission(x.df.dungeon_id) == 0) {
e = 1;
} else if (!sub_02062C4C(1, x.list, &x.df, sub_0205E258(mission))) {
e = sub_0205E258(mission) ? 1 : 2;
} else if (!sub_0205CF58(tmpl->field_0x20, &tmpl->field_0x21, &x.df)) {
e = 1;
} else {
e = 0;
mission->dungeon_id = x.df.dungeon_id;
mission->floor = x.df.floor_id;
}
continue;
case 5:
{
s32 n;
s32 ok;
u8 *p;
n = sub_02062814(&sp0c);
if (tmpl->field_0x20 != 3 || tmpl->field_0x21.other != 3) {
e = 1;
continue;
}
p = MemAlloc(n, 0xf);
n = sub_0206282C(p);
if (n == 0) {
MemFree(p);
e = 1;
continue;
}
ok = sub_02062C4C(n, p, &x.df, sub_0205E258(mission));
x.df.floor_id = sub_02063424(mission->type, &mission->subtype, x.df.dungeon_id);
while (x.df.floor_id != 0) {
if (!IsForbiddenFloor(&x.df)) {
break;
}
x.df.floor_id--;
}
MemFree(p);
if (ok && x.df.floor_id != 0) {
e = 0;
mission->dungeon_id = x.df.dungeon_id;
mission->floor = x.df.floor_id;
continue;
}
}
break;
case 2:
case 3:
case 4:
default:
{
s32 n;
u8 *p;
e = 1;
n = *(s32 *)&MISSION_DELIVER_LIST_PTR.unk0[0xc];
p = *(u8 **)&MISSION_DELIVER_LIST_PTR.unk0[8];
if (n == 0) {
e = 2;
continue;
}
if (!sub_02062C4C(n, p, &x.df, sub_0205E258(mission))) {
if (sub_0205E258(mission) == 0) {
e = 2;
}
continue;
}
switch (mission->type) {
case 2:
case 3:
case 4:
case 5:
if (GetMaxMembersAllowed(x.df.dungeon_id) < 4) {
e = 0;
}
break;
case 7:
if (GetMaxItemsAllowed(x.df.dungeon_id) == 0) {
e = 0;
}
case 6:
if (!sub_02062D40(&x.df)) {
e = 0;
}
break;
case 10:
if (mission->subtype.other == 4 && GetMaxMembersAllowed(x.df.dungeon_id) < 4) {
e = 0;
}
break;
default:
break;
}
if (e != 0) {
e = 0;
mission->dungeon_id = x.df.dungeon_id;
mission->floor = x.df.floor_id;
continue;
}
}
break;
}
e = 1;
} while (0);
if (e) {
ClearMissionData(mission);
return e;
}
mission->description_id = MtNext() & 0xFFFFFF;
sp08 = NULL;
}
do {
switch (tmpl->field_0x2[2].field_0x0) {
case 0:
case 1: {
m = tmpl->field_0x2[2].field_0x4;
if (!CheckMonsterForMissionType(tmpl->field_0x20, &tmpl->field_0x21, (enum monster_id)m, TRUE)) {
err = 1;
} else if (tmpl->field_0x20 == MISSION_CHALLENGE_REQUEST && tmpl->field_0x21.other == 0) {
if (CanMonsterBeUsedForMission(m, m != MONSTER_LUCARIO && m != MONSTER_LUCARIO_SECONDARY)) {
mission->client = (enum monster_id)m;
mission->target = (enum monster_id)*(u16 *)&tmpl->field_0x2[3].field_0x4;
mission->outlaw_backup_species = (enum monster_id)*(u16 *)&tmpl->field_0x2[4].field_0x4;
err = 0;
} else {
err = 1;
}
} else {
if (tmpl->field_0x2[2].field_0x0 == 0) {
if (!CanMonsterBeUsedForMissionWrapper(m)) {
err = 1;
continue;
}
} else if (!sub_02062AD0((enum monster_id)m)) {
err = 1;
continue;
}
mission->client = (enum monster_id)m;
break;
}
continue;
}
case 2: {
err = CountAndPopulateValidMissionTableMonsters(&sp08, &tmpl->field_0x2[2]);
if (err == 0) {
MemFree(sp08);
sp08 = NULL;
err = 1;
} else {
m = sp08[RandInt(err)];
if (!CheckMonsterForMissionType(tmpl->field_0x20, &tmpl->field_0x21, (enum monster_id)m, TRUE)) {
MemFree(sp08);
sp08 = NULL;
err = 1;
} else {
if (!(tmpl->field_0x2[2].field_0x0 == tmpl->field_0x2[3].field_0x0
&& tmpl->field_0x2[2].field_0x2 == tmpl->field_0x2[3].field_0x2
&& (u16)tmpl->field_0x2[2].field_0x4 == (u16)tmpl->field_0x2[3].field_0x4)) {
err = 0;
MemFree(sp08);
sp08 = NULL;
}
mission->client = (enum monster_id)m;
if (!(mission->floor & 1)) {
mission->client = (enum monster_id)GetSecondFormIfValid((enum monster_id)mission->client);
}
break;
}
}
continue;
}
case 3: {
u16 n = tmpl->field_0x2[2].field_0x2;
sp08 = sub_020627F4() + (u16)tmpl->field_0x2[2].field_0x4;
m = sp08[RandInt(n)];
if (!CheckMonsterForMissionType(tmpl->field_0x20, &tmpl->field_0x21, (enum monster_id)m, TRUE)) {
err = 1;
} else if (!sub_02062AD0((enum monster_id)m)) {
err = 1;
} else {
mission->client = (enum monster_id)m;
if (!(mission->floor & 1)) {
mission->client = (enum monster_id)GetSecondFormIfValid((enum monster_id)mission->client);
}
err = 0;
sp08 = NULL;
break;
}
continue;
}
case 4: {
s32 n = GetAllPossibleMonsters(&sp08);
if (n == 0) {
err = 2;
} else {
m = sp08[RandIntSafe(n)];
if (!CheckMonsterForMissionType(tmpl->field_0x20, &tmpl->field_0x21, (enum monster_id)m, TRUE)) {
err = 1;
} else {
mission->client = (enum monster_id)m;
if (!(mission->floor & 1)) {
mission->client = (enum monster_id)GetSecondFormIfValid((enum monster_id)mission->client);
}
err = 0;
sp08 = NULL;
break;
}
}
continue;
}
}
if (sub_0205E1E8(mission)) {
if (sp08 != NULL) {
MemFree(sp08);
sp08 = NULL;
}
mission->target = (enum monster_id)mission->client;
mission->outlaw_backup_species = (enum monster_id)0;
err = 0;
} else {
int i;
s32 sl;
if ((mission->type == MISSION_ARREST_OUTLAW && mission->subtype.outlaw == MISSION_OUTLAW_HIDEOUT) ||
(mission->type == MISSION_CHALLENGE_REQUEST && mission->subtype.challenge == MISSION_CHALLENGE_NORMAL)) {
sl = 2;
} else {
mission->outlaw_backup_species = (enum monster_id)0;
sl = 1;
}
for (i = 0; i < sl || (err = 0); i++) {
switch (tmpl->field_0x2[i + 3].field_0x0) {
case 0:
case 1: {
m = tmpl->field_0x2[i + 3].field_0x4;
if (!CheckMonsterForMissionType(tmpl->field_0x20, &tmpl->field_0x21, (enum monster_id)m, 0)) {
err = 1;
break;
} else if (tmpl->field_0x20 == MISSION_ARREST_OUTLAW &&
tmpl->field_0x21.outlaw == MISSION_OUTLAW_HIDEOUT) {
if (!CanMonsterBeUsedForMissionWrapper(m)) {
err = 1;
} else {
mission->target = (enum monster_id)m;
mission->outlaw_backup_species = (enum monster_id)*(u16 *)&tmpl->field_0x2[4].field_0x4;
err = 0;
}
break;
} else if (tmpl->field_0x2[i + 3].field_0x0 == 0) {
if (!CanMonsterBeUsedForMissionWrapper(m)) {
err = 1;
break;
}
} else {
if (!sub_02062AD0((enum monster_id)m)) {
err = 1;
break;
}
}
((s16 *)&mission->target)[i] = m;
continue;
}
case 2: {
s16 id;
if (sp08 == NULL) {
err = CountAndPopulateValidMissionTableMonsters(&sp08, &tmpl->field_0x2[3] + i);
if (err == 0) {
MemFree(sp08);
sp08 = NULL;
err = 1;
break;
}
}
id = sp08[RandInt(err)];
if (!CheckMonsterForMissionType(tmpl->field_0x20, &tmpl->field_0x21, (enum monster_id)id, 0)) {
MemFree(sp08);
sp08 = NULL;
err = 1;
} else {
if (i + 1 >= sl ||
tmpl->field_0x2[(u32)i + 3].field_0x0 != tmpl->field_0x2[(u32)i + 4].field_0x0 ||
tmpl->field_0x2[(u32)i + 3].field_0x2 != tmpl->field_0x2[(u32)i + 4].field_0x2 ||
(u16)tmpl->field_0x2[(u32)i + 3].field_0x4 != (u16)tmpl->field_0x2[(u32)i + 4].field_0x4) {
err = 0;
MemFree(sp08);
sp08 = NULL;
}
((s16 *)&mission->target)[i] = id;
if (!(mission->floor & 1)) {
((s16 *)&mission->target)[i] = GetSecondFormIfValid(((s16 *)&mission->target)[i]);
}
continue;
}
break;
}
case 3: {
s16 id;
s32 m = tmpl->field_0x2[i + 3].field_0x2;
sp08 = sub_020627F4() + (u16)tmpl->field_0x2[i + 3].field_0x4;
id = sp08[RandInt(m)];
if (!CheckMonsterForMissionType(tmpl->field_0x20, &tmpl->field_0x21, (enum monster_id)id, 0)) {
err = 1;
} else if (!sub_02062AD0((enum monster_id)id)) {
err = 1;
} else {
((s16 *)&mission->target)[i] = id;
if (!(mission->floor & 1)) {
((s16 *)&mission->target)[i] = GetSecondFormIfValid(((s16 *)&mission->target)[i]);
}
sp08 = NULL;
err = 0;
continue;
}
break;
}
case 4:
case 6: {
s16 id;
s32 m = GetAllPossibleMonsters(&sp08);
if (m == 0) {
err = 2;
} else {
id = sp08[RandIntSafe(m)];
if (!CheckMonsterForMissionType(tmpl->field_0x20, &tmpl->field_0x21, (enum monster_id)id, 0)) {
err = 1;
} else {
((s16 *)&mission->target)[i] = id;
if (!(mission->floor & 1)) {
((s16 *)&mission->target)[i] = GetSecondFormIfValid(((s16 *)&mission->target)[i]);
}
sp08 = NULL;
continue;
}
}
break;
}
default:
continue;
}
break;
}
}
} while (0);
if (err != 0) {
ClearMissionData(mission);
return err;
}
do {
s16 item;
switch (tmpl->field_0x2[0].field_0x0) {
case 0:
case 1:
item = tmpl->field_0x2[0].field_0x4;
if (CheckItemForMissionType(tmpl->field_0x20, &tmpl->field_0x21, item) == 0) {
err = 1;
} else if (tmpl->field_0x2[0].field_0x0 == 0 && IsAvailableItem(item) == 0) {
err = 1;
} else {
mission->item_wanted = (enum item_id)item;
break;
}
continue;
case 2: {
s16 *buf;
s32 k;
s32 i;
s32 n;
s32 off;
s16 v;
s16 *base;
n = tmpl->field_0x2[0].field_0x2;
off = (u16)tmpl->field_0x2[0].field_0x4;
k = 0;
base = (s16 *)LoadMissionTemplates();
buf = (s16 *)MemAlloc(n * 2, 0xf);
for (i = 0; i < n; i++) {
v = base[off + i];
if (IsAvailableItem(v) != 0) {
buf[k] = v;
k++;
}
}
if (k == 0) {
MemFree(buf);
err = 1;
} else {
item = buf[RandIntSafe(k)];
MemFree(buf);
if (CheckItemForMissionType(tmpl->field_0x20, &tmpl->field_0x21, item) == 0) {
err = 1;
} else {
mission->item_wanted = (enum item_id)item;
break;
}
}
continue;
}
case 3: {
s32 off;
s32 n;
s16 *p;
n = tmpl->field_0x2[0].field_0x2;
off = (u16)tmpl->field_0x2[0].field_0x4;
p = (s16 *)LoadMissionTemplates() + off;
item = p[RandIntSafe(n)];
if (CheckItemForMissionType(tmpl->field_0x20, &tmpl->field_0x21, item) == 0) {
err = 1;
} else {
mission->item_wanted = (enum item_id)item;
break;
}
continue;
}
case 4: {
s32 cnt;
s16 *lst;
s16 v;
cnt = *(s32 *)&MISSION_DELIVER_LIST_PTR.unk0[4];
lst = *(s16 **)&MISSION_DELIVER_LIST_PTR.unk0[0];
if (cnt == 0)
v = 0x46;
else
v = lst[RandIntSafe(cnt)];
mission->item_wanted = (enum item_id)v;
if (*(s16 *)&mission->item_wanted == 0) {
if (mission->type == 6 || mission->type == 7) {
err = 1;
continue;
} else {
do {
RollRandomItemReward((struct dungeon_floor_pair *)&mission->dungeon_id,
mission->type,
(s16 *)&mission->item_wanted);
} while (IsThrownItem(*(s16 *)&mission->item_wanted)
|| !IsStorableItem(*(s16 *)&mission->item_wanted));
}
}
break;
}
default:
break;
}
err = 0;
} while (0);
if (err != 0) {
ClearMissionData(mission);
return err;
}
if (mission->dungeon_id == 0x26 || mission->dungeon_id == 0x29 ||
GetMaxMembersAllowed(*(volatile enum dungeon_id *)&mission->dungeon_id) < 4) {
mission->restriction_type = (enum mission_restriction_type)0;
mission->restriction.monster_id = (enum monster_id)0;
} else {
sub_02062900((u16)count, sp10, &mission->restriction_type);
}
err = 0;
mission->unique_map_id = 0;
switch (mission->type) {
case 3:
switch (mission->subtype.other) {
case 1:
mission->unique_map_id = GetMissionSpecificFixedRoom(mission->type, mission->subtype.other);
break;
case 2:
mission->unique_map_id = GetMissionSpecificFixedRoom(mission->type, mission->subtype.other);
break;
case 3:
break;
}
break;
case 6:
switch (mission->subtype.other) {
case 0:
case 1:
case 2:
case 3:
break;
case 4:
{
enum dungeon_id dn = mission->dungeon_id;
s32 v = sub_02063424(*(volatile enum mission_type *)&mission->type, &mission->subtype, dn);
if (sub_0205E090(dn, v, 1)) {
err = 1;
} else {
mission->floor = v;
}
break;
}
}
break;
case 9:
if (*(s16 *)&mission->item_wanted == 0x49 || mission->client == mission->target) {
err = 1;
}
break;
case 10:
switch (mission->subtype.other) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 7:
break;
case 6:
mission->unique_map_id = GetMissionSpecificFixedRoom(mission->type, mission->subtype.other);
break;
}
break;
case 11:
switch (mission->subtype.other) {
case 5:
{
enum dungeon_id dn = mission->dungeon_id;
s32 v = sub_02063424(*(volatile enum mission_type *)&mission->type, &mission->subtype, dn);
if (sub_0205E090(dn, v, 1)) {
err = 1;
break;
}
mission->floor = v;
}
case 0:
case 1:
case 2:
case 3:
case 4:
default:
mission->unique_map_id = GetMissionSpecificFixedRoom(mission->type, mission->subtype.other);
break;
}
break;
case 12:
mission->unique_map_id = GetMissionSpecificFixedRoom(mission->type, mission->subtype.other);
mission->restriction_type = (enum mission_restriction_type)0;
mission->restriction.monster_id = (enum monster_id)0;
break;
case 14:
switch (mission->subtype.other) {
case 1:
mission->restriction_type = (enum mission_restriction_type)0;
mission->restriction.monster_id = (enum monster_id)0;
break;
case 2:
err = 1;
break;
}
break;
}
if (err) {
ClearMissionData(mission);
return err;
}
return 0;
}