mirror of
https://codeberg.org/KeybadeBlox/JSRF-Decompilation.git
synced 2026-02-20 18:27:04 +03:00
Compare commits
4 commits
522bf8be7f
...
bbe9d63294
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbe9d63294 | ||
|
|
aac010eb71 | ||
|
|
be4946eb98 | ||
|
|
73b59a95cd |
10 changed files with 292 additions and 263 deletions
|
|
@ -40,7 +40,7 @@
|
|||
},
|
||||
"symbol_mappings": {
|
||||
"?finalizeGameData@@YAXXZ": "_$E2",
|
||||
"?initGameData@@YGXXZ": "_$E1"
|
||||
"?initGameData@@YAXXZ": "_$E1"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@ Game and GameObj classes that form the foundation of the JSRF game code.
|
|||
void readInput();
|
||||
|
||||
// Address: 0x00011000
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
GameObj::~GameObj() {
|
||||
g_game->gameObjCnt -= 1;
|
||||
}
|
||||
|
||||
// Address: 0x00011070
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursiveExecDefault() {
|
||||
}
|
||||
|
||||
// Address: 0x000110A0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawListDefault(
|
||||
GameObjFlags flagFilterAny1,
|
||||
int drawArg1,
|
||||
|
|
@ -36,22 +36,22 @@ void GameObj::drawListDefault(
|
|||
}
|
||||
|
||||
// Address: 0x00011220
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeDefault1() {
|
||||
}
|
||||
|
||||
// Address: 0x00011260
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeDefault2() {
|
||||
}
|
||||
|
||||
// Address: 0x000112A0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursiveExecEvent() {
|
||||
}
|
||||
|
||||
// Address: 0x000112D0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawListEvent(
|
||||
GameObjFlags flagFilterAny1,
|
||||
int drawArg1,
|
||||
|
|
@ -64,22 +64,22 @@ void GameObj::drawListEvent(
|
|||
}
|
||||
|
||||
// Address: 0x00011450
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeEvent1() {
|
||||
}
|
||||
|
||||
// Address: 0x00011490
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeEvent2() {
|
||||
}
|
||||
|
||||
// Address: 0x000114D0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursiveExecCoveredPause() {
|
||||
}
|
||||
|
||||
// Address: 0x00011500
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawListCoveredPause(
|
||||
GameObjFlags flagFilterAny1,
|
||||
int drawArg1,
|
||||
|
|
@ -92,22 +92,22 @@ void GameObj::drawListCoveredPause(
|
|||
}
|
||||
|
||||
// Address: 0x00011680
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeCoveredPause1() {
|
||||
}
|
||||
|
||||
// Address: 0x000116C0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeCoveredPause2() {
|
||||
}
|
||||
|
||||
// Address: 0x00011700
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursiveExecFreezeCam() {
|
||||
}
|
||||
|
||||
// Address: 0x00011730
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawListFreezeCam(
|
||||
GameObjFlags flagFilterAny1,
|
||||
int drawArg1,
|
||||
|
|
@ -120,22 +120,22 @@ void GameObj::drawListFreezeCam(
|
|||
}
|
||||
|
||||
// Address: 0x000118B0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeFreezeCam1() {
|
||||
}
|
||||
|
||||
// Address: 0x000118F0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeFreezeCam2() {
|
||||
}
|
||||
|
||||
// Address: 0x00011930
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursiveExecUncoveredPause() {
|
||||
}
|
||||
|
||||
// Address: 0x00011960
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawListUncoveredPause(
|
||||
GameObjFlags flagFilterAny1,
|
||||
int drawArg1,
|
||||
|
|
@ -148,40 +148,40 @@ void GameObj::drawListUncoveredPause(
|
|||
}
|
||||
|
||||
// Address: 0x00011AE0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeUncoveredPause1() {
|
||||
}
|
||||
|
||||
// Address: 0x00011B20
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::drawTreeUncoveredPause2() {
|
||||
}
|
||||
|
||||
// Address: 0x00011B60
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::addToSiblings(GameObj * sibling, GameObj * parent) {
|
||||
}
|
||||
|
||||
// Address: 0x00011B90
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::destructChildren(GameObj * firstChild) {
|
||||
}
|
||||
|
||||
// Address: 0x00011BD0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
GameObj * GameObj::getParent() {
|
||||
return this->parent;
|
||||
}
|
||||
|
||||
// Address: 0x00011BE0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::removeFromObjList(GameObj * obj) {
|
||||
if (obj->flags > -1) {
|
||||
}
|
||||
}
|
||||
|
||||
// Address: 0x00011C20
|
||||
// Matching: no
|
||||
// Status: nonmatching
|
||||
void GameObj::removeChildrenFromObjList(GameObj * firstChild) {
|
||||
if (firstChild != NULL) do {
|
||||
if (firstChild->flags > -1) {
|
||||
|
|
@ -196,11 +196,11 @@ void GameObj::removeChildrenFromObjList(GameObj * firstChild) {
|
|||
}
|
||||
|
||||
// Address: 0x00011C80
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void GameObj::drawDefault(unsigned unknown) {}
|
||||
|
||||
// Address: 0x00011C90
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void GameObj::execDefault() {}
|
||||
|
||||
// Eliminated by link time code generation (aliased with 0x00011C90)
|
||||
|
|
@ -243,42 +243,42 @@ void GameObj::postExecUncoveredPause() {}
|
|||
void GameObj::drawUncoveredPause(unsigned unknown) {}
|
||||
|
||||
// Address: 0x00011CA0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void DrawTree::copySomeVectors() {
|
||||
}
|
||||
|
||||
// Address: 0x00011D00
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursivePostExecDefault() {
|
||||
}
|
||||
|
||||
// Address: 0x00011D00
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursivePostExecEvent() {
|
||||
}
|
||||
|
||||
// Address: 0x00011E40
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursivePostExecCoveredPause() {
|
||||
}
|
||||
|
||||
// Address: 0x00011EE0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursivePostExecFreezeCam() {
|
||||
}
|
||||
|
||||
// Address: 0x00011F80
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::recursivePostExecUncoveredPause() {
|
||||
}
|
||||
|
||||
// Address: 0x00012020
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameObj::setParent(GameObj * parent) {
|
||||
}
|
||||
|
||||
// Address: 0x00012100
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
GameObj::GameObj(GameObj * parent, GameObjIndex index, GameObjFlags flags) {
|
||||
g_game->setObj(index, this);
|
||||
|
||||
|
|
@ -298,7 +298,7 @@ GameObj::GameObj(GameObj * parent, GameObjIndex index, GameObjFlags flags) {
|
|||
}
|
||||
|
||||
// Address: 0x00012170
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
DrawTree::DrawTree(GameObj * parent, GameObjIndex index, GameObjFlags flags) :
|
||||
GameObj(parent, index, flags) {
|
||||
this->otherBitfield = 7;
|
||||
|
|
@ -306,11 +306,11 @@ DrawTree::DrawTree(GameObj * parent, GameObjIndex index, GameObjFlags flags) :
|
|||
}
|
||||
|
||||
// Address: 0x000121D0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
DrawTree::~DrawTree() {}
|
||||
|
||||
// Address: 0x000121E0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
PlayerObj::PlayerObj(GameObj * parent, GameObjIndex index, GameObjFlags flags) :
|
||||
GameObj(parent, index, flags) {
|
||||
this->unknown0x44 = 0;
|
||||
|
|
@ -321,27 +321,27 @@ PlayerObj::PlayerObj(GameObj * parent, GameObjIndex index, GameObjFlags flags) :
|
|||
PlayerObj::~PlayerObj() {}
|
||||
|
||||
// Address: 0x00012210
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
Game::Game(unsigned * unknown1, unsigned unknown2) {
|
||||
}
|
||||
|
||||
// Address: 0x00012390
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
Game::~Game() {
|
||||
}
|
||||
|
||||
// Address: 0x000123E0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::exec() {
|
||||
}
|
||||
|
||||
// Address: 0x00012580
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::drawObj(GameObj * obj, unsigned unknown) {
|
||||
}
|
||||
|
||||
// Address: 0x000125E0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::drawList_(
|
||||
GameObjFlags flagFilterAny1,
|
||||
int drawArg1,
|
||||
|
|
@ -354,97 +354,97 @@ void Game::drawList_(
|
|||
}
|
||||
|
||||
// Address: 0x00012680
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::drawTree1(GameObj * obj) {
|
||||
}
|
||||
|
||||
// Address: 0x000126D0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::setCoveredPauseNextFrame(BOOL val) {
|
||||
}
|
||||
|
||||
// Address: 0x000126F0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::setEventNextFrame(BOOL val) {
|
||||
}
|
||||
|
||||
// Address: 0x00012710
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::setFreezeCamNextFrame(BOOL val) {
|
||||
}
|
||||
|
||||
// Address: 0x00012730
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::setUncoveredPauseNextFrame(BOOL val) {
|
||||
}
|
||||
|
||||
// Address: 0x00012750
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::enableDrawChildren() {
|
||||
this->drawChildren = TRUE;
|
||||
}
|
||||
|
||||
// Address: 0x00012760
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::enableSkipDraw() {
|
||||
this->skipDraw = TRUE;
|
||||
}
|
||||
|
||||
// Address: 0x00012770
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::fatal() {
|
||||
}
|
||||
|
||||
// Address: 0x000127B0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::setDrawMode(DrawMode mode) {
|
||||
this->drawMode = mode;
|
||||
}
|
||||
|
||||
// Address: 0x000127C0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::setGlobal(GlobalIndex index, unsigned val) {
|
||||
this->globals[index] = val;
|
||||
}
|
||||
|
||||
// Address: 0x000127E0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
unsigned Game::getGlobal(GlobalIndex index) {
|
||||
return this->globals[index];
|
||||
}
|
||||
|
||||
// Address: 0x000127F0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::addToDrawList(GameObj * obj) {
|
||||
}
|
||||
|
||||
// Address: 0x00012840
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::removeFromDrawList(GameObj * obj) {
|
||||
}
|
||||
|
||||
// Address: 0x00012870
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::setObj(GameObjIndex index, GameObj * obj) {
|
||||
if (0 <= index && index < OBJ_CNT)
|
||||
this->objects[index] = obj;
|
||||
}
|
||||
|
||||
// Address: 0x00012890
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::unsetObj(GameObjIndex index) {
|
||||
if (0 <= index && this->objects[index] != NULL)
|
||||
this->objects[index] = NULL;
|
||||
}
|
||||
|
||||
// Address: 0x000128C0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
GameObj * Game::getObj(GameObjIndex index) {
|
||||
return 0 <= index ? this->objects[index] : NULL;
|
||||
}
|
||||
|
||||
// Address: 0x000128E0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
int Game::allocObjIndex(GameObjIndex min, GameObjIndex max) {
|
||||
if (min <= max) do
|
||||
if (this->objects[min] == NULL) return min;
|
||||
|
|
@ -455,13 +455,13 @@ int Game::allocObjIndex(GameObjIndex min, GameObjIndex max) {
|
|||
}
|
||||
|
||||
// Address: 0x00012910
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
BOOL Game::objIndexAvail(GameObjIndex index) {
|
||||
return this->objects[index] == NULL;
|
||||
}
|
||||
|
||||
// Address: 0x00012930
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::swapObjs(GameObjIndex index1, GameObjIndex index2) {
|
||||
GameObj * tmp = this->objects[index1];
|
||||
this->objects[index1] = this->objects[index2];
|
||||
|
|
@ -472,37 +472,37 @@ void Game::swapObjs(GameObjIndex index1, GameObjIndex index2) {
|
|||
}
|
||||
|
||||
// Address: 0x00012980
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::clearScreen() {
|
||||
}
|
||||
|
||||
// Address: 0x000129B0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::enableSomeExtraDrawListCode() {
|
||||
this->runSomeExtraDrawListCode = TRUE;
|
||||
}
|
||||
|
||||
// Address: 0x000129C0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::setLogosStarted(BOOL val) {
|
||||
this->logosStarted = val;
|
||||
}
|
||||
|
||||
// Address: 0x000129D0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::clearDrawPriorityList() {
|
||||
this->drawPriorityListHead = NULL;
|
||||
this->drawPriorityListEndNext = &g_game->drawPriorityListHead;
|
||||
}
|
||||
|
||||
// Address: 0x000129F0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
GameObj * Game::getDrawPriorityListHead() {
|
||||
return this->drawPriorityListHead;
|
||||
}
|
||||
|
||||
// Address: 0x00012A00
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::appendToDrawPriorityList(GameObj * obj) {
|
||||
obj->drawPriorityListNext = NULL;
|
||||
*this->drawPriorityListEndNext = obj;
|
||||
|
|
@ -510,7 +510,7 @@ void Game::appendToDrawPriorityList(GameObj * obj) {
|
|||
}
|
||||
|
||||
// Address: 0x00012A20
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::sortDrawPriorityListSingleLevel(unsigned char sortKeyBitOffset) {
|
||||
for (
|
||||
unsigned i = 0;
|
||||
|
|
@ -520,35 +520,35 @@ void Game::sortDrawPriorityListSingleLevel(unsigned char sortKeyBitOffset) {
|
|||
}
|
||||
|
||||
// Address: 0x00012AC0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void Game::setFallbackBgColour(D3DCOLOR colour, BOOL useFallback) {
|
||||
this->useFallbackBgColour = useFallback;
|
||||
this->bgColourFallback = colour;
|
||||
}
|
||||
|
||||
// Address: 0x00012AE0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
RootExecObj::RootExecObj(GameObj * parent, GameObjIndex index, GameObjFlags flags) :
|
||||
GameObj(parent, index, flags) {
|
||||
}
|
||||
|
||||
// Address: 0x00012BE0
|
||||
// Matching: no
|
||||
// Status: matching
|
||||
RootExecObj::~RootExecObj() {
|
||||
}
|
||||
|
||||
// Address: 0x00012C10
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::initRootExecObj() {
|
||||
}
|
||||
|
||||
// Address: 0x00012C80
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::drawList(GameObjFlags flagFilterAll, BOOL unknown) {
|
||||
}
|
||||
|
||||
// Address: 0x000131A0
|
||||
// Matching: no
|
||||
// Status: nonmatching
|
||||
void Game::sortDrawPriorityList() {
|
||||
this->sortDrawPriorityListSingleLevel(0x00);
|
||||
this->sortDrawPriorityListSingleLevel(0x08);
|
||||
|
|
@ -557,23 +557,23 @@ void Game::sortDrawPriorityList() {
|
|||
}
|
||||
|
||||
// Address: 0x000131F0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::drawObjs() {
|
||||
}
|
||||
|
||||
// Address: 0x00013930
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::draw() {
|
||||
}
|
||||
|
||||
// Address: 0x00013A80
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void Game::frame() {
|
||||
readInput();
|
||||
}
|
||||
|
||||
// Address: 0x00013F80
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
int Game::mainLoop() {
|
||||
if (this->initState >= 0) while (true) {
|
||||
if (!this->fatalErr) this->frame();
|
||||
|
|
@ -585,7 +585,7 @@ int Game::mainLoop() {
|
|||
}
|
||||
|
||||
// Address: 0x00013FC0
|
||||
// Matching: no
|
||||
// Status: nonmatching
|
||||
void removeFromObjListByIndex(GameObjIndex index) {
|
||||
GameObj * obj = g_game->getObj(index);
|
||||
if (obj != NULL) {
|
||||
|
|
|
|||
|
|
@ -13,347 +13,347 @@ GameData g_gameData = GameData();
|
|||
|
||||
|
||||
// Address: 0x00039B50
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::checkFlagCondition(unsigned cond) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x00039BE0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::writeStateFlag(unsigned flagVal) {
|
||||
}
|
||||
|
||||
// Address: 0x00039C70
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::incrementChapter() {
|
||||
}
|
||||
|
||||
// Address: 0x00039C80
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setMissionDigits34(unsigned val) {
|
||||
}
|
||||
|
||||
// Address: 0x00039C90
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setSpawnPosIndex(unsigned val) {
|
||||
}
|
||||
|
||||
// Address: 0x00039CA0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
unsigned GameData::getSpawnPosIndex() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x00039CB0
|
||||
// Matching: no
|
||||
void GameData::unlockCharacter(unsigned charId) {
|
||||
// Status: unimplemented
|
||||
void GameData::unlockCharacter(PlayerCharId charId) {
|
||||
}
|
||||
|
||||
// Address: 0x00039CF0
|
||||
// Matching: no
|
||||
void GameData::lockCharacter(unsigned charId) {
|
||||
// Status: unimplemented
|
||||
void GameData::lockCharacter(PlayerCharId charId) {
|
||||
}
|
||||
|
||||
// Address: 0x00039D10
|
||||
// Matching: no
|
||||
BOOL GameData::characterUnlocked(unsigned charId) {
|
||||
// Status: unimplemented
|
||||
BOOL GameData::characterUnlocked(PlayerCharId charId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x00039D40
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::checkFlagConditions(unsigned * conds, unsigned count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x00039D80
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::writeStateFlags(unsigned * writes, unsigned count) {
|
||||
}
|
||||
|
||||
// Address: 0x00039DB0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::checkFlagConditionUnpacked(FlagList flagList, unsigned index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x00039DE0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::writeStateFlagUnpacked(FlagList flagList, unsigned index, unsigned val) {
|
||||
}
|
||||
|
||||
// Address: 0x00039E10
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setSoulSpawned(unsigned soulId) {
|
||||
}
|
||||
|
||||
// Address: 0x00039E40
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::getSoulSpawned(unsigned soulId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x00039E80
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setSoulCollected(unsigned soulId) {
|
||||
}
|
||||
|
||||
// Address: 0x00039EB0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::getSoulHeld(unsigned soulId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x00039EF0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::soulSpawnedUncollected(unsigned soulId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x00039F40
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::clearHeldSouls() {
|
||||
}
|
||||
|
||||
// Address: 0x00039F60
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::restoreHeldSouls() {
|
||||
}
|
||||
|
||||
// Address: 0x00039FD0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
unsigned GameData::getSoulCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A0A0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
unsigned GameData::getTotalSoulsInStage(unsigned stageId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A130
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
unsigned GameData::getHeldSoulsInStage(unsigned stageId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A2B0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::getSoulCollectedBySize(TagSize size, unsigned index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A2F0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setUnusedPerStageBitmask(unsigned stageId, unsigned index) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A340
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
int GameData::getTagState(unsigned stageId, unsigned tagIndex, BOOL rivalTag) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A3A0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setTagState(unsigned stageId, unsigned tagIndex, BOOL rivalTag, unsigned val) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A400
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setTagCovered(unsigned stageId, unsigned tagIndex, BOOL rivalTag, unsigned gangOrPlayer) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A4A0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setVolumeSettings(float volMusic, float volSfx) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A4C0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::getVolumeSettings(float * outMusic, float * outSfx) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A4E0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setRumbleEnabled(BOOL val) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A4F0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::getRumbleEnabled() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A500
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setGarageMusic(unsigned songId) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A510
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
unsigned GameData::getGarageMusic() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A520
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setUnusedBitfield(unsigned index) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A550
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setMiscObjective(unsigned index) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A580
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::getMiscObjective(unsigned index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A5C0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
unsigned GameData::countMiscObjectives() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A690
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::getHighScore(unsigned stageId, TestRunType type, unsigned rank, TestRunScore * out) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A750
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::incrementTimer(Timer timer) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A780
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
unsigned GameData::getTimer(Timer timer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A7B0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setTimer(Timer timer, unsigned frames) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A7F0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setSelectedTag(unsigned gangOrPlayer, TagSize size, unsigned tagId, BOOL multiplayer) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A820
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
unsigned GameData::getSelectedTag(unsigned gangOrPlayer, TagSize size, BOOL multiplayer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A840
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setCustomTagSelected(unsigned gangOrPlayer, TagSize size, BOOL active, BOOL multiplayer) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A870
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::getCustomTagSelected(unsigned gangOrPlayer, TagSize size, BOOL multiplayer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A890
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::setEventSeen(unsigned eventId) {
|
||||
}
|
||||
|
||||
// Address: 0x0003A8C0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::eventSeen(unsigned eventId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003A900
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::incrementPlaytime() {
|
||||
}
|
||||
|
||||
// Address: 0x0003A910
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
unsigned GameData::getSaveDataSize() {
|
||||
// 0x50 byte key + 0x3508 actual save data + 0x8 extra (why extra?)
|
||||
return 0x50 + sizeof this->saveActive + 0x8;
|
||||
}
|
||||
|
||||
// Address: 0x0003A920
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
BOOL GameData::decrypt(char * saveData) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Address: 0x0003AB60
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::encrypt(char * outSaveData) {
|
||||
}
|
||||
|
||||
// Address: 0x0003AE00
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::getSaveDescription(SaveDescription * outDesc) {
|
||||
}
|
||||
|
||||
// Address: 0x0003AE20
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::clearStateFlags(FlagListOrPtr flagList) {
|
||||
}
|
||||
|
||||
// Address: 0x0003AEA0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::resetTimer(Timer timer) {
|
||||
}
|
||||
|
||||
// Address: 0x0003AED0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
GameData::GameData() {
|
||||
}
|
||||
|
||||
// Address: 0x0003B3C0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::resetSelectedTags() {
|
||||
}
|
||||
|
||||
// Address: 0x0003B420
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::resetExceptSettingsAndSouls() {
|
||||
}
|
||||
|
||||
// Address: 0x0003B5A0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::resetExceptSettingsAndHighScores() {
|
||||
}
|
||||
|
||||
// Address: 0x0003B640
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::resetExceptSettings() {
|
||||
}
|
||||
|
||||
// Address: 0x0003B680
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::stash() {
|
||||
}
|
||||
|
||||
// Address: 0x0003B6A0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::stashRestoreExceptSpecialFlags() {
|
||||
}
|
||||
|
||||
// Address: 0x0003B6F0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::stashRestoreExceptHighScores() {
|
||||
}
|
||||
|
||||
// Address: 0x0003B790
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::stashRestore() {
|
||||
}
|
||||
|
||||
|
|
@ -361,6 +361,6 @@ void GameData::stashRestore() {
|
|||
GameData::~GameData() {}
|
||||
|
||||
// Address: 0x0003B7E0
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void GameData::addHighScore(unsigned stageId, TestRunType type, TestRunScore * score) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,34 @@ Save data and closely-related runtime data.
|
|||
#include "../XDK/Win32.h"
|
||||
|
||||
|
||||
// Enum that should probably go somewhere else
|
||||
enum PlayerCharId {
|
||||
PCHARID_BEAT,
|
||||
PCHARID_CORN,
|
||||
PCHARID_GUM,
|
||||
PCHARID_COMBO,
|
||||
PCHARID_RHYTH,
|
||||
PCHARID_SODA,
|
||||
PCHARID_YOYO,
|
||||
PCHARID_NT3000,
|
||||
PCHARID_GARAM,
|
||||
PCHARID_BOOGIE,
|
||||
PCHARID_CUBE,
|
||||
PCHARID_LOVESHOCKERS,
|
||||
PCHARID_POISONJAM,
|
||||
PCHARID_NOISETANK,
|
||||
PCHARID_IMMORTALS,
|
||||
PCHARID_DOOMRIDERS,
|
||||
PCHARID_RAPID99,
|
||||
PCHARID_POTTS,
|
||||
PCHARID_GOUJI,
|
||||
PCHARID_ROBOY,
|
||||
PCHARID_CLUTCH,
|
||||
PCHARID_JAZZ,
|
||||
PCHARID_AKUMU,
|
||||
PCHARID_ZEROBEAT
|
||||
};
|
||||
|
||||
// Data structure actually saved to disk
|
||||
struct SaveData {
|
||||
unsigned chapter;
|
||||
|
|
@ -93,10 +121,10 @@ enum TestRunType {
|
|||
|
||||
// Unpacked version of TestRunScoreSaved
|
||||
struct TestRunScore {
|
||||
unsigned score;
|
||||
unsigned character;
|
||||
unsigned rank1; // Used by Jet Tech
|
||||
unsigned rank2; // Used by other test runs
|
||||
unsigned score;
|
||||
PlayerCharId character;
|
||||
unsigned rank1; // Used by Jet Tech
|
||||
unsigned rank2; // Used by other test runs
|
||||
};
|
||||
|
||||
// Numeric IDs for different timers
|
||||
|
|
@ -140,9 +168,9 @@ struct GameData {
|
|||
void setMissionDigits34 (unsigned val);
|
||||
void setSpawnPosIndex (unsigned val);
|
||||
unsigned getSpawnPosIndex ();
|
||||
void unlockCharacter (unsigned charId);
|
||||
void lockCharacter (unsigned charId);
|
||||
BOOL characterUnlocked (unsigned charId);
|
||||
void unlockCharacter (PlayerCharId charId);
|
||||
void lockCharacter (PlayerCharId charId);
|
||||
BOOL characterUnlocked (PlayerCharId charId);
|
||||
BOOL checkFlagConditions (unsigned * conds , unsigned count);
|
||||
void writeStateFlags (unsigned * writes, unsigned count);
|
||||
BOOL checkFlagConditionUnpacked(FlagList flagList, unsigned index);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Game * g_game;
|
|||
|
||||
|
||||
// Address: 0x0006F9E0
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
void main(void) {
|
||||
g_game = new Game(NULL, 0);
|
||||
g_game->initRootExecObj();
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ Mat4 * g_matrices[3];
|
|||
|
||||
|
||||
// Address: 0x001BB690
|
||||
// Matching: yes
|
||||
// Status: matching
|
||||
HRESULT __stdcall initMatrices(unsigned const count) {
|
||||
/* Initialize the matrix stack with the given number of matrices.
|
||||
The requested number of matrices is rounded up to the nearest multiple of two.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter);
|
|||
|
||||
|
||||
// Address: 0x00148023
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
void __cdecl mainCRTStartup(void) {
|
||||
/* The true entrypoint of the game, spawning a thread for the rest to run in
|
||||
The linker automatically sets this function to the entrypoint.
|
||||
|
|
@ -25,7 +25,7 @@ The linker automatically sets this function to the entrypoint.
|
|||
}
|
||||
|
||||
// Address: 0x00147FB4
|
||||
// Matching: no
|
||||
// Status: unimplemented
|
||||
DWORD __stdcall mainXapiStartup(LPVOID const lpThreadParameter) {
|
||||
/* Runs some initialization and then calls main() */
|
||||
main();
|
||||
|
|
@ -83,38 +83,54 @@ executable where objdiff doesn't expect them to be, which will mess up our
|
|||
diffs. To correct this, open the memory map (`Window > Memory Map`) and
|
||||
uncheck the "X" column for `.rdata`, `.data`, and `DOLBY`.
|
||||
|
||||
Now we'll import data types from the decompilation. Open a shell in the
|
||||
`ghidra/` directory of your copy of the repository and run `make_header.sh`,
|
||||
which will produce a `jsrf.h` in the same directory with the combined contents
|
||||
of every header in a format suitable for Ghidra. Then, in Ghidra, select
|
||||
`File > Parse C Source...` to open a window for importing C headers. Remove
|
||||
everything from the "Source files to parse" and "Parse options" boxes, and add
|
||||
`jsrf.h` to the former (click the green + symbol on the right and select the
|
||||
`jsrf.h` file). Click the "..." on the "Program Architecture:" box and select
|
||||
the row with the values "x86," "default," "32," "little," and "Visual Studio."
|
||||
Finally, click the "Parse to Program" button, "Continue" to confirm, and
|
||||
"Don't Use Open Archives" (the header is completely self-contained and doesn't
|
||||
need any information from any other data type archives). You should then see a
|
||||
window reporting successful import, and you'll be able to find `jsrf.h` with
|
||||
all of its definitions under `default.xbe` in the Data Type Manager window in
|
||||
the bottom left.
|
||||
Now we'll import data types from the decompilation. Open a Unix-style shell
|
||||
(e.g. Git Bash if on Windows) in the `ghidra/` directory of your copy of the
|
||||
repository and run `make_header.sh`, which will produce a `jsrf.h` in the same
|
||||
directory with the combined contents of every header in a format suitable for
|
||||
Ghidra. Then, in Ghidra, select `File > Parse C Source...` to open a window
|
||||
for importing C headers. Remove everything from the "Source files to parse"
|
||||
and "Parse options" boxes, and add `jsrf.h` to the former (click the green +
|
||||
symbol on the right and select the `jsrf.h` file). Click the "..." on the
|
||||
"Program Architecture:" box and select the row with the values "x86,"
|
||||
"default," "32," "little," and "Visual Studio." Finally, click the "Parse to
|
||||
Program" button, "Continue" to confirm, and "Don't Use Open Archives" (the
|
||||
header is completely self-contained and doesn't need any information from any
|
||||
other data type archives). You should then see a window reporting successful
|
||||
import, and you'll be able to find `jsrf.h` with all of its definitions under
|
||||
`default.xbe` in the Data Type Manager window in the bottom left.
|
||||
|
||||
Lastly, we'll import symbols from the JSRF decompilation repository. Open the
|
||||
script manager (`Window > Script Manager`) and select the "Data" folder in the
|
||||
left pane. Double click the script titled `ImportSymbolsScript.py`, and a file
|
||||
picker will open after a moment. Select `symboltable.tsv` from the `ghidra/`
|
||||
directory of your cloned JSRF decompilation repository, and you should see a
|
||||
bunch of `Created function...` and `Created label...` printed to the scripting
|
||||
console window. Save your changes (save icon in the top left of the
|
||||
CodeBrowser window), and your Ghidra project should be all ready for creating
|
||||
object files for objdiff.
|
||||
Much of our work with Ghidra will make use of some custom scripts we've
|
||||
written, so we'll have to tell it where to find them. Open up the Script
|
||||
Manager (`Window > Script Manager`) and then open the Bundle Manager by
|
||||
clicking the "manage script directories" button (it looks sort of like a
|
||||
bulleted list). Click the green + in the top right to add a new directory and
|
||||
select the `ghidra/ghidra_scripts` directory in this repository.
|
||||
|
||||
The first script we'll want to run is the symbol importer to get known data and
|
||||
functions into your Ghidra project. In the Script Manager window, select the
|
||||
"Import" category in the left pane and double click the `EnhancedImport.java`
|
||||
script in the right pane to run it. You'll then be asked for an input file;
|
||||
select `ghidra/symboltable.tsv` from this repository. Afterwards, you'll see a
|
||||
bunch of "Importing ..." messages in a console in the main CodeBrowser window,
|
||||
some of which may have "can't find data type X" added on if something's marked
|
||||
with a type that hasn't made its way into our decompiled code yet, and there'll
|
||||
be a bunch of new functions and labels defined.
|
||||
|
||||
While we imported a bunch of data types earlier, Ghidra's C parser leaves out
|
||||
some important information that we'll have to fill in with another script. In
|
||||
the Script Manager, run `ClassFixup.java` from the "Data Types" category, and
|
||||
you should see some "Converting X to class" and "Fixing calling convention of
|
||||
X" messages in the console.
|
||||
|
||||
Now you've got a Ghidra project containing everything we know about JSRF's
|
||||
code! Make sure you save your Ghidra project now that everything's set up.
|
||||
|
||||
|
||||
### Producing Object Files
|
||||
Close all of your Ghidra windows and open a Unix-style shell (e.g. Git Bash if
|
||||
on Windows) in the decompilation repository's `ghidra/` directory. The
|
||||
`delink.sh` script is our automated tool for extracting all the object files
|
||||
that have been identified so far. Invoke it with three arguments:
|
||||
Close all of your Ghidra windows and open a Unix-style shell in the
|
||||
decompilation repository's `ghidra/` directory. The `delink.sh` script is our
|
||||
automated tool for extracting all the object files that have been identified so
|
||||
far. The easiest way to run it is to invoke it with three arguments:
|
||||
|
||||
- The path to your Ghidra installation (the directory with files like
|
||||
`ghidraRun` and `ghidraRun.bat`, and directories like `docs/` and
|
||||
|
|
@ -128,27 +144,30 @@ Unix-style paths. Make sure the paths are surrounded by quotes, too (e.g.
|
|||
`'C:\path\to\whatever'`), else the shell won't understand the backslashes
|
||||
correctly.
|
||||
|
||||
If you find typing out these arguments to be too much of a pain, you can also
|
||||
define the environment variables `$GHIDRA_HOME`, `$JSRFDECOMP_PROJECTPATH`, and
|
||||
`$JSRFDECOMP_PROJECTNAME` and invoke the script without arguments.
|
||||
|
||||
There are a couple errors you might get here:
|
||||
|
||||
- `Unable to lock project!`: This means that Ghidra isn't fully closed. Make
|
||||
sure you've completely closed every Ghidra window before running `delink.sh`.
|
||||
- `Script not found: DelinkProgram.java` and
|
||||
`Invalid script: DelinkProgram.java`: This means that the either the Ghidra
|
||||
delinker extension isn't properly installed, or you've somehow invoked the
|
||||
script in a way that can't see the extension (e.g. installing Ghidra on
|
||||
Windows and then invoking the script from WSL). Ensure it's installed and
|
||||
enabled first, and that you're not running in some kind of environment
|
||||
different from where you installed Ghidra.
|
||||
- `Script not found` and `Invalid script`: This means that you haven't added
|
||||
the repository's `ghidra_scripts` directory to the script search path as
|
||||
described in the previous section (particulary if it mentions
|
||||
`MSVC7Mangle.java`), the Ghidra delinker extension isn't properly installed
|
||||
(particularly if it mentions `DelinkProgram.java`), or you've somehow invoked
|
||||
the script in a way that can't see the scripts (e.g. installing Ghidra on
|
||||
Windows and then invoking the script from WSL).
|
||||
- `java.lang.RuntimeException: Failed to export ...`: This means that the
|
||||
delinker extension doesn't like something about what it was told to delink.
|
||||
One known cause is duplicate symbol names. If you haven't modified
|
||||
`objects.csv` or `symboltable.tsv`, let other people on the project know so
|
||||
that they can look into fixing it.
|
||||
|
||||
If all goes well, you'll see the message `Delinking complete!` at the end of
|
||||
the script's output, and the extracted object files will be in the
|
||||
`decompile/target/` directory of the repository. Now we're ready to start
|
||||
recompiling and diffing code with objdiff.
|
||||
If all goes well, the extracted object files will be in the `decompile/target/`
|
||||
directory of the repository. Now we're ready to start recompiling and diffing
|
||||
code with objdiff.
|
||||
|
||||
|
||||
### Setting Up objdiff
|
||||
|
|
@ -167,9 +186,11 @@ correctly set up on your `PATH`.
|
|||
|
||||
One important piece of information, to make sure you get the correct match
|
||||
percentages: set `Diff Options > Function relocation diffs` to "None."
|
||||
Otherwise, approximately all references to functions and non-local variables
|
||||
will be marked as nonmatching (this has to do with the delinking process not
|
||||
applying name mangling, which isn't expected to be fixed).
|
||||
Otherwise, some references to non-local variables will be marked as nonmatching
|
||||
(this is because it's sometimes not possible to make certain things named
|
||||
variables in Ghidra, particularly thread-local storage, and other times it's
|
||||
not possible to assign a fixed name to certain implicitly generated output in
|
||||
the recompiled code).
|
||||
|
||||
|
||||
### Using objdiff
|
||||
|
|
@ -180,14 +201,13 @@ them. In the best case, corresponding functions in each file will have the
|
|||
same name and be in the same section, at which point objdiff can link them
|
||||
automatically. Otherwise, one has to click on one of the corresponding
|
||||
functions in one pane and the other function in the other pane to tell objdiff
|
||||
to link them. Common cases of this are class methods (the names won't match)
|
||||
and implicitly generated functions, such as exception handling code placed in
|
||||
`.text$x` in the recompiled object file. Keep in mind that objdiff's matching
|
||||
does not appear fully reliable in some cases, particularly when diffing data
|
||||
with external pointers (which appear as `?? ?? ?? ??`) that aren't explicitly
|
||||
marked as non-matching but still somehow reduce the match percentage, so you'll
|
||||
have to use a tiny amount of judgement to determine when you actually have a
|
||||
match.
|
||||
to link them. The most common cases of this are implicitly generated functions
|
||||
and data, such as exception handling code placed in `.text$x` in the recompiled
|
||||
object file. Be aware that objdiff's matching does not appear fully reliable
|
||||
in some cases, particularly when diffing data with external pointers (which
|
||||
appear as `?? ?? ?? ??`) that aren't explicitly marked as non-matching but
|
||||
still somehow reduce the match percentage, so you'll have to use a tiny amount
|
||||
of judgement to determine when you actually have a match.
|
||||
|
||||
Clicking on a function that's been linked across both object files shows a diff
|
||||
of the disassembly of both versions of the function, with any differences
|
||||
|
|
@ -197,8 +217,20 @@ reaches 100%. Depending on how you configure objdiff, it will rebuild
|
|||
automatically whenever you save a change to a source file, or you can manually
|
||||
rebuild with the "Build" button at the top of the right pane.
|
||||
|
||||
There are no concrete instructions to give for writing decompiled code. Try
|
||||
importing headers from `decompile/src/` into Ghidra
|
||||
When viewing and editing decompiled source files, be mindful of the
|
||||
`// Status:` annotation above each function, which has the following meanings:
|
||||
- `unimplemented`: The decompiled function does not yet reproduce the behaviour
|
||||
of the original
|
||||
- `nonmatching`: The decompiled function is believed to behave the same as the
|
||||
original, but it does not fully match in objdiff
|
||||
- `matching`: The decompiled function perfectly matches the original in objdiff
|
||||
Be sure to update them as you decompile if appropriate. Some functions may
|
||||
also have other annotations describing nontrivial effects of link-time code
|
||||
generation (LTCG), such as a nonstandard calling convention or multiple
|
||||
functions being merged into one.
|
||||
|
||||
Otherwise, there are no concrete instructions to give for writing decompiled
|
||||
code. Try importing headers from `decompile/src/` into Ghidra
|
||||
(`File > Parse C Source...`) to get access to JSRF classes, and use Ghidra's
|
||||
decompilation of the function in the CodeBrowser as a starting point for
|
||||
writing your matching function, exercising whatever C++ and x86 assembly
|
||||
|
|
@ -223,46 +255,11 @@ whole executable in Ghidra.
|
|||
|
||||
|
||||
### Updating `symboltable.tsv`
|
||||
If you have got a bunch of symbols you'd like to add to `symboltable.tsv`, a
|
||||
workflow has been devised to generate it from your Ghidra project. Before
|
||||
regenerating the table, however, make sure that you have all of it symbols
|
||||
already in your project so that you don't end up deleting any. One option is
|
||||
to import `symboltable.tsv` into your project with the `ImportSymbolsScript.py`
|
||||
script as mentioned under "Creating a JSRF Ghidra Project," but be aware that
|
||||
this will overwrite any names you've assigned to the same symbols. You will
|
||||
also have to ensure that no two symbols share the same name. This can be
|
||||
avoided by using namespaces if need be (i.e. `X::symbol` and `Y::symbol` may
|
||||
coexist), but function overloading must be avoided (you may not have one
|
||||
function with the signature `void X::f(int)` and another with the signature
|
||||
`void X::f(float)`), else errors can arise when delinking, as the delinker
|
||||
extension does not mangle symbol names. Thunked functions can also cause
|
||||
problems because Ghidra does not include them alongside other functions in the
|
||||
symbol table, so convert them to regular functions (right click on the thunked
|
||||
function in the symbol tree and unset it as a thunk in the `Function` submenu).
|
||||
|
||||
Once you're ready to export your symbols, open the symbol table
|
||||
(`Window > Symbol Table`). Open the symbol filter window (cog button near the
|
||||
top right), and uncheck everything but "User Defined" under "Symbol Source,"
|
||||
"Data Labels" and "Function Labels" under "Symbol Types," "Use Advanced
|
||||
Filters," and "Non-Externals" under "Non-Externals." This ensures that you
|
||||
only export symbols that you've defined and that are useful for delinking.
|
||||
|
||||
Now we need to configure the columns that we want to export. Right-click on
|
||||
one of the colum headers, click "Add/Remove Columns..." to open the "Select
|
||||
Columns" window, and in it check only "Location," "Name," "Namespace," and
|
||||
"Type." Click "OK" to close the window and ensure that the column order is
|
||||
"Location," "Namespace," "Name," "Type" (you can drag the column headers to
|
||||
reorder them if needed).
|
||||
|
||||
Now, to actually export the table, right-click on one of the table cells, click
|
||||
"Select All," and then right-click again on a cell to select "Export > Export
|
||||
to CSV..." before selecting where to save your exported symbol table.
|
||||
|
||||
The final step is converting this CSV file to the format expected by
|
||||
`ImportSymbolsScript.py`. Open a shell in the repository's `ghidra/` directory
|
||||
and run `make_symboltable.sh` with the path of your exported CSV as an
|
||||
argument, and `symboltable.tsv` will be overwritten with a new table containing
|
||||
your exported symbols.
|
||||
If you have got a bunch of symbols you'd like to add to `symboltable.tsv`, you
|
||||
can generate a new copy from your Ghidra project by running the
|
||||
`EnhancedExport.java` script from the "Export" category. If you want to merge
|
||||
the new table into the repository, make sure to take a look at the diff first
|
||||
to ensure you're not inadvertently deleting anything.
|
||||
|
||||
|
||||
### Updating `make_header.sh`
|
||||
|
|
@ -314,12 +311,16 @@ correctly (exception-handling code might be appended onto another function, for
|
|||
example). Because `symboltable.tsv` should only be populated with symbols that
|
||||
have been manually defined as per the previous section, this means that you
|
||||
need to define variable names and labels in Ghidra for everything therein (and
|
||||
ideally everything referenced externally, as well). Do try to maintain basic
|
||||
ideally everything referenced externally, as well). Strive to maintain basic
|
||||
consistency with the rest of the codebase: functions and methods begin with
|
||||
lowercase letters, for instance, while class/struct/enum names begin with
|
||||
capital letters, and special methods like constructors and destructors should
|
||||
have the names they would have in real C++ code (i.e. `Class::Class` and
|
||||
`Class::~Class`, respectively).
|
||||
`Class::~Class`, respectively). Special class methods and members like
|
||||
constructors and vtables must follow their established naming conventions for
|
||||
our tooling to work properly. Also note that you can (mostly) disable name
|
||||
mangling for a symbol by making it a member of the `extern_"C"` namespace,
|
||||
which applies C-style name mangling as used by some symbols.
|
||||
|
||||
Once an object is ready for extracting, its `Delink?` column should be set to
|
||||
`true` and the `objdiff.json` file in the `decompile/` directory should be
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ public class MSVC7Mangle extends GhidraScript {
|
|||
// main() and extern "C" symbols get C name mangling
|
||||
// (some other things, do, too, but just use extern "C" instead
|
||||
// of making me find and list them all...)
|
||||
return nameRaw == "main" ||
|
||||
return nameRaw.equals("main") ||
|
||||
nameRaw.startsWith("extern_\"C\"::") ? mangleCFn (f)
|
||||
: mangleCppFn(f);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,9 +238,9 @@
|
|||
0x00039c80 func void __thiscall notinline nofixup GameData::setMissionDigits34 uint missionDigits34
|
||||
0x00039c90 func void __thiscall notinline nofixup GameData::setSpawnPosIndex uint index
|
||||
0x00039ca0 func uint __thiscall notinline nofixup GameData::getSpawnPosIndex
|
||||
0x00039cb0 func void __thiscall notinline nofixup GameData::unlockCharacter PlayerCharId32 charId
|
||||
0x00039cf0 func void __thiscall notinline nofixup GameData::lockCharacter PlayerCharId32 charId
|
||||
0x00039d10 func BOOL __thiscall notinline nofixup GameData::characterUnlocked PlayerCharId32 charId
|
||||
0x00039cb0 func void __thiscall notinline nofixup GameData::unlockCharacter PlayerCharId charId
|
||||
0x00039cf0 func void __thiscall notinline nofixup GameData::lockCharacter PlayerCharId charId
|
||||
0x00039d10 func BOOL __thiscall notinline nofixup GameData::characterUnlocked PlayerCharId charId
|
||||
0x00039d40 func BOOL __thiscall notinline nofixup GameData::checkFlagConditions uint * conds uint count
|
||||
0x00039d80 func void __thiscall notinline nofixup GameData::writeStateFlags uint * writes uint count
|
||||
0x00039db0 func BOOL __thiscall notinline nofixup GameData::checkFlagConditionUnpacked FlagList flagList uint index
|
||||
|
|
@ -343,7 +343,7 @@
|
|||
0x0004c070 func void __thiscall notinline nofixup Mission::waitForSomething
|
||||
0x0004c400 func void __thiscall notinline nofixup Mission::showTextForFrame
|
||||
0x0004ca20 func void __thiscall notinline nofixup Mission::setPauseOptions Call_SetPauseOptions * args
|
||||
0x0004d5f0 func undefined __stdcall notinline nofixup populateSomeCharIdArray PlayerCharId32 * buf
|
||||
0x0004d5f0 func undefined __stdcall notinline nofixup populateSomeCharIdArray PlayerCharId * buf
|
||||
0x0004d880 func void __thiscall notinline nofixup Mission::setManyPlayerStateFieldsToOne
|
||||
0x0004db30 func void default notinline nofixup resolveMissionBinPtrs MissionBin * mssn
|
||||
0x0004e930 func void __stdcall notinline nofixup newMission uint chapter uint idDigits34 BOOL param_3
|
||||
|
|
@ -446,7 +446,7 @@
|
|||
0x0006f600 func undefined __cdecl notinline nofixup showReconnectNAndStartMessage1 uint controllerId
|
||||
0x0006f650 func undefined __cdecl notinline nofixup showReconnectNAndStartMessage2 uint controllerId
|
||||
0x0006f6a0 func void __cdecl notinline nofixup showReconnectControllerMessage
|
||||
0x0006f6d0 func void default notinline nofixup showCharacterJoinMessage PlayerCharId32 character undefined4 controller
|
||||
0x0006f6d0 func void default notinline nofixup showCharacterJoinMessage PlayerCharId character undefined4 controller
|
||||
0x0006f730 func undefined unknown notinline nofixup showProblemWithDisc
|
||||
0x0006f760 func undefined unknown notinline nofixup showSaveLoadErr
|
||||
0x0006f9c0 func undefined4 * __thiscall notinline nofixup UnknownStatic27::~UnknownStatic27 byte param_1
|
||||
|
|
@ -1126,7 +1126,7 @@
|
|||
0x001f9358 data UnknownStatic13Part g_unknownStatic13PartDefault
|
||||
0x001f93c8 data UnknownStatic13 g_unknownStatic13
|
||||
0x001f9640 data UnknownStatic13Part * g_unchangingUnknownStatic3Ptr
|
||||
0x001f9808 data PlayerCharId32[32] charIds
|
||||
0x001f9808 data PlayerCharId[32] charIds
|
||||
0x001f9888 data SwitcherMethod *[119] Mission::exec_1Funcs
|
||||
0x001f9a68 data SwitcherMethod *[119] Mission::exec_5Funcs
|
||||
0x001f9c48 data SwitcherMethod *[119] Mission::exec_3Funcs
|
||||
|
|
@ -1176,7 +1176,7 @@
|
|||
0x0021c234 data uint[5] tagIdsPoisonJam
|
||||
0x0021c248 data uint[5] tagIdsImmortals
|
||||
0x0021c25c data uint[5] tagIdsZeroBeat
|
||||
0x0021c450 data PlayerCharId32[24] someCharIdArray
|
||||
0x0021c450 data PlayerCharId[24] someCharIdArray
|
||||
0x0021c510 data EvCharId[24] someCharIdMapping
|
||||
0x0021c570 data char *[5] hasJoinedYou
|
||||
0x0021c584 data undefined * testRunRankNamesJa
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 3.
|
Loading…
Add table
Add a link
Reference in a new issue