Initial commit

This commit is contained in:
EpochFlame 2021-09-27 20:13:41 -04:00
commit 01851b93c4
27 changed files with 648964 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
.vscode
*.dat
*.exe
*.dll
*.idb
*.id0
*.id1
*.id2
*.nam
*.til
*.o
*.out
*.elf
*.dol
*.a
*.d
*.map
*.exe
*.dump
*.7z
*.bat
build
tools/mwcc_compiler/

117
Makefile Normal file
View File

@ -0,0 +1,117 @@
ifneq ($(findstring MINGW,$(shell uname)),)
WINDOWS := 1
endif
ifneq ($(findstring MSYS,$(shell uname)),)
WINDOWS := 1
endif
#-------------------------------------------------------------------------------
# Files
#-------------------------------------------------------------------------------
TARGET_COL := gc
NAME := pikmin
VERSION := usa.1
#VERSION := usa.0
BUILD_DIR := build/$(NAME).$(VERSION)
SRC_DIRS := src
ASM_DIRS := asm
# Inputs
S_FILES := $(wildcard asm/*.s)
C_FILES := $(wildcard src/*.c)
LDSCRIPT := $(BUILD_DIR)/ldscript.lcf
# Outputs
DOL := $(BUILD_DIR)/main.dol
ELF := $(DOL:.dol=.elf)
MAP := $(BUILD_DIR)/pikmin1.map
include obj_files.mk
O_FILES := $(INIT_O_FILES) $(EXTAB_O_FILES) $(EXTABINDEX_O_FILES) $(TEXT_O_FILES) \
$(CTORS_O_FILES) $(DTORS_O_FILES) $(RODATA_O_FILES) $(DATA_O_FILES) \
$(BSS_O_FILES) $(SDATA_O_FILES) $(SBSS_O_FILES) $(SDATA2_O_FILES) \
$(SBSS2_O_FILES) \
#-------------------------------------------------------------------------------
# Tools
#-------------------------------------------------------------------------------
MWCC_VERSION := 1.0
MWLD_VERSION := 1.1
# Programs
ifeq ($(WINDOWS),1)
WINE :=
else
WINE := wine
endif
AS := $(DEVKITPPC)/bin/powerpc-eabi-as.exe
OBJCOPY := $(DEVKITPPC)/bin/powerpc-eabi-objcopy.exe
CPP := $(DEVKITPPC)/bin/powerpc-eabi-cpp.exe -P
CC := $(WINE) tools/mwcc_compiler/$(MWCC_VERSION)/mwcceppc.exe
LD := $(WINE) tools/mwcc_compiler/$(MWLD_VERSION)/mwldeppc.exe
ELF2DOL := tools/elf2dol
SHA1SUM := sha1sum
PYTHON := python3
POSTPROC := tools/postprocess.py
# Options
INCLUDES := -i include/ -i include/dolphin/ -i src/sysdolphin/ -i include/dolphin/mtx/
ASFLAGS := -mgekko -I include/
LDFLAGS := -map $(MAP) -fp hard -nodefaults
CFLAGS := -Cpp_exceptions off -proc gekko -fp hard -O4,p -nodefaults -msgstyle gcc $(INCLUDES)
# for postprocess.py
PROCFLAGS := -fprologue-fixup=old_stack
# elf2dol needs to know these in order to calculate sbss correctly.
SDATA_PDHR := 9
SBSS_PDHR := 10
#-------------------------------------------------------------------------------
# Recipes
#-------------------------------------------------------------------------------
### Default target ###
default: all
all: $(DOL)
ALL_DIRS := build $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(ASM_DIRS))
# Make sure build directory exists before compiling anything
DUMMY != mkdir -p $(ALL_DIRS)
$(LDSCRIPT): ldscript.lcf
$(CPP) -MMD -MP -MT $@ -MF $@.d -I include/ -I . -DBUILD_DIR=$(BUILD_DIR) -o $@ $<
$(DOL): $(ELF) | tools
$(ELF2DOL) $< $@ $(SDATA_PDHR) $(SBSS_PDHR) $(TARGET_COL)
$(SHA1SUM) -c sha1/$(NAME).$(VERSION).sha1
clean:
rm -f -d -r build
$(ELF): $(O_FILES) $(LDSCRIPT)
$(LD) $(LDFLAGS) -lcf $(LDSCRIPT) $(O_FILES) -o $@
# The Metrowerks linker doesn't generate physical addresses in the ELF program headers. This fixes it somehow.
$(OBJCOPY) $@ $@
$(BUILD_DIR)/%.o: %.s
$(AS) $(ASFLAGS) -o $@ $<
$(BUILD_DIR)/%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
$(PYTHON) $(POSTPROC) $(PROCFLAGS) $@
### Debug Print ###
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true

3
README.MD Normal file
View File

@ -0,0 +1,3 @@
Pikmin 1 (USA Revision 1) disassembly/decompilation
The elf2dol in tools should be compiled once for Windows.

606
asm/bss.s Normal file
View File

@ -0,0 +1,606 @@
.include "macros.inc"
.section .bss, "wa" # 0x802E9640 - 0x803E81E5
.global dac
dac:
.skip 0xC
.global audio_hp
audio_hp:
.skip 0x14
.global audioproc_mq
audioproc_mq:
.skip 0x20
.global msgbuf
msgbuf:
.skip 0x40
.global finfo$42
finfo$42:
.skip 0x3C
.global cmd$43
cmd$43:
.skip 0x34
.global dsp_buf
dsp_buf:
.skip 0x10
.global dsp_buf_1
dsp_buf_1:
.skip 0x10
.global PLAYER_CALLLIST
PLAYER_CALLLIST:
.skip 0xC0
.global mq
mq:
.skip 0x20
.global msgbuf_1
msgbuf_1:
.skip 0x200
.global CALLSTACK
CALLSTACK:
.skip 0x8000
.global finfo$221
finfo$221:
.skip 0x3C
.global req$222
req$222:
.skip 0x80
.global finfo$264
finfo$264:
.skip 0x3C
.global req$265
req$265:
.skip 0x20
.global finfo$272
finfo$272:
.skip 0x3C
.global req$273
req$273:
.skip 0x20
.global finfo$290
finfo$290:
.skip 0x3C
.global finfo$307
finfo$307:
.skip 0x3C
.global finfo$312
finfo$312:
.skip 0x3C
.global dvd_file
dvd_file:
.skip 0x800
.global dvd_entrynum
dvd_entrynum:
.skip 0x88
.global GLOBAL_CHANNEL
GLOBAL_CHANNEL:
.skip 0x80
.global CHANNEL
CHANNEL:
.skip 0x14000
.global waitp
waitp:
.skip 0x80
.global waittime
waittime:
.skip 0x80
.global DSPCH
DSPCH:
.skip 0x400
.global CH_BUF
CH_BUF:
.skip 0x6000
.global FX_BUF
FX_BUF:
.skip 0x80
.global bankp
bankp:
.skip 0x400
.global wavearc
wavearc:
.skip 0x400
.global wavegroup
wavegroup:
.skip 0x400
.global WS_V2P_TABLE
WS_V2P_TABLE:
.skip 0x200
.global BNK_V2P_TABLE
BNK_V2P_TABLE:
.skip 0x200
.global SINTABLE
SINTABLE:
.skip 0x408
.global FH_TO_FAT
FH_TO_FAT:
.skip 0x400
.global FAT
FAT:
.skip 0x800
.global fattmp
fattmp:
.skip 0x800
.global JV_DIR_NAME
JV_DIR_NAME:
.skip 0x400
.global JV_ARC_NAME
JV_ARC_NAME:
.skip 0x200
.global JV_ARC
JV_ARC:
.skip 0x40
.global finfo$150
finfo$150:
.skip 0x58
.global dmabuffer
dmabuffer:
.skip 0x10000
.global TRACK_LIST
TRACK_LIST:
.skip 0x100
.global SEQ_ARG
SEQ_ARG:
.skip 0x20
.global seq
seq:
.skip 0x560
.global lbl_80320000
lbl_80320000:
.skip 0x4
.global lbl_80320004
lbl_80320004:
.skip 0x42E9C
.global ROOT_OUTER
ROOT_OUTER:
.skip 0x400
.global rootseq
rootseq:
.skip 0x40
.global FREE_SEQP_QUEUE
FREE_SEQP_QUEUE:
.skip 0x400
.global process_stack
process_stack:
.skip 0x10
.global system_se
system_se:
.skip 0x6C
.global system_se_stop
system_se_stop:
.skip 0x6C
.global player_se$123
player_se$123:
.skip 0x6C
.global player_se_stop$189
player_se_stop$189:
.skip 0x6C
.global outerparam$219
outerparam$219:
.skip 0x40
.global bgm
bgm:
.skip 0xD38
.global demo_q
demo_q:
.skip 0x70
.global seq_loadbuffer
seq_loadbuffer:
.skip 0x400
.global rootseq_1
rootseq_1:
.skip 0x4340
.global rootseqhandle
rootseqhandle:
.skip 0x40
.global as
as:
.skip 0x98
.global SC
SC:
.skip 0x21A50
.global copyinfo
copyinfo:
.skip 0x18
.global interleavebuf
interleavebuf:
.skip 0x38
.global filename
filename:
.skip 0x40
.global dvd_buf
dvd_buf:
.skip 0xC
.global file_header
file_header:
.skip 0x44
.global gop_header
gop_header:
.skip 0x18
.global jac_hvqmThread
jac_hvqmThread:
.skip 0x318
.global hvqmStack
hvqmStack:
.skip 0x1000
.global clipTable
clipTable:
.skip 0x200
.global divTable
divTable:
.skip 0x40
.global mcdivTable
mcdivTable:
.skip 0x800
.global fnVerts
fnVerts:
.skip 0x1800
.global fnNorms
fnNorms:
.skip 0x1800
.global fnTexs
fnTexs:
.skip 0x1000
.global matUsed
matUsed:
.skip 0x100
.global sintable
sintable:
.skip 0x4000
.global costable
costable:
.skip 0x4000
.global ident__8Matrix4f
ident__8Matrix4f:
.skip 0x40
.global lbl_80398880
lbl_80398880:
.skip 0xC
.global sys
sys:
.skip 0x334
.global dvdMesgQueue
dvdMesgQueue:
.skip 0x20
.global loadMesgQueue
loadMesgQueue:
.skip 0x20
.global sysMesgQueue
sysMesgQueue:
.skip 0x20
.global aramStream
aramStream:
.skip 0x14
.global lastName
lastName:
.skip 0x100
.global dvdStream
dvdStream:
.skip 0x54
.global dvdBufferedStream
dvdBufferedStream:
.skip 0x20
.global mMemoryTable$1026
mMemoryTable$1026:
.skip 0x10
.global Thread
Thread:
.skip 0x328
.global ThreadStack
ThreadStack:
.skip 0x2000
.global dvdThread
dvdThread:
.skip 0x320
.global dvdThreadStack
dvdThreadStack:
.skip 0x2000
.global sControllerPad
sControllerPad:
.skip 0x30
.global GColors
GColors:
.skip 0x8
.global CardThread
CardThread:
.skip 0x310
.global CardControl
CardControl:
.skip 0x70
.global gameflow
gameflow:
.skip 0x368
.global flowCont
flowCont:
.skip 0x260
.global resultTable
resultTable:
.skip 0x40
.global collExtents
collExtents:
.skip 0x18
.global bcs
bcs:
.skip 0x408
.global cst
cst:
.skip 0x80
.global CardWorkArea
CardWorkArea:
.skip 0xA000
.global cardData
cardData:
.skip 0x26000
.global CardStack
CardStack:
.skip 0x1DA4
.global lbl_803D0004
lbl_803D0004:
.skip 0x25C
.global YtexObj
YtexObj:
.skip 0x20
.global UVtexObj
UVtexObj:
.skip 0x20
.global playbackThread
playbackThread:
.skip 0x310
.global playbackThreadStack
playbackThreadStack:
.skip 0x1000
.global invCamMat
invCamMat:
.skip 0x40
.global triList
triList:
.skip 0x800
.global pikiInfMgr
pikiInfMgr:
.skip 0x28
.global pikiColors__4Piki
pikiColors__4Piki:
.skip 0x18
.global kinokoColors__4Piki
kinokoColors__4Piki:
.skip 0x18
.global _instances__15PikiShapeObject
_instances__15PikiShapeObject:
.skip 0x10
.global deadPikis__8GameStat
deadPikis__8GameStat:
.skip 0xC
.global fallPikis__8GameStat
fallPikis__8GameStat:
.skip 0xC
.global formationPikis__8GameStat
formationPikis__8GameStat:
.skip 0xC
.global freePikis__8GameStat
freePikis__8GameStat:
.skip 0xC
.global workPikis__8GameStat
workPikis__8GameStat:
.skip 0xC
.global mePikis__8GameStat
mePikis__8GameStat:
.skip 0xC
.global containerPikis__8GameStat
containerPikis__8GameStat:
.skip 0xC
.global bornPikis__8GameStat
bornPikis__8GameStat:
.skip 0xC
.global victimPikis__8GameStat
victimPikis__8GameStat:
.skip 0xC
.global mapPikis__8GameStat
mapPikis__8GameStat:
.skip 0xC
.global allPikis__8GameStat
allPikis__8GameStat:
.skip 0x10
.global workString__3zen
workString__3zen:
.skip 0x400
.global bloFile_Diary_Table__3zen
bloFile_Diary_Table__3zen:
.skip 0x28
.global pTexTable__Q23zen13StickCallBack
pTexTable__Q23zen13StickCallBack:
.skip 0x58
.global texTable__Q23zen9NumberTex
texTable__Q23zen9NumberTex:
.skip 0x28
.global shadowTexTable__Q23zen9NumberTex
shadowTexTable__Q23zen9NumberTex:
.skip 0x28
.global OSErrorTable
OSErrorTable:
.skip 0x40
.global Ecb
Ecb:
.skip 0xB0
.global Header
Header:
.skip 0x20
.global Scb
Scb:
.skip 0x58
.global Packet
Packet:
.skip 0x80
.global Alarm
Alarm:
.skip 0xA0
.global RunQueue
RunQueue:
.skip 0x100
.global IdleThread
IdleThread:
.skip 0x310
.global DefaultThread
DefaultThread:
.skip 0x310
.global IdleContext
IdleContext:
.skip 0x2C8
.global CommandList
CommandList:
.skip 0x40
.global AlarmForWA
AlarmForWA:
.skip 0x28
.global AlarmForTimeout
AlarmForTimeout:
.skip 0x28
.global AlarmForBreak
AlarmForBreak:
.skip 0x28
.global Prev
Prev:
.skip 0xC
.global Curr
Curr:
.skip 0x1C
.global tmpBuffer
tmpBuffer:
.skip 0x80
.global DummyCommandBlock
DummyCommandBlock:
.skip 0x30
.global ResetAlarm
ResetAlarm:
.skip 0x28
.global WaitingQueue
WaitingQueue:
.skip 0x20
.global bb2Buf
bb2Buf:
.skip 0x40
.global block$16
block$16:
.skip 0x30
.global regs
regs:
.skip 0x78
.global shdwRegs
shdwRegs:
.skip 0x78
.global HorVer
HorVer:
.skip 0x58
.global PADType
PADType:
.skip 0x10
.global Type
Type:
.skip 0x10
.global Origin
Origin:
.skip 0x30
.global cmdProbeDevice
cmdProbeDevice:
.skip 0x10
.global cmdFixDevice
cmdFixDevice:
.skip 0x10
.global __CARDBlock
__CARDBlock:
.skip 0x210
.global __CARDDiskNone
__CARDDiskNone:
.skip 0x20
.global gxData
gxData:
.skip 0x4F4
.global FifoObj
FifoObj:
.skip 0x84
.global DisplayListFifo
DisplayListFifo:
.skip 0x24
.global __savedGXdata
__savedGXdata:
.skip 0x4F4
.global fragmentinfo
fragmentinfo:
.skip 0x10
.global atexit_funcs
atexit_funcs:
.skip 0x100
.global __atexit_funcs
__atexit_funcs:
.skip 0x100
.global gTRKEventQueue
gTRKEventQueue:
.skip 0x28
.global gTRKBigEndian
gTRKBigEndian:
.skip 0x8
.global gTRKMsgBufs
gTRKMsgBufs:
.skip 0x19B0
.global gTRKFramingState
gTRKFramingState:
.skip 0x14
.global gTRKInputPendingPtr
gTRKInputPendingPtr:
.skip 0x4
.global gTRKDispatchTableSize
gTRKDispatchTableSize:
.skip 0x8
.global TRK_saved_exceptionID
TRK_saved_exceptionID:
.skip 0x4
.global gTRKSaveState
gTRKSaveState:
.skip 0x94
.global TRKvalue128_temp
TRKvalue128_temp:
.skip 0x10
.global gTRKState
gTRKState:
.skip 0xA8
.global gTRKCPUState
gTRKCPUState:
.skip 0x430
.global lc_base
lc_base:
.skip 0x8
.global TRK_mainError
TRK_mainError:
.skip 0x8
.global Ecb_1
Ecb_1:
.skip 0x18
.global aram_hp
aram_hp:
.skip 0x28
.global jac_audioThread
jac_audioThread:
.skip 0x2940
.global jac_audioStack
jac_audioStack:
.skip 0x1000
.global jac_dvdThread
jac_dvdThread:
.skip 0x320
.global jac_dvdStack
jac_dvdStack:
.skip 0x1000
.global EX_DSPTASK
EX_DSPTASK:
.skip 0x40
.global CGRP_ARRAY
CGRP_ARRAY:
.skip 0x40
.global aram_mother
aram_mother:
.skip 0x2C
.global EVENT
EVENT:
.skip 0x1B64
.global pic_ctrl
pic_ctrl:
.skip 0x1B0
.global dvd_ctrl
dvd_ctrl:
# .skip 0xAB40
.skip 0x40

5
asm/ctors.s Normal file
View File

@ -0,0 +1,5 @@
.include "macros.inc"
.section .ctors, "wa" # 0x80221F60 - 0x80221FC0
.global __init_cpp_exceptions_reference
__init_cpp_exceptions_reference:
.incbin "baserom.dol", 0x21EF60, 0x60

6329
asm/data.s Normal file

File diff suppressed because it is too large Load Diff

12
asm/dtors.s Normal file
View File

@ -0,0 +1,12 @@
.include "macros.inc"
.section ._dtors, "wa" # 0x80221FC0 - 0x80221FE0
.global __destroy_global_chain_reference
__destroy_global_chain_reference:
.incbin "baserom.dol", 0x21EFC0, 0x4
.global __fini_cpp_exceptions_reference
__fini_cpp_exceptions_reference:
.incbin "baserom.dol", 0x21EFC4, 0x4
.global __destroy_global_chain_reference_1
__destroy_global_chain_reference_1:
.incbin "baserom.dol", 0x21EFC8, 0x18

6
asm/exidx.s Normal file
View File

@ -0,0 +1,6 @@
.include "macros.inc"
.section ._exidx, "wa" # 0x80005500 - 0x80005560
.incbin "baserom.dol", 0x21EF00, 0x30
.global lbl_80005530
lbl_80005530:
.incbin "baserom.dol", 0x21EF30, 0x30

6
asm/extab.s Normal file
View File

@ -0,0 +1,6 @@
.include "macros.inc"
.section ._extab, "wa" # 0x800054C0 - 0x80005500
.global extab
extab:
.incbin "baserom.dol", 0x21EEC0, 0x40

2352
asm/init.s Normal file

File diff suppressed because it is too large Load Diff

266
asm/rodata.s Normal file
View File

@ -0,0 +1,266 @@
.include "macros.inc"
.section .rodata, "a" # 0x80221FE0 - 0x80222DC0
.global lbl_80221FE0
lbl_80221FE0:
.incbin "baserom.dol", 0x21EFE0, 0xC
.global lbl_80221FEC
lbl_80221FEC:
.incbin "baserom.dol", 0x21EFEC, 0xC
.global lbl_80221FF8
lbl_80221FF8:
.incbin "baserom.dol", 0x21EFF8, 0x10
.global lbl_80222008
lbl_80222008:
.incbin "baserom.dol", 0x21F008, 0xC
.global lbl_80222014
lbl_80222014:
.incbin "baserom.dol", 0x21F014, 0xC
.global lbl_80222020
lbl_80222020:
.incbin "baserom.dol", 0x21F020, 0x20
.global lbl_80222040
lbl_80222040:
.incbin "baserom.dol", 0x21F040, 0x34
.global lbl_80222074
lbl_80222074:
.incbin "baserom.dol", 0x21F074, 0x34
.global lbl_802220A8
lbl_802220A8:
.incbin "baserom.dol", 0x21F0A8, 0x20
.global lbl_802220C8
lbl_802220C8:
.incbin "baserom.dol", 0x21F0C8, 0x14
.global lbl_802220DC
lbl_802220DC:
.incbin "baserom.dol", 0x21F0DC, 0x2C
.global lbl_80222108
lbl_80222108:
.incbin "baserom.dol", 0x21F108, 0x10
.global lbl_80222118
lbl_80222118:
.incbin "baserom.dol", 0x21F118, 0x14
.global lbl_8022212C
lbl_8022212C:
.incbin "baserom.dol", 0x21F12C, 0x14C
.global lbl_80222278
lbl_80222278:
.incbin "baserom.dol", 0x21F278, 0x14
.global lbl_8022228C
lbl_8022228C:
.incbin "baserom.dol", 0x21F28C, 0x14
.global mcbtypetrans$1481
mcbtypetrans$1481:
.incbin "baserom.dol", 0x21F2A0, 0x18
.global lbl_802222B8
lbl_802222B8:
.incbin "baserom.dol", 0x21F2B8, 0x60
.global lbl_80222318
lbl_80222318:
.incbin "baserom.dol", 0x21F318, 0x60
.global lbl_80222378
lbl_80222378:
.incbin "baserom.dol", 0x21F378, 0xC
.global lbl_80222384
lbl_80222384:
.incbin "baserom.dol", 0x21F384, 0xC
.global lbl_80222390
lbl_80222390:
.incbin "baserom.dol", 0x21F390, 0x10
.global lbl_802223A0
lbl_802223A0:
.incbin "baserom.dol", 0x21F3A0, 0x14
.global lbl_802223B4
lbl_802223B4:
.incbin "baserom.dol", 0x21F3B4, 0x1C
.global lbl_802223D0
lbl_802223D0:
.incbin "baserom.dol", 0x21F3D0, 0x10
.global lbl_802223E0
lbl_802223E0:
.incbin "baserom.dol", 0x21F3E0, 0xC
.global lbl_802223EC
lbl_802223EC:
.incbin "baserom.dol", 0x21F3EC, 0xC
.global lbl_802223F8
lbl_802223F8:
.incbin "baserom.dol", 0x21F3F8, 0x10
.global lbl_80222408
lbl_80222408:
.incbin "baserom.dol", 0x21F408, 0x10
.global lbl_80222418
lbl_80222418:
.incbin "baserom.dol", 0x21F418, 0x28
.global lbl_80222440
lbl_80222440:
.incbin "baserom.dol", 0x21F440, 0x20
.global lbl_80222460
lbl_80222460:
.incbin "baserom.dol", 0x21F460, 0x20
.global lbl_80222480
lbl_80222480:
.incbin "baserom.dol", 0x21F480, 0x20
.global lbl_802224A0
lbl_802224A0:
.incbin "baserom.dol", 0x21F4A0, 0x10
.global lbl_802224B0
lbl_802224B0:
.incbin "baserom.dol", 0x21F4B0, 0x1C
.global lbl_802224CC
lbl_802224CC:
.incbin "baserom.dol", 0x21F4CC, 0xC
.global lbl_802224D8
lbl_802224D8:
.incbin "baserom.dol", 0x21F4D8, 0xA8
.global lbl_80222580
lbl_80222580:
.incbin "baserom.dol", 0x21F580, 0x18
.global lbl_80222598
lbl_80222598:
.incbin "baserom.dol", 0x21F598, 0xC
.global lbl_802225A4
lbl_802225A4:
.incbin "baserom.dol", 0x21F5A4, 0x18
.global lbl_802225BC
lbl_802225BC:
.incbin "baserom.dol", 0x21F5BC, 0x18
.global lbl_802225D4
lbl_802225D4:
.incbin "baserom.dol", 0x21F5D4, 0x10
.global lbl_802225E4
lbl_802225E4:
.incbin "baserom.dol", 0x21F5E4, 0x18
.global lbl_802225FC
lbl_802225FC:
.incbin "baserom.dol", 0x21F5FC, 0x18
.global lbl_80222614
lbl_80222614:
.incbin "baserom.dol", 0x21F614, 0x2C
.global lbl_80222640
lbl_80222640:
.incbin "baserom.dol", 0x21F640, 0xC
.global lbl_8022264C
lbl_8022264C:
.incbin "baserom.dol", 0x21F64C, 0xC
.global lbl_80222658
lbl_80222658:
.incbin "baserom.dol", 0x21F658, 0x10
.global lbl_80222668
lbl_80222668:
.incbin "baserom.dol", 0x21F668, 0xC
.global lbl_80222674
lbl_80222674:
.incbin "baserom.dol", 0x21F674, 0xC
.global lbl_80222680
lbl_80222680:
.incbin "baserom.dol", 0x21F680, 0x20
.global lbl_802226A0
lbl_802226A0:
.incbin "baserom.dol", 0x21F6A0, 0x30
.global lbl_802226D0
lbl_802226D0:
.incbin "baserom.dol", 0x21F6D0, 0x10
.global lbl_802226E0
lbl_802226E0:
.incbin "baserom.dol", 0x21F6E0, 0x10
.global lbl_802226F0
lbl_802226F0:
.incbin "baserom.dol", 0x21F6F0, 0x18
.global lbl_80222708
lbl_80222708:
.incbin "baserom.dol", 0x21F708, 0x40
.global lbl_80222748
lbl_80222748:
.incbin "baserom.dol", 0x21F748, 0x10
.global __ptmf_null
__ptmf_null:
.incbin "baserom.dol", 0x21F758, 0x10
.global __constants
__constants:
.incbin "baserom.dol", 0x21F768, 0x18
.global bit_values
bit_values:
.incbin "baserom.dol", 0x21F780, 0x48
.global digit_values
digit_values:
.incbin "baserom.dol", 0x21F7C8, 0x40
.global __ctype_map
__ctype_map:
.incbin "baserom.dol", 0x21F808, 0x100
.global __lower_map
__lower_map:
.incbin "baserom.dol", 0x21F908, 0x100
.global __upper_map
__upper_map:
.incbin "baserom.dol", 0x21FA08, 0x100
.global $$2stringBase0
$$2stringBase0:
.incbin "baserom.dol", 0x21FB08, 0x8
.global $$2stringBase0_1
$$2stringBase0_1:
.incbin "baserom.dol", 0x21FB10, 0x10
.global lbl_80222B20
lbl_80222B20:
.incbin "baserom.dol", 0x21FB20, 0x58
.global bp
bp:
.incbin "baserom.dol", 0x21FB78, 0x10
.global dp_h
dp_h:
.incbin "baserom.dol", 0x21FB88, 0x10
.global dp_l
dp_l:
.incbin "baserom.dol", 0x21FB98, 0x10
.global atanhi
atanhi:
.incbin "baserom.dol", 0x21FBA8, 0x20
.global atanlo
atanlo:
.incbin "baserom.dol", 0x21FBC8, 0x20
.global aT
aT:
.incbin "baserom.dol", 0x21FBE8, 0x58
.global atan_coeff$96
atan_coeff$96:
.incbin "baserom.dol", 0x21FC40, 0x1C
.global onep_one_over_xisqr_hi$97
onep_one_over_xisqr_hi$97:
.incbin "baserom.dol", 0x21FC5C, 0x18
.global onep_one_over_xisqr_lo$98
onep_one_over_xisqr_lo$98:
.incbin "baserom.dol", 0x21FC74, 0x18
.global atan_xi_hi$99
atan_xi_hi$99:
.incbin "baserom.dol", 0x21FC8C, 0x1C
.global atan_xi_lo$100
atan_xi_lo$100:
.incbin "baserom.dol", 0x21FCA8, 0x1C
.global one_over_xi_hi$101
one_over_xi_hi$101:
.incbin "baserom.dol", 0x21FCC4, 0x18
.global one_over_xi_lo$102
one_over_xi_lo$102:
.incbin "baserom.dol", 0x21FCDC, 0x1C
.global tmp_float
tmp_float:
.incbin "baserom.dol", 0x21FCF8, 0x10
.global __sincos_on_quadrant
__sincos_on_quadrant:
.incbin "baserom.dol", 0x21FD08, 0x20
.global __sincos_poly
__sincos_poly:
.incbin "baserom.dol", 0x21FD28, 0x28
.global lbl_80222D50
lbl_80222D50:
.incbin "baserom.dol", 0x21FD50, 0x20
.global gTRKMemMap
gTRKMemMap:
.incbin "baserom.dol", 0x21FD70, 0x10
.global lbl_80222D80
lbl_80222D80:
.incbin "baserom.dol", 0x21FD80, 0x14
.global lbl_80222D94
lbl_80222D94:
.incbin "baserom.dol", 0x21FD94, 0x14
.global lbl_80222DA8
lbl_80222DA8:
.incbin "baserom.dol", 0x21FDA8, 0x18

1676
asm/sbss.s Normal file

File diff suppressed because it is too large Load Diff

16481
asm/sdata.s Normal file

File diff suppressed because it is too large Load Diff

11345
asm/sdata2.s Normal file

File diff suppressed because it is too large Load Diff

607791
asm/text.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="$DEVKITPPC/bin/powerpc-eabi-objdump -D -bbinary -EB -mpowerpc -M gekko"
OPTIONS="--start-address=$(($1)) --stop-address=$(($1 + $2))"
$OBJDUMP $OPTIONS baserom.dol > baserom.dump
$OBJDUMP $OPTIONS build/pikmin2.us.demo/main.dol > main.dump
diff -u --color=always baserom.dump main.dump

686
doldisasm.py Normal file
View File

@ -0,0 +1,686 @@
#!/usr/bin/env python
#
# GameCube .dol file disassembler
# Usage: doldisasm.py DOL_FILE > assembly_file.s
#
from capstone import *
from capstone.ppc import *
import re
import sys
substitutions = (
('<', '$$0'),
('>', '$$1'),
('@', '$$2'),
('\\', '$$3'),
(',', '$$4'),
('-', '$$5')
)
def format(symbol):
for sub in substitutions:
symbol = symbol.replace(sub[0], sub[1])
return symbol
def decodeformat(symbol):
for sub in substitutions:
symbol = symbol.replace(sub[1], sub[0])
return symbol
r13_addr = None
r2_addr = None
labels = set()
labelNames = {}
#argshift = 1
#if sys.argv[argshift] == '-m':
with open('pik1excised.map', 'r') as mapfile:
for line in mapfile:
match = re.match(' [0-9a-f]{8} [0-9a-f]{6} ([0-9a-f]{8}) [ 0-9][0-9] ([^ 0-9.][^ ]*)', line)
if match:
addr = int(match.group(1), 16)
name = format(match.group(2))
labels.add(addr)
labelNames[addr] = name
#argshift += 2
argshift = 1 #This is just to auto-find the mapfile. I prefer to drag and drop the dol onto this script.
with open(sys.argv[argshift], 'rb') as dolfile:
filecontent = bytearray(dolfile.read())
def read_u8(offset):
return filecontent[offset]
def read_u32(offset):
return (filecontent[offset + 0] << 24) | (filecontent[offset + 1] << 16) | (filecontent[offset + 2] << 8) | filecontent[offset + 3]
def sign_extend_16(value):
if value > 0 and (value & 0x8000):
value -= 0x10000
return value
def sign_extend_12(value):
if value > 0 and (value & 0x800):
value -= 0x1000
return value
textOffsets = []
textAddresses = []
textSizes = []
dataOffsets = []
dataAddresses = []
dataSizes = []
for i in range(0, 7):
textOffsets.append(read_u32(0x00 + 4 * i))
textAddresses.append(read_u32(0x48 + 4 * i))
textSizes.append(read_u32(0x90 + 4 * i))
for i in range(0, 11):
dataOffsets.append(read_u32(0x1C + 4 * i))
dataAddresses.append(read_u32(0x64 + 4 * i))
dataSizes.append(read_u32(0xAC + 4 * i))
bssAddress = read_u32(0xD8)
bssSize = read_u32(0xDC)
entryPoint = read_u32(0xE0)
origStdout = sys.stdout
with open('asm\disasm.s', 'w') as out:
sys.stdout = out
print('/*')
print('Code sections:')
for i in range(0, 7):
if textOffsets[i] != 0 and textAddresses[i] != 0 and textSizes[i] != 0:
print('\t.text%i:\t0x%08X\t0x%08X\t0x%08X' % (i, textOffsets[i], textAddresses[i], textAddresses[i] + textSizes[i]))
print('Data sections:')
for i in range(0, 11):
if dataOffsets[i] != 0 and dataAddresses[i] != 0 and dataSizes[i] != 0:
print('\t.data%i:\t0x%08X\t0x%08X\t0x%08X' % (i, dataOffsets[i], dataAddresses[i], dataAddresses[i] + dataSizes[i]))
print('BSS section:')
print('\t.bss:\t0x%08X\t0x%08X\t0x%08X' % (0, bssAddress, bssAddress + bssSize))
print('Entry Point: 0x%08X' % entryPoint)
print('*/')
# Add entry point
labels.add(entryPoint)
labelNames[entryPoint] = '__start'
def addr_to_label(addr):
if addr in labels:
if addr in labelNames:
return labelNames[addr]
else:
return "lbl_%08X" % addr
else:
return "0x%08X" % addr
def add_label(addr, name):
labels.add(addr)
if name != None and not addr in labelNames:
labelNames[addr] = name
def is_label_candidate(addr):
for i in range(0, 7):
if addr >= textAddresses[i] and addr < textAddresses[i] + textSizes[i] and (addr & 3) == 0:
return True
for i in range(0, 11):
if addr >= dataAddresses[i] and addr < dataAddresses[i] + dataSizes[i]:
return True
if addr >= bssAddress and addr < bssAddress + bssSize:
return True
return False
# TODO: find all of them
loadStoreInsns = {
PPC_INS_LWZ,
PPC_INS_LMW,
PPC_INS_LHA,
PPC_INS_LHAU,
PPC_INS_LHZ,
PPC_INS_LHZU,
PPC_INS_LBZ,
PPC_INS_LBZU,
PPC_INS_LFD,
PPC_INS_LFDU,
PPC_INS_LFS,
PPC_INS_LFSU,
PPC_INS_STW,
PPC_INS_STWU,
PPC_INS_STMW,
PPC_INS_STH,
PPC_INS_STHU,
PPC_INS_STB,
PPC_INS_STBU,
PPC_INS_STFS,
PPC_INS_STFSU,
PPC_INS_STFD,
PPC_INS_STDU,
}
# Returns true if the instruction is a load or store with the given register as a base
def is_load_store_reg_offset(insn, reg):
return insn.id in loadStoreInsns and (reg == None or insn.operands[1].mem.base == reg)
cs = Cs(CS_ARCH_PPC, CS_MODE_32 | CS_MODE_BIG_ENDIAN)
cs.detail = True
cs.imm_unsigned = False
blacklistedInsns = {
# Unsupported instructions
PPC_INS_VMSUMSHM, PPC_INS_VMHADDSHS, PPC_INS_XXSLDWI, PPC_INS_VSEL,
PPC_INS_XVSUBSP, PPC_INS_XXSEL, PPC_INS_XVMULSP, PPC_INS_XVDIVSP,
PPC_INS_VADDUHM, PPC_INS_XXPERMDI, PPC_INS_XVMADDASP, PPC_INS_XVMADDMSP,
PPC_INS_XVCMPGTSP, PPC_INS_XXMRGHD, PPC_INS_XSMSUBMDP, PPC_INS_XSTDIVDP,
PPC_INS_XVADDSP, PPC_INS_XVCMPEQSP, PPC_INS_XVMSUBASP, PPC_INS_XVCMPGESP,
PPC_INS_VMRGHB, PPC_INS_XXSPLTW,
# Found during disassemble attempts
PPC_INS_XVCMPEQDP, PPC_INS_XVCMPEQDP, PPC_INS_XVMADDADP, PPC_INS_XVMADDMDP,
PPC_INS_VPKUHUM, PPC_INS_XSMADDMDP, PPC_INS_XSMADDADP, PPC_INS_XSCMPUDP,
PPC_INS_XSMSUBADP, PPC_INS_XSCMPODP, PPC_INS_XSCMPUDP, PPC_INS_XVMSUBMSP,
PPC_INS_XVMSUBMDP, PPC_INS_XVCMPGEDP, PPC_INS_XVMSUBADP, PPC_INS_XVCMPGTDP,
PPC_INS_XVMSUBADP, PPC_INS_XVNMSUBMSP,
# Instructions that Capstone gets wrong
PPC_INS_MFESR, PPC_INS_MFDEAR, PPC_INS_MTESR, PPC_INS_MTDEAR, PPC_INS_MFICCR, PPC_INS_MFASR,
# Sus
PPC_INS_ATTN
}
# Calls callback for every instruction in the specified code section
def disasm_iter(offset, address, size, callback):
if size == 0:
return
start = address
end = address + size
while address < end:
code = filecontent[offset + (address-start) : offset + size]
for insn in cs.disasm(code, address):
address = insn.address
if insn.id in blacklistedInsns:
callback(address, offset + address - start, None, insn.bytes)
else:
callback(address, offset + address - start, insn, insn.bytes)
address += 4
if address < end:
o = offset + address - start
callback(address, offset + address - start, None, filecontent[o : o + 4])
address += 4
lisInsns = {} # register : insn
splitDataLoads = {} # address of load insn (both high and low) : data
linkedInsns = {} # addr of lis insn : ori/addi insn
# Returns true if the instruction writes to the specified register
def reg_modified(insn, reg):
if insn.op[0].type == PPC_OP_REG and insn.op[0].reg == reg:
return True
else:
return False
# Computes the combined value from a lis, addi/ori instruction pairr
def combine_split_load_value(hiLoadInsn, loLoadInsn):
assert hiLoadInsn.id == PPC_INS_LIS
#assert loLoadInsn.id in {PPC_INS_ADDI, PPC_INS_ORI}
#assert loLoadInsn.operands[1].reg == hiLoadInsn.operands[0].reg
# hiLoadInsn must be "lis rX, hiPart"
value = hiLoadInsn.operands[1].imm << 16
# loLoadInsn must be "addi rY, rX, loPart"
if loLoadInsn.id == PPC_INS_ORI:
value |= loLoadInsn.operands[2].imm
elif loLoadInsn.id == PPC_INS_ADDI:
value += sign_extend_16(loLoadInsn.operands[2].imm)
elif is_load_store_reg_offset(loLoadInsn, hiLoadInsn.operands[0].reg):
value += sign_extend_16(loLoadInsn.operands[1].mem.disp)
else:
assert False
return value
def is_store_insn(insn):
# TODO: all store instructions
return insn.id in {PPC_INS_STW}
# Get labels
def get_label_callback(address, offset, insn, bytes):
global r13_addr
global r2_addr
if insn == None:
return
#print("%s %s" % (insn.mnemonic, insn.op_str))
# if branch instruction
if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BC, PPC_INS_BDZ, PPC_INS_BDNZ}:
lisInsns.clear()
for op in insn.operands:
if op.type == PPC_OP_IMM:
#print("label 0x%08X" % op.imm)
labels.add(op.imm)
if insn.id == PPC_INS_BL:
#labelNames[op.imm] = 'func_%08X' % op.imm
add_label(op.imm, 'func_%08X' % op.imm)
# Detect split load (high part)
# this is 'lis rX, hipart'
if insn.id == PPC_INS_LIS:
# Record instruction that loads into register with 'lis'
lisInsns[insn.operands[0].reg] = insn
# Detect split load (low part)
# this is either 'addi/ori rY, rX, lopart' or 'load/store rY, lopart(rX)'
elif (insn.id in {PPC_INS_ADDI, PPC_INS_ORI} and insn.operands[1].reg in lisInsns) \
or (is_load_store_reg_offset(insn, None) and insn.operands[1].mem.base in lisInsns):
hiLoadInsn = lisInsns[insn.operands[1].reg]
# Compute combined value
value = combine_split_load_value(hiLoadInsn, insn)
if is_label_candidate(value):
labels.add(value)
# Record linked instruction
linkedInsns[hiLoadInsn.address] = insn
splitDataLoads[hiLoadInsn.address] = value
splitDataLoads[insn.address] = value
lisInsns.pop(insn.operands[1].reg, None)
# detect r2/r13 initialization
if insn.id == PPC_INS_ORI and insn.operands[0].reg == insn.operands[1].reg:
if r2_addr == None and insn.operands[0].reg == PPC_REG_R2:
r2_addr = value
#print('# DEBUG: set r2 to 0x%08X' % value)
elif r13_addr == None and insn.operands[0].reg == PPC_REG_R13:
r13_addr = value
#print('# DEBUG: set r13 to 0x%08X' % value)
# Remove record if register is overwritten
elif (not is_store_insn(insn)) and len(insn.operands) >= 1 and insn.operands[0].type == PPC_OP_REG:
lisInsns.pop(insn.operands[0].reg, None)
# Handle r13 offset values
if r13_addr != None:
if insn.id == PPC_INS_ADDI and insn.operands[1].value.reg == PPC_REG_R13: # r13 offset
value = r13_addr + sign_extend_16(insn.operands[2].imm)
if is_label_candidate(value):
labels.add(value)
#labelNames[value] = 'r13_%08X' % value
if is_load_store_reg_offset(insn, PPC_REG_R13):
value = r13_addr + sign_extend_16(insn.operands[1].mem.disp)
if is_label_candidate(value):
labels.add(value)
#labelNames[value] = 'r13_%08X' % value
# Handle r2 offset values
if r2_addr != None:
if insn.id == PPC_INS_ADDI and insn.operands[1].value.reg == PPC_REG_R2: # r13 offset
value = r2_addr + sign_extend_16(insn.operands[2].imm)
if is_label_candidate(value):
labels.add(value)
#labelNames[value] = 'r2_%08X' % value
if is_load_store_reg_offset(insn, PPC_REG_R2):
value = r2_addr + sign_extend_16(insn.operands[1].mem.disp)
if is_label_candidate(value):
labels.add(value)
#labelNames[value] = 'r2_%08X' % value
for i in range(0, 7):
if textSizes[i] != 0:
disasm_iter(textOffsets[i], textAddresses[i], textSizes[i], get_label_callback)
# Write macros
print('# PowerPC Register Constants')
for i in range(0, 32):
print(".set r%i, %i" % (i, i))
for i in range(0, 32):
print(".set f%i, %i" % (i, i))
for i in range(0, 8):
print(".set qr%i, %i" % (i, i))
if r13_addr != None:
print('# Small Data Area (read/write) Base')
print(".set _SDA_BASE_, 0x%08X" % r13_addr)
if r2_addr != None:
print('# Small Data Area (read only) Base')
print(".set _SDA2_BASE_, 0x%08X" % r2_addr)
print('')
# Converts the instruction to a string, fixing various issues with Capstone
def insn_to_text(insn, raw):
# Probably data, not a real instruction
if insn.id == PPC_INS_BDNZ and (insn.bytes[0] & 1):
return None
if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BDZ, PPC_INS_BDNZ}:
return "%s %s" % (insn.mnemonic, addr_to_label(insn.operands[0].imm))
elif insn.id == PPC_INS_BC:
branchPred = '+' if (insn.bytes[1] & 0x20) else ''
if insn.operands[0].type == PPC_OP_IMM:
return "%s%s %s" % (insn.mnemonic, branchPred, addr_to_label(insn.operands[0].imm))
elif insn.operands[1].type == PPC_OP_IMM:
return "%s%s %s, %s" % (insn.mnemonic, branchPred, insn.reg_name(insn.operands[0].value.reg), addr_to_label(insn.operands[1].imm))
# Handle split loads (high part)
if insn.address in splitDataLoads and insn.id == PPC_INS_LIS:
loLoadInsn = linkedInsns[insn.address]
#assert loLoadInsn.id in {PPC_INS_ADDI, PPC_INS_ORI}
value = splitDataLoads[insn.address]
suffix = 'h' if loLoadInsn.id == PPC_INS_ORI else 'ha'
return '%s %s, %s@%s' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), addr_to_label(value), suffix)
# Handle split loads (low part)
elif insn.address in splitDataLoads and insn.id in {PPC_INS_ADDI, PPC_INS_ORI}:
value = splitDataLoads[insn.address]
return '%s %s, %s, %s@l' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].reg), addr_to_label(value))
elif insn.address in splitDataLoads and is_load_store_reg_offset(insn, None):
value = splitDataLoads[insn.address]
return '%s %s, %s@l(%s)' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), addr_to_label(value), insn.reg_name(insn.operands[1].mem.base))
# r13 offset loads
if r13_addr != None:
if insn.id == PPC_INS_ADDI and insn.operands[1].reg == PPC_REG_R13:
value = r13_addr + sign_extend_16(insn.operands[2].imm)
if value in labels:
return "%s %s, %s, %s@sda21" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].reg), addr_to_label(value))
if is_load_store_reg_offset(insn, PPC_REG_R13):
value = r13_addr + sign_extend_16(insn.operands[1].mem.disp)
if value in labels:
return "%s %s, %s@sda21(%s)" % (insn.mnemonic, insn.reg_name(insn.operands[0].value.reg), addr_to_label(value), insn.reg_name(insn.operands[1].mem.base))
# r2 offset loads
if r2_addr != None:
if insn.id == PPC_INS_ADDI and insn.operands[1].reg == PPC_REG_R2:
value = r2_addr + sign_extend_16(insn.operands[2].imm)
if value in labels:
return "%s %s, %s, %s@sda21" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].reg), addr_to_label(value))
if is_load_store_reg_offset(insn, PPC_REG_R2):
value = r2_addr + sign_extend_16(insn.operands[1].mem.disp)
if value in labels:
return "%s %s, %s@sda21(%s)" % (insn.mnemonic, insn.reg_name(insn.operands[0].value.reg), addr_to_label(value), insn.reg_name(insn.operands[1].mem.base))
# Sign-extend immediate values because Capstone is an idiot and doesn't do that automatically
if insn.id in {PPC_INS_ADDI, PPC_INS_ADDIC, PPC_INS_SUBFIC, PPC_INS_MULLI} and (insn.operands[2].imm & 0x8000):
return "%s %s, %s, %i" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].value.reg), insn.operands[2].imm - 0x10000)
elif (insn.id == PPC_INS_LI or insn.id == PPC_INS_CMPWI) and (insn.operands[1].imm & 0x8000):
return "%s %s, %i" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.operands[1].imm - 0x10000)
# cntlz -> cntlzw
elif insn.id == PPC_INS_CNTLZW:
return "cntlzw %s" % insn.op_str
elif insn.id == PPC_INS_MTICCR:
return 'mtictc %s' % insn.op_str
# Dunno why GNU assembler doesn't accept this
elif insn.id == PPC_INS_LMW and insn.operands[0].reg == PPC_REG_R0:
return '.4byte 0x%08X /* illegal %s %s */' % (raw, insn.mnemonic, insn.op_str)
return '%s %s' % (insn.mnemonic, insn.op_str)
def disasm_ps(inst):
RA = ((inst >> 16) & 0x1f)
RB = ((inst >> 11) & 0x1f)
FA = ((inst >> 16) & 0x1f)
FB = ((inst >> 11) & 0x1f)
FC = ((inst >> 6) & 0x1f)
FD = ((inst >> 21) & 0x1f)
FS = ((inst >> 21) & 0x1f)
IX = ((inst >> 7) & 0x7)
WX = ((inst >> 10) & 0x1)
opcode = (inst >> 1) & 0x1F
if opcode == 6: # doesn't seem to be used
mnemonic = 'psq_lux' if inst & 0x40 else 'psq_lx'
return '%s f%i, r%i, r%i, %i, qr%i' % (mnemonic, FD, RA, RB, WX, IX)
if opcode == 7:
mnemonic = 'psq_stux' if inst & 0x40 else 'psq_stx'
return '%s f%i, r%i, r%i, %i, qr%i' % (mnemonic, FS, RA, RB, WX, IX)
if opcode == 18:
return 'ps_div f%i, f%i, f%i' % (FD, FA, FB)
if opcode == 20:
return 'ps_sub f%i, f%i, f%i' % (FD, FA, FB)
if opcode == 21:
return 'ps_add f%i, f%i, f%i' % (FD, FA, FB)
if opcode == 23:
return 'ps_sel f%i, f%i, f%i' % (FD, FA, FC)
if opcode == 24:
return 'ps_res f%i, f%i' % (FD, FB)
if opcode == 25:
return 'ps_mul f%i, f%i, f%i' % (FD, FA, FC)
if opcode == 26:
return 'ps_rsqrte f%i, f%i' % (FD, FB)
if opcode == 28:
return 'ps_msub f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
if opcode == 29:
return 'ps_madd f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
if opcode == 30:
return 'ps_nmsub f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
if opcode == 31:
return 'ps_nmadd f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
if opcode == 10:
return 'ps_sum0 f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
if opcode == 11:
return 'ps_sum1 f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
if opcode == 12:
return 'ps_muls0 f%i, f%i, f%i' % (FD, FA, FC)
if opcode == 13:
return 'ps_muls1 f%i, f%i, f%i' % (FD, FA, FC)
if opcode == 14:
return 'ps_madds0 f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
if opcode == 15:
return 'ps_madds1 f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
opcode = (inst >> 1) & 0x3FF
if opcode == 40:
return 'ps_neg f%i, f%i' % (FD, FB)
if opcode == 72:
return 'ps_mr f%i, f%i' % (FD, FB)
if opcode == 136:
return 'ps_nabs f%i, f%i' % (FD, FB)
if opcode == 264:
return 'ps_abs f%i, f%i' % (FD, FB)
if opcode in {0, 32, 64, 96}:
mnemonics = ['ps_cmpu0', 'ps_cmpo0', 'ps_cmpu1', 'ps_cmpo1']
mnemonic = mnemonics[(inst >> 6) & 3]
i = (inst & 0x03800000) >> 23
return '%s cr%i, f%i, f%i' % (mnemonic, i, FA, FB)
if opcode == 528:
return 'ps_merge00 f%i, f%i, f%i' % (FD, FA, FB)
if opcode == 560:
return 'ps_merge01 f%i, f%i, f%i' % (FD, FA, FB)
if opcode == 592:
return 'ps_merge10 f%i, f%i, f%i' % (FD, FA, FB)
if opcode == 624:
return 'ps_merge11 f%i, f%i, f%i' % (FD, FA, FB)
if opcode == 1014:
if not (inst & 0x03e00000):
if (inst & 1) == 0:
return 'dcbz_l r%i, r%i' % ((inst & 0x001f0000) >> 16, (inst & 0x0000f800) >> 11)
return None
def disasm_ps_mem(inst, idx):
RA = ((inst >> 16) & 0x1f)
RS = ((inst >> 21) & 0x1f)
I = ((inst >> 12) & 0x7)
W = ((inst >> 15) & 0x1)
disp = sign_extend_12(inst & 0xFFF)
if idx == 56:
mnemonic = 'psq_l'
if idx == 57:
mnemonic = 'psq_lu'
if idx == 60:
mnemonic = 'psq_st'
if idx == 61:
mnemonic = 'psq_stu'
return '%s f%i, %i(r%i), %i, qr%i' % (mnemonic, RS, disp, RA, W, I)
def disasm_fcmp(inst):
crd = (inst & 0x03800000) >> 23
a = (inst & 0x001f0000) >> 16
b = (inst & 0x0000f800) >> 11
return 'fcmpo cr%i, f%i, f%i' % (crd, a, b)
def disasm_mspr(inst, mode):
if (inst & 1):
return None
d = (inst & 0x03e00000) >> 21
a = (inst & 0x001f0000) >> 16
b = (inst & 0x0000f800) >>11
spr = (b << 5) + a
if mode:
return 'mtspr 0x%X, r%i' % (spr, d)
else:
return 'mfspr r%i, 0x%X' % (d, spr)
def disasm_mcrxr(inst):
if (inst & 0x007ff801):
return None
crd = (inst & 0x03800000) >> 23
return 'mcrxr cr%i' % crd
# Disassemble code
def disassemble_callback(address, offset, insn, bytes):
# Output label (if any)
if address in labels:
if address in labelNames:
print("\n.global %s" % addr_to_label(address))
print("%s:" % addr_to_label(address))
prefixComment = '/* %08X %08X %02X %02X %02X %02X */' % (address, offset, bytes[0], bytes[1], bytes[2], bytes[3])
asm = None
raw = read_u32(offset)
if insn != None:
asm = insn_to_text(insn, raw)
else: # Capstone couldn't disassemble it
idx = (raw & 0xfc000000) >> 26
idx2 = (raw & 0x000007fe) >> 1
# mtspr
if idx == 31 and idx2 == 467:
asm = disasm_mspr(raw, 1)
# mfspr
elif idx == 31 and idx2 == 339:
asm = disasm_mspr(raw, 0)
# mcrxr
elif idx == 31 and idx2 == 512:
asm = disasm_mcrxr(raw)
# fcmpo
elif idx == 63 and idx2 == 32:
asm = disasm_fcmp(raw)
# Paired singles
elif idx == 4:
asm = disasm_ps(raw)
elif idx in {56, 57, 60, 61}:
asm = disasm_ps_mem(raw, idx)
if asm == None:
asm = '.4byte 0x%08X /* unknown instruction */' % raw
print('%s\t%s' % (prefixComment, asm))
for i in range(0, 7):
if textSizes[i] != 0:
print("\n.section .text%i, \"ax\" # 0x%08X - 0x%08X" % (i, textAddresses[i], textAddresses[i] + textSizes[i]))
disasm_iter(textOffsets[i], textAddresses[i], textSizes[i], disassemble_callback)
# Disassemble data
for i in range(0, 11):
offset = dataOffsets[i]
address = dataAddresses[i]
size = dataSizes[i]
start = address
end = start + size
if size == 0:
continue
print("\n.section .data%i, \"wa\" # 0x%08X - 0x%08X" % (i, start, end))
# Get a sorted list of labels in this data section
sectionLabels = []
for l in labels:
if l >= start and l < end:
sectionLabels.append(l)
sectionLabels.sort()
# Split incbins by labels
j = 0
while address < end:
if j < len(sectionLabels):
incbinSize = sectionLabels[j] - address
if incbinSize != 0:
print("\t.incbin \"baserom.dol\", 0x%X, 0x%X" % (offset, incbinSize))
l = addr_to_label(sectionLabels[j])
print(".global %s\n%s:" % (l, l))
j += 1
else:
incbinSize = end - address
if incbinSize != 0:
print("\t.incbin \"baserom.dol\", 0x%X, 0x%X" % (offset, incbinSize))
offset += incbinSize
address += incbinSize
# Remove labels to avoid duplicates in case of overlap with other sections
for l in sectionLabels:
labels.remove(l)
# Disassemble bss
start = bssAddress
end = bssAddress + bssSize
address = bssAddress
print("\n.section .bss, \"wa\" # 0x%08X - 0x%08X" % (start, end))
# Get a sorted list of labels in this bss section
sectionLabels = []
for l in labels:
if l >= start and l < end:
sectionLabels.append(l)
sectionLabels.sort()
# Split incbins by labels
j = 0
while address < end:
if j < len(sectionLabels):
gapSize = sectionLabels[j] - address
if gapSize != 0:
print("\t.skip 0x%X" % gapSize)
l = addr_to_label(sectionLabels[j])
print(".global %s\n%s:" % (l, l))
j += 1
else:
gapSize = end - address
if gapSize != 0:
print("\t.skip 0x%X" % gapSize)
address += gapSize
# Output linker script
origStdout = sys.stdout
with open('ldscript.ld', 'w') as out:
sys.stdout = out
print("ENTRY(__start)")
if r13_addr != None:
print("_SDA_BASE_ = 0x%08X;" % r13_addr)
if r2_addr != None:
print("_SDA2_BASE_ = 0x%08X;" % r2_addr)
print("PHDRS\n{")
for i in range(0, 7):
if textSizes[i] != 0:
print(" text%i PT_LOAD;" % i)
for i in range(0, 11):
if dataSizes[i] != 0:
print(" data%i PT_LOAD;" % i)
print(" bss PT_LOAD;")
print("}")
print("SECTIONS\n{")
for i in range(0, 7):
if textSizes[i] != 0:
print(" .text%i 0x%08X : { *(.text%i) } : text%i" % (i, textAddresses[i], i, i))
for i in range(0, 11):
if dataSizes[i] != 0:
print(" .data%i 0x%08X : { *(.data%i) } : data%i" % (i, dataAddresses[i], i, i))
print(" .bss 0x%08X (NOLOAD) : { *(.bss) } : bss" % bssAddress)
print("}")
sys.stdout = origStdout
# Output linker script (Metrowerks)
origStdout = sys.stdout
with open('ldscript.lcf', 'w') as out:
sys.stdout = out
if r13_addr != None:
print("_SDA_BASE_ = 0x%08X;" % r13_addr)
if r2_addr != None:
print("_SDA2_BASE_ = 0x%08X;" % r2_addr)
print("SECTIONS\n{")
for i in range(0, 7):
if textSizes[i] != 0:
print(" .text%i BIND(0x%08X) : { *(.text%i) }" % (i, textAddresses[i], i))
for i in range(0, 11):
if dataSizes[i] != 0:
print(" .data%i BIND(0x%08X) : { *(.data%i) }" % (i, dataAddresses[i], i))
print(" .bss BIND(0x%08X) : { *(.bss) }" % bssAddress)
print("}")
sys.stdout = origStdout

95
include/macros.inc Normal file
View File

@ -0,0 +1,95 @@
/*
Code sections:
.init: 0x00000100 0x80003100 0x800054C0
.text: 0x000024C0 0x80005560 0x80221F60
Data sections:
extab: 0x0021EEC0 0x800054C0 0x80005500
extabindex: 0x0021EF00 0x80005500 0x80005560
.ctors: 0x0021EF60 0x80221F60 0x80221FC0
.dtors: 0x0021EFC0 0x80221FC0 0x80221FE0
.rodata: 0x0021EFE0 0x80221FE0 0x80222DC0
.data: 0x0021FDC0 0x80222DC0 0x802E9640
.sdata: 0x002E6640 0x803DCD20 0x803E7820
.sdata2: 0x002F1140 0x803E8200 0x803EC840
BSS section:
.bss: 0x00000000 0x802E9640 0x803E81E5
.sbss:
Entry Point: 0x80003100
*/
# PowerPC Register Constants
.set r0, 0
.set r1, 1
.set r2, 2
.set r3, 3
.set r4, 4
.set r5, 5
.set r6, 6
.set r7, 7
.set r8, 8
.set r9, 9
.set r10, 10
.set r11, 11
.set r12, 12
.set r13, 13
.set r14, 14
.set r15, 15
.set r16, 16
.set r17, 17
.set r18, 18
.set r19, 19
.set r20, 20
.set r21, 21
.set r22, 22
.set r23, 23
.set r24, 24
.set r25, 25
.set r26, 26
.set r27, 27
.set r28, 28
.set r29, 29
.set r30, 30
.set r31, 31
.set f0, 0
.set f1, 1
.set f2, 2
.set f3, 3
.set f4, 4
.set f5, 5
.set f6, 6
.set f7, 7
.set f8, 8
.set f9, 9
.set f10, 10
.set f11, 11
.set f12, 12
.set f13, 13
.set f14, 14
.set f15, 15
.set f16, 16
.set f17, 17
.set f18, 18
.set f19, 19
.set f20, 20
.set f21, 21
.set f22, 22
.set f23, 23
.set f24, 24
.set f25, 25
.set f26, 26
.set f27, 27
.set f28, 28
.set f29, 29
.set f30, 30
.set f31, 31
.set qr0, 0
.set qr1, 1
.set qr2, 2
.set qr3, 3
.set qr4, 4
.set qr5, 5
.set qr6, 6
.set qr7, 7
# Small Data Area (read/write) Base
.set _SDA_BASE_, 0x803E4D20
# Small Data Area (read only) Base
.set _SDA2_BASE_, 0x803F0200

29
ldscript.lcf Normal file
View File

@ -0,0 +1,29 @@
_SDA_BASE_ = 0x803E4D20;
_SDA2_BASE_ = 0x803F0200;
MEMORY {
text : origin = 0x80003100
}
SECTIONS {
GROUP: {
.init ALIGN(0x20) : {}
._extab ALIGN(0x20) : {}
._exidx ALIGN(0x20) : {}
.text ALIGN(0x20) : {}
.ctors ALIGN(0x20) : {}
.dtors ALIGN(0x20) : {}
.rodata ALIGN(0x20) : {}
.data ALIGN(0x20) : {}
.bss ALIGN(0x20) : {}
.sdata ALIGN(0x20) : {}
.sbss ALIGN(0x20) : {}
.sdata2 ALIGN(0x20): {}
.sbss2 ALIGN(0x20) : {}
.stack ALIGN(0x100) : {}
} > text
_stack_addr = (_f_sbss2 + SIZEOF(.sbss2) + 65536 + 0x7) & ~0x7;
_stack_end = _f_sbss2 + SIZEOF(.sbss2);
_db_stack_addr = (_stack_addr + 0x2000);
_db_stack_end = _stack_addr;
__ArenaLo = (_db_stack_addr + 0x1f) & ~0x1f;
__ArenaHi = 0x81700000;
}

40
obj_files.mk Normal file
View File

@ -0,0 +1,40 @@
# Linker order for every file, passed to the Metrowerks linker.
INIT_O_FILES := \
$(BUILD_DIR)/asm/init.o \
EXTAB_O_FILES := \
$(BUILD_DIR)/asm/extab.o \
EXTABINDEX_O_FILES := \
$(BUILD_DIR)/asm/exidx.o \
TEXT_O_FILES := \
$(BUILD_DIR)/asm/text.o \
CTORS_O_FILES := \
$(BUILD_DIR)/asm/ctors.o \
DTORS_O_FILES := \
$(BUILD_DIR)/asm/dtors.o \
RODATA_O_FILES := \
$(BUILD_DIR)/asm/rodata.o \
DATA_O_FILES := \
$(BUILD_DIR)/asm/data.o \
BSS_O_FILES := \
$(BUILD_DIR)/asm/bss.o \
SDATA_O_FILES := \
$(BUILD_DIR)/asm/sdata.o \
SBSS_O_FILES := \
$(BUILD_DIR)/asm/sbss.o \
SDATA2_O_FILES := \
$(BUILD_DIR)/asm/sdata2.o \
SBSS2_O_FILES := \
# $(BUILD_DIR)/asm/sbss2.o \

206
ptr_checker.py Normal file
View File

@ -0,0 +1,206 @@
#!/usr/bin/env python3
# ====================================
# Ptr checker v0.0.9 (by RevoSucks)
# ====================================
# WARNING, seriously, get a puke bucket. This is just a file i am writing for my own sole use.
# If you want to use it, I have some advice for you: D.O.N.T.
# It stands for dooooooonnnnnn't use it. Ever.
# ------------------
# Imports
# ------------------
import os, fnmatch, math
# ------------------
# Defines
# ------------------
# Path to baserom.dol
baserom_path = "baserom.dol"
map_path = "build/pikmin.usa.1/pikmin1.map"
# ------------------
# Methods
# ------------------
# Return a list of searchable '.s' files in a given folder recursively.
def findExt(folder):
matches = []
for root, dirnames, filenames in os.walk(folder):
for filename in filenames:
if filename.endswith('.s'):
matches.append(os.path.join(root, filename))
#print(matches)
return matches
# Name is passed in as the address initially. If we cannot find it, return the string of the address.
def getMatchingTextSymbolFromAddress(name):
# Strip the 0x from the start.
#print("DEBUG: ", str(hex(name))[2:].zfill(8))
result = str(hex(name))[2:].zfill(8)
matches = [result, '(entry of ']
# First open the .map file.
map = open(map_path, 'r')
Lines = map.readlines()
# Parse over the map until a match is found.
for line in Lines:
# Does the line have the 2 expected strings?
if all(x in line for x in matches):
# We found the line.
#print("Match found! Line: ", line)
# Split the name out and return it.
return line.split(' ')[5]
# We're done, close it.
map.close()
return hex(name)
# Build a list of the pointers and scan each word.
def dumpVirtualTable(line):
# Init the table.
table = ""
# Open baserom.dol for processing.
f = open(baserom_path, 'rb')
address = line.split(' ')[2][:-1]
size = line.split(' ')[3]
#print("ADDR: ", address)
#print("SIZE: ", size)
f.seek(int(address, 16))
entries = int(size, 16) / 4
while entries != 0:
entry = int.from_bytes(f.read(4), byteorder='big')
if entry == 0:
str_to_print = "0"
elif (entry & 0xFF000000) == 0x80000000:
str_to_print = str(getMatchingTextSymbolFromAddress(entry))
else:
str_to_print = str(hex(entry))
table = table + " .4byte " + str_to_print + "\n"
entries = entries - 1
return table
def isBaseromEntrySus(line):
with open(baserom_path, 'rb') as f: # context management to close file for us
address = line.split(' ')[2][:-1]
size = line.split(' ')[3]
lower_four = 4 * math.floor(int(size, 16)/4) # Floor the size to the lower multiple of 4 if needed.
entries = int(size, 16) / 4
f.seek(int(address, 16))
while entries > 0:
entry = int.from_bytes(f.read(4), byteorder='big', signed=False)
if (entry & 0xFF000000) == 0x80000000 and entry != 0x80000000:
print("Suspicious Ptr: ", str(hex(entry)))
return True
entries -= 1
return False
# Same as isBaseromEntrySus but check for rodata str. Man this file is a mess.
def isBaseromEntrySus_ShouldDump(line):
with open(baserom_path, 'rb') as f: # context management to close file for us
address = line.split(' ')[2][:-1]
size = line.split(' ')[3]
lower_four = 4 * math.floor(int(size, 16)/4) # Floor the size to the lower multiple of 4 if needed.
entries = int(size, 16) / 4
f.seek(int(address, 16))
while entries > 0:
entry = int.from_bytes(f.read(4), byteorder='big', signed=False)
if (entry & 0xFF000000) == 0x80000000 and entry != 0x80000000:
text_sym = getMatchingTextSymbolFromAddress(entry)
if text_sym != str(hex(entry)):
if text_sym[:2] == "$$":
return True
entries -= 1
return False
def isBaseromEntryPurePtrTable(line):
table = ""
with open(baserom_path, 'rb') as f: # context management to close file for us
address = line.split(' ')[2][:-1]
size = line.split(' ')[3]
lower_four = 4 * math.floor(int(size, 16)/4) # Floor the size to the lower multiple of 4 if needed.
entries = int(size, 16) / 4
f.seek(int(address, 16))
# If any values are neither 0 nor a
while entries > 0:
entry = int.from_bytes(f.read(4), byteorder='big', signed=False)
if entry == 0:
str_to_print = "0"
elif (entry & 0xFF000000) == 0x80000000:
str_to_print = getMatchingTextSymbolFromAddress(entry)
print("DEBUG: ", str_to_print)
elif entry == 0xFFFFFFFF:
str_to_print = "-1"
else:
return line # Not a pure pure table. Keep the entry.
if str_to_print[:2] == "0x":
return line # Not a pure pure table. Keep the entry.
table = table + " .4byte " + str_to_print + "\n"
entries = entries - 1
return table
def dumpActorTable(line):
table = ""
with open(baserom_path, 'rb') as f: # context management to close file for us
address = line.split(' ')[2][:-1]
size = line.split(' ')[3]
lower_four = 4 * math.floor(int(size, 16)/4) # Floor the size to the lower multiple of 4 if needed.
entries = int(size, 16) / 4
f.seek(int(address, 16))
# If any values are neither 0 nor a
while entries > 0:
entry = int.from_bytes(f.read(4), byteorder='big', signed=False)
if entry == 0:
str_to_print = "0"
elif (entry & 0xFF000000) == 0x80000000:
str_to_print = getMatchingTextSymbolFromAddress(entry)
print("DEBUG: ", str_to_print)
elif entry == 0xFFFFFFFF:
str_to_print = "-1"
else:
str_to_print = str(entry)
table = table + " .4byte " + str_to_print + "\n"
entries = entries - 1
return table
# -----------------------------------
# Config
# -----------------------------------
print_baserom_calls = True
# -----------------------------------
# ------------------
# Main code
# ------------------
asm_files = findExt("asm")
#asm_files = ['/c/sms/asm/NPC/NpcInitData.s']
print("Checking all files for possible ptrs...")
# Using readlines()
for i in asm_files:
file1 = open(i, 'r')
Lines = file1.readlines()
file1.close()
file1 = open(i, 'w')
count = 0
line_array = [] #array of current line and last two lines
line_array.append("")
line_array.append("")
line_array.append("")
file_sus = False
for line in Lines:
line_array[0] = line_array[1]
line_array[1] = line_array[2]
line_array[2] = line
# Do something with the line here. Does the line belong to a baserom.dol call for a __vt__?
if "__vt__" in line_array[1] and "baserom.dol" in line:
file1.write(dumpVirtualTable(line))
else:
file1.write(line)
if file_sus == True:
print("Sus: ", i, "Count: ", count, "\n")
file1.close()
print("Done")
#print(os.getcwd())

53
resolveLinkerErrors.py Normal file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env python3
# very experimental python script that goes through a linker error file and resolves any undefined labels while splitting
with open("errors.txt", "r") as f:
lines = f.readlines()
lbls = []
undefcount = 0
for line in lines:
if "undefined" in line:
undefcount = undefcount + 1
splitLine = line.split(" ")
for spl in splitLine:
if spl.startswith("\'lbl_"):
localstr = spl.strip("\n")
localstr = localstr.strip("\'lbl_")
lbls.append(localstr)
with open("asm/MSound/MSoundBGM.s", "r") as asm:
asms = asm.readlines()
prevLine = ""
output = []
for l in asms:
l = l.strip("\n")
for lbl in lbls:
if l.startswith("/* " + lbl):
output.append("lbl_" + lbl + ":\n")
#if prevLine.startswith("func_"):
# prevLine = l
# output.append(l + "\n")
# continue
#else:
# for lbl in lbls:
# lstr = l.strip(":")
# if lstr == lbl:
# output.append(f".global {lbl}\n")
# prevLine = l
# break
output.append(l + "\n")
prevLine = l
with open("output.asm", "w") as w:
for o in output:
w.write(o)

1
sha1/pikmin.usa.1.sha1 Normal file
View File

@ -0,0 +1 @@
02204260B7EFE8742D34572E58BA3DFECD92E4E9 build/pikmin.usa.1/main.dol

3
tools/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Build artifacts
*.exe
elf2dol

506
tools/elf2dol.c Normal file
View File

@ -0,0 +1,506 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/param.h>
#ifndef MAX
//! Get the maximum of two values
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
//! Get the minimum of two values
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry;
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} Elf32_Ehdr;
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_PAD 7
#define EI_NIDENT 16
#define ELFCLASS32 1
#define ELFDATA2MSB 2
#define EV_CURRENT 1
#define ET_EXEC 2
#define EM_PPC 20
typedef struct {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} Elf32_Phdr;
#define PT_LOAD 1
#define PF_R 4
#define PF_W 2
#define PF_X 1
int verbosity = 0;
#if BYTE_ORDER == BIG_ENDIAN
#define swap32(x) (x)
#define swaf16(x) (x)
#else
static inline uint32_t swap32(uint32_t v)
{
return (v >> 24) |
((v >> 8) & 0x0000FF00) |
((v << 8) & 0x00FF0000) |
(v << 24);
}
static inline uint16_t swaf16(uint16_t v)
{
return (v >> 8) | (v << 8);
}
#endif /* BIG_ENDIAN */
typedef struct {
uint32_t text_off[7];
uint32_t data_off[11];
uint32_t text_addr[7];
uint32_t data_addr[11];
uint32_t text_size[7];
uint32_t data_size[11];
uint32_t bss_addr;
uint32_t bss_size;
uint32_t entry;
uint32_t pad[7];
} DOL_hdr;
#define HAVE_BSS 1
#define MAX_TEXT_SEGMENTS 7
#define MAX_DATA_SEGMENTS 11
#define DOL_ALIGNMENT 32
#define DOL_ALIGN(x) (((x) + DOL_ALIGNMENT - 1) & ~(DOL_ALIGNMENT - 1))
typedef struct {
DOL_hdr header;
int text_cnt;
int data_cnt;
uint32_t text_elf_off[7];
uint32_t data_elf_off[11];
uint32_t flags;
FILE *elf;
} DOL_map;
// We need to track 2 PDHR sizes in the event this is for Wii, but for Gamecube
// we only need the first element(s).
uint32_t sdataSizes[2] = {0, 0};
uint32_t sbssSizes[2] = {0, 0};
void usage(const char *name)
{
fprintf(stderr, "Usage: %s [-h] [-v] [--] elf-file dol-file\n", name);
fprintf(stderr, " Convert an ELF file to a DOL file (by segments)\n");
fprintf(stderr, " Options:\n");
fprintf(stderr, " -h Show this help\n");
fprintf(stderr, " -v Be more verbose (twice for even more)\n");
}
#define die(x) { fprintf(stderr, x "\n"); exit(1); }
#define perrordie(x) { perror(x); exit(1); }
void ferrordie(FILE *f, const char *str)
{
if(ferror(f)) {
fprintf(stderr, "Error while ");
perrordie(str);
} else if(feof(f)) {
fprintf(stderr, "EOF while %s\n", str);
exit(1);
} else {
fprintf(stderr, "Unknown error while %s\n", str);
exit(1);
}
}
void add_bss(DOL_map *map, uint32_t paddr, uint32_t memsz)
{
if(map->flags & HAVE_BSS) {
uint32_t start = swap32(map->header.bss_addr);
uint32_t size = swap32(map->header.bss_size);
if ( (start+size) == paddr) {
map->header.bss_size = swap32(size+memsz);
}
} else {
map->header.bss_addr = swap32(paddr);
map->header.bss_size = swap32(memsz);
map->flags |= HAVE_BSS;
}
}
void increment_bss_size(DOL_map *map, uint32_t memsz)
{
// because it can be byte swapped, we need to force the add via a temporary.
uint32_t preAdd = swap32(map->header.bss_size);
preAdd += memsz;
map->header.bss_size = swap32(preAdd);
}
void read_elf_segments(DOL_map *map, const char *elf, uint32_t sdata_pdhr, uint32_t sbss_pdhr, const char *platform)
{
int read, i;
Elf32_Ehdr ehdr;
//int isWii = !(strcmp(platform, "wii")) ? 1 : 0;
int isWii = 1;
if(verbosity >= 2)
fprintf(stderr, "Reading ELF file...\n");
map->elf = fopen(elf, "rb");
if(!map->elf)
perrordie("Could not open ELF file");
read = fread(&ehdr, sizeof(ehdr), 1, map->elf);
if(read != 1)
ferrordie(map->elf, "reading ELF header");
if(memcmp(&ehdr.e_ident[0], "\177ELF", 4))
die("Invalid ELF header");
if(ehdr.e_ident[EI_CLASS] != ELFCLASS32)
die("Invalid ELF class");
if(ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
die("Invalid ELF byte order");
if(ehdr.e_ident[EI_VERSION] != EV_CURRENT)
die("Invalid ELF ident version");
if(swap32(ehdr.e_version) != EV_CURRENT)
die("Invalid ELF version");
if(swaf16(ehdr.e_type) != ET_EXEC)
die("ELF is not an executable");
if(swaf16(ehdr.e_machine) != EM_PPC)
die("Machine is not PowerPC");
if(!swap32(ehdr.e_entry))
die("ELF has no entrypoint");
map->header.entry = ehdr.e_entry;
if(verbosity >= 2)
fprintf(stderr, "Valid ELF header found\n");
uint16_t phnum = swaf16(ehdr.e_phnum);
uint32_t phoff = swap32(ehdr.e_phoff);
Elf32_Phdr *phdrs;
if(!phnum || !phoff)
die("ELF has no program headers");
if(swaf16(ehdr.e_phentsize) != sizeof(Elf32_Phdr))
die("Invalid program header entry size");
phdrs = malloc(phnum * sizeof(Elf32_Phdr));
if(fseek(map->elf, phoff, SEEK_SET) < 0)
ferrordie(map->elf, "reading ELF program headers");
read = fread(phdrs, sizeof(Elf32_Phdr), phnum, map->elf);
if(read != phnum)
ferrordie(map->elf, "reading ELF program headers");
for(i=0; i<phnum; i++) {
if(swap32(phdrs[i].p_type) == PT_LOAD) {
uint32_t offset = swap32(phdrs[i].p_offset);
uint32_t paddr = swap32(phdrs[i].p_paddr);
uint32_t filesz = swap32(phdrs[i].p_filesz);
uint32_t memsz = swap32(phdrs[i].p_memsz);
uint32_t flags = swap32(phdrs[i].p_flags);
if(memsz) {
if(verbosity >= 2)
fprintf(stderr, "PHDR %d: 0x%x [0x%x] -> 0x%08x [0x%x] flags 0x%x\n",
i, offset, filesz, paddr, memsz, flags);
if(flags & PF_X) {
// TEXT segment
if(!(flags & PF_R))
fprintf(stderr, "Warning: non-readable segment %d\n", i);
if(flags & PF_W)
fprintf(stderr, "Warning: writable and executable segment %d\n", i);
if(filesz > memsz) {
fprintf(stderr, "Error: TEXT segment %d memory size (0x%x) smaller than file size (0x%x)\n",
i, memsz, filesz);
exit(1);
} else if (memsz > filesz) {
add_bss(map, paddr + filesz, memsz - filesz);
}
if(map->text_cnt >= MAX_TEXT_SEGMENTS) {
die("Error: Too many TEXT segments");
}
map->header.text_addr[map->text_cnt] = swap32(paddr);
map->header.text_size[map->text_cnt] = swap32(filesz);
map->text_elf_off[map->text_cnt] = offset;
map->text_cnt++;
} else {
// DATA or BSS segment
if(!(flags & PF_R))
fprintf(stderr, "Warning: non-readable segment %d\n", i);
if(filesz == 0) {
// BSS segment
add_bss(map, paddr, memsz);
// We need to keep PHDF sizes, so track these.
if(i == sbss_pdhr) {
sbssSizes[0] = memsz;
} else if(isWii && i == sbss_pdhr + 2) {
sbssSizes[1] = memsz;
}
} else {
// DATA segment
if(filesz > memsz) {
fprintf(stderr, "Error: segment %d memory size (0x%x) is smaller than file size (0x%x)\n",
i, memsz, filesz);
exit(1);
}
if(map->data_cnt >= MAX_DATA_SEGMENTS) {
die("Error: Too many DATA segments");
}
// Track sdata as well.
if(i == sdata_pdhr) {
sdataSizes[0] = memsz;
} else if(isWii && i == sdata_pdhr + 2) {
sdataSizes[1] = memsz;
}
map->header.data_addr[map->data_cnt] = swap32(paddr);
map->header.data_size[map->data_cnt] = swap32(filesz);
map->data_elf_off[map->data_cnt] = offset;
map->data_cnt++;
}
}
} else {
if(verbosity >= 1)
fprintf(stderr, "Skipping empty program header %d\n", i);
}
} else if(verbosity >= 1) {
fprintf(stderr, "Skipping program header %d of type %d\n", i, swap32(phdrs[i].p_type));
}
}
increment_bss_size(map, sdataSizes[0]);
//increment_bss_size(map, sdataSizes[1]);
increment_bss_size(map, sbssSizes[0]);
//increment_bss_size(map, sbssSizes[1]);
if(verbosity >= 2) {
fprintf(stderr, "Segments:\n");
for(i=0; i<map->text_cnt; i++) {
fprintf(stderr, " TEXT %d: 0x%08x [0x%x] from ELF offset 0x%x\n",
i, swap32(map->header.text_addr[i]), swap32(map->header.text_size[i]),
map->text_elf_off[i]);
}
for(i=0; i<map->data_cnt; i++) {
fprintf(stderr, " DATA %d: 0x%08x [0x%x] from ELF offset 0x%x\n",
i, swap32(map->header.data_addr[i]), swap32(map->header.data_size[i]),
map->data_elf_off[i]);
}
if(map->flags & HAVE_BSS)
fprintf(stderr, " BSS segment: 0x%08x [0x%x]\n", swap32(map->header.bss_addr),
swap32(map->header.bss_size));
}
}
void map_dol(DOL_map *map)
{
uint32_t fpos;
int i;
if(verbosity >= 2)
fprintf(stderr, "Laying out DOL file...\n");
fpos = DOL_ALIGN(sizeof(DOL_hdr));
for(i=0; i<map->text_cnt; i++) {
if(verbosity >= 2)
fprintf(stderr, " TEXT segment %d at 0x%x\n", i, fpos);
map->header.text_off[i] = swap32(fpos);
fpos = DOL_ALIGN(fpos + swap32(map->header.text_size[i]));
}
for(i=0; i<map->data_cnt; i++) {
if(verbosity >= 2)
fprintf(stderr, " DATA segment %d at 0x%x\n", i, fpos);
map->header.data_off[i] = swap32(fpos);
fpos = DOL_ALIGN(fpos + swap32(map->header.data_size[i]));
}
if(map->text_cnt == 0) {
if(verbosity >= 1)
fprintf(stderr, "Note: adding dummy TEXT segment to work around IOS bug\n");
map->header.text_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
}
if(map->data_cnt == 0) {
if(verbosity >= 1)
fprintf(stderr, "Note: adding dummy DATA segment to work around IOS bug\n");
map->header.data_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
}
}
#define BLOCK (1024*1024)
void fcpy(FILE *dst, FILE *src, uint32_t dst_off, uint32_t src_off, uint32_t size)
{
int left = size;
int read;
int written;
int block;
void *blockbuf;
if(fseek(src, src_off, SEEK_SET) < 0)
ferrordie(src, "reading ELF segment data");
if(fseek(dst, dst_off, SEEK_SET) < 0)
ferrordie(dst, "writing DOL segment data");
blockbuf = malloc(MIN(BLOCK, left));
while(left) {
block = MIN(BLOCK, left);
read = fread(blockbuf, 1, block, src);
if(read != block) {
free(blockbuf);
ferrordie(src, "reading ELF segment data");
}
written = fwrite(blockbuf, 1, block, dst);
if(written != block) {
free(blockbuf);
ferrordie(dst, "writing DOL segment data");
}
left -= block;
}
free(blockbuf);
}
void write_dol(DOL_map *map, const char *dol)
{
FILE *dolf;
int written;
int i;
if(verbosity >= 2)
fprintf(stderr, "Writing DOL file...\n");
dolf = fopen(dol, "wb");
if(!dolf)
perrordie("Could not open DOL file");
if(verbosity >= 2) {
fprintf(stderr, "DOL header:\n");
for(i=0; i<MAX(1,map->text_cnt); i++)
fprintf(stderr, " TEXT %d @ 0x%08x [0x%x] off 0x%x\n", i,
swap32(map->header.text_addr[i]), swap32(map->header.text_size[i]),
swap32(map->header.text_off[i]));
for(i=0; i<MAX(1,map->data_cnt); i++)
fprintf(stderr, " DATA %d @ 0x%08x [0x%x] off 0x%x\n", i,
swap32(map->header.data_addr[i]), swap32(map->header.data_size[i]),
swap32(map->header.data_off[i]));
if(swap32(map->header.bss_addr) && swap32(map->header.bss_size))
fprintf(stderr, " BSS @ 0x%08x [0x%x]\n", swap32(map->header.bss_addr),
swap32(map->header.bss_size));
fprintf(stderr, " Entry: 0x%08x\n", swap32(map->header.entry));
fprintf(stderr, "Writing DOL header...\n");
}
written = fwrite(&map->header, sizeof(DOL_hdr), 1, dolf);
if(written != 1)
ferrordie(dolf, "writing DOL header");
for(i=0; i<map->text_cnt; i++) {
if(verbosity >= 2)
fprintf(stderr, "Writing TEXT segment %d...\n", i);
fcpy(dolf, map->elf, swap32(map->header.text_off[i]), map->text_elf_off[i],
swap32(map->header.text_size[i]));
}
for(i=0; i<map->data_cnt; i++) {
if(verbosity >= 2)
fprintf(stderr, "Writing DATA segment %d...\n", i);
fcpy(dolf, map->elf, swap32(map->header.data_off[i]), map->data_elf_off[i],
swap32(map->header.data_size[i]));
}
if(verbosity >= 2)
fprintf(stderr, "All done!\n");
fclose(map->elf);
fclose(dolf);
}
int main(int argc, char **argv)
{
char **arg;
if(argc < 2) {
usage(argv[0]);
return 1;
}
arg = &argv[1];
argc--;
while(argc && *arg[0] == '-') {
if(!strcmp(*arg, "-h")) {
usage(argv[0]);
return 1;
} else if(!strcmp(*arg, "-v")) {
verbosity++;
} else if(!strcmp(*arg, "--")) {
arg++;
argc--;
break;
} else {
fprintf(stderr, "Unrecognized option %s\n", *arg);
usage(argv[0]);
return 1;
}
arg++;
argc--;
}
if(argc < 2) {
usage(argv[0]);
exit(1);
}
const char *elf_file = arg[0];
const char *dol_file = arg[1];
uint32_t sdata_pdhr = atoi(arg[2]);
uint32_t sbss_pdhr = atoi(arg[3]);
const char *platform = arg[4];
DOL_map map;
memset(&map, 0, sizeof(map));
read_elf_segments(&map, elf_file, sdata_pdhr, sbss_pdhr, platform);
map_dol(&map);
write_dol(&map, dol_file);
return 0;
}

318
tools/postprocess.py Normal file
View File

@ -0,0 +1,318 @@
#!/usr/bin/env python3
BANNER = """
# This script is the culmination of three patches supporting decompilation
# with the CodeWarrior compiler.
# - riidefi, 2020
#
# postprocess.py [args] file
#
# 1) Certain versions have a bug where the ctor alignment is ignored and set incorrectly.
# This option is enabled with -fctor-realign, and disabled by default with -fno-ctor-realign
#
# 2) Certain C++ symbols cannot be assembled normally.
# To support the buildsystem, a simple substitution system has been devised
#
# ?<ID> -> CHAR
#
# IDs (all irregular symbols in mangled names):
# 0: <
# 1: >
# 2: @
# 3: \\
# 4: ,
# 5: -
#
# This option is enabled with -fsymbol-fixup, and disabled by default with -fno-symbol-fixup
#
# 3) CodeWarrior versions below 2.3 used a different scheduler model.
# The script can currently adjust function epilogues with the old_stack option.
# -fprologue-fixup=[default=none, none, old_stack]
"""
import struct
# Substitutions
substitutions = (
('<', '?0'),
('>', '?1'),
('@', '?2'),
('\\', '?3'),
(',', '?4'),
('-', '?5')
)
def format(symbol):
for sub in substitutions:
symbol = symbol.replace(sub[0], sub[1])
return symbol
def decodeformat(symbol):
for sub in substitutions:
symbol = symbol.replace(sub[1], sub[0])
return symbol
# Stream utilities
def read_u8(f):
return struct.unpack("B", f.read(1))[0]
def read_u32(f):
return struct.unpack(">I", f.read(4))[0]
def read_u16(f):
return struct.unpack(">H", f.read(2))[0]
def write_u32(f, val):
f.write(struct.pack(">I", val))
class ToReplace:
def __init__(self, position, dest, src_size):
self.position = position # Where in file
self.dest = dest # String to patch
self.src_size = src_size # Pad rest with zeroes
# print("To replace: %s %s %s" % (self.position, self.dest, self.src_size))
def read_string(f):
tmp = ""
c = 0xff
while c != 0x00:
c = read_u8(f)
if c != 0:
tmp += chr(c)
return tmp
def ctor_realign(f, ofsSecHeader, nSecHeader, idxSegNameSeg):
patch_align_ofs = []
for i in range(nSecHeader):
f.seek(ofsSecHeader + i * 0x28)
ofsname = read_u32(f)
if not ofsname: continue
back = f.tell()
f.seek(ofsSecHeader + (idxSegNameSeg * 0x28) + 0x10)
ofsShST = read_u32(f)
f.seek(ofsShST + ofsname)
name = read_string(f)
if name == ".ctors" or name == ".dtors":
patch_align_ofs.append(ofsSecHeader + i * 0x28 + 0x20)
f.seek(back)
return patch_align_ofs
SHT_PROGBITS = 1
SHT_STRTAB = 3
def impl_postprocess_elf(f, do_ctor_realign, do_old_stack, do_symbol_fixup):
result = []
f.seek(0x20)
ofsSecHeader = read_u32(f)
f.seek(0x30)
nSecHeader = read_u16(f)
idxSegNameSeg = read_u16(f)
secF = False # First instance the section names
# Header: 0x32:
patch_align_ofs = []
if do_ctor_realign:
patch_align_ofs = ctor_realign(f, ofsSecHeader, nSecHeader, idxSegNameSeg)
for i in range(nSecHeader):
f.seek(ofsSecHeader + i * 0x28)
sh_name = read_u32(f)
sh_type = read_u32(f)
if sh_type == SHT_STRTAB and do_symbol_fixup:
if not secF:
secF = True
continue
f.seek(ofsSecHeader + i * 0x28 + 0x10)
ofs = read_u32(f)
size = read_u32(f)
f.seek(ofs)
string = ""
str_spos = ofs
for i in range(ofs, ofs+size):
c = read_u8(f)
if c == 0:
if len(string):
fixed = decodeformat(string)
if fixed != string:
result.append(ToReplace(str_spos, fixed, len(string)))
string = ""
str_spos = i+1
else:
string += chr(c)
else:
f.seek(ofsSecHeader + (idxSegNameSeg * 0x28) + 0x10)
ofsShST = read_u32(f)
f.seek(ofsShST + sh_name)
name = read_string(f)
if name == ".text" and do_old_stack:
f.seek(ofsSecHeader + i * 0x28 + 0x10)
ofs = read_u32(f)
size = read_u32(f)
# We assume
# 1) Only instructions are in the .text section
# 2) These instructions are 4-byte aligned
assert ofs != 0
assert ofs % 4 == 0
assert size % 4 == 0
f.seek(ofs)
mtlr_pos = 0
# (mtlr position, blr position)
epilogues = []
for _ in range(ofs, ofs+size, 4):
it = f.tell()
instr = read_u32(f)
# Skip padding
if instr == 0: continue
# Call analysis is not actually required
# No mtlr will exist without a blr; mtctr/bctr* is used for dynamic dispatch
# FUN_A:
# li r3, 0
# blr <---- No mtlr, move onto the next function
# FUN_B:
# ; complex function, stack manip
# mtlr r0 <---- Expect a blr
# addi r1, r1, 24
# blr <---- Confirm patch above
# mtlr alias for mtspr
if instr == 0x7C0803A6:
assert mtlr_pos == 0
mtlr_pos = it
# blr
elif instr == 0x4E800020:
if mtlr_pos:
epilogues.append((mtlr_pos, it))
mtlr_pos = 0
# Check for a lone mtlr
assert mtlr_pos == 0
# Reunify mtlr/blr instructions, shifting intermediary instructions up
for mtlr_pos, blr_pos in epilogues:
# Check if we need to do anything
if mtlr_pos + 4 == blr_pos: continue
# As the processor can only hold 6 instructions at once in the pipeline,
# it's unlikely for the mtlr be shifted up more instructions than that--usually,
# only one:
# mtlr r0
# addi r1, r1, 24
# blr
assert blr_pos - 4 > mtlr_pos
assert blr_pos - mtlr_pos <= 6 * 4
print("Patching old epilogue: %s %s" % (mtlr_pos, blr_pos))
f.seek(mtlr_pos)
mtlr = read_u32(f)
for it in range(mtlr_pos, blr_pos - 4, 4):
f.seek(it + 4)
next_instr = read_u32(f)
f.seek(it)
write_u32(f, next_instr)
f.seek(blr_pos - 4)
write_u32(f, mtlr)
return (result, patch_align_ofs)
def postprocess_elf(f, do_ctor_realign, do_old_stack, do_symbol_fixup):
patches = impl_postprocess_elf(f, do_ctor_realign, do_old_stack, do_symbol_fixup)
f.seek(0)
source_bytes = list(f.read())
for patch in patches[0]:
assert len(patch.dest) <= patch.src_size
for j in range(patch.src_size):
if j >= len(patch.dest):
c = 0
else:
c = ord(patch.dest[j])
source_bytes[patch.position + j] = c
# Patch ctor align
nP = 0
for p in patches[1]:
print("Patching ctors")
source_bytes[p + 0] = 0
source_bytes[p + 1] = 0
source_bytes[p + 2] = 0
source_bytes[p + 3] = 4
nP += 1
if nP > 1:
print("Patched ctors + dtors")
f.seek(0)
f.write(bytes(source_bytes))
def frontend(args):
inplace = ""
do_ctor_realign = False
do_old_stack = False
do_symbol_fixup = False
for arg in args:
if arg.startswith('-f'):
negated = False
if arg.startswith('-fno-'):
negated = True
arg = arg[len('-fno-'):]
else:
arg = arg[len('-f'):]
if arg == 'ctor_realign':
do_ctor_realign = not negated
elif arg == 'symbol-fixup':
do_symbol_fixup = not negated
elif arg.startswith('prologue-fixup='):
do_old_stack = arg[len('prologue-fixup='):] == 'old_stack'
else:
print("Unknown argument: %s" % arg)
elif arg.startswith('-'):
print("Unknown argument: %s. Perhaps you meant -f%s?" % (arg, arg))
else:
if inplace:
print("Cannot process %s. Only one source file may be specified." % arg)
else:
inplace = arg
if not inplace:
print("A file must be specified!")
return
try:
postprocess_elf(open(inplace, 'rb+'), do_ctor_realign, do_old_stack, do_symbol_fixup)
except FileNotFoundError:
print("Cannot open file %s" % inplace)
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print(BANNER)
else:
frontend(sys.argv[1:])