Merge branch 'isledecomp:master' into master

This commit is contained in:
Cydra 2023-06-22 00:18:05 +02:00 committed by GitHub
commit 7950a29e8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 162 additions and 85 deletions

View File

@ -60,11 +60,14 @@ jobs:
C:\msys64\usr\bin\wget.exe https://legoisland.org/download/ISLE.EXE C:\msys64\usr\bin\wget.exe https://legoisland.org/download/ISLE.EXE
C:\msys64\usr\bin\wget.exe https://legoisland.org/download/LEGO1.DLL C:\msys64\usr\bin\wget.exe https://legoisland.org/download/LEGO1.DLL
pip install capstone pip install capstone
python3 tools/reccomp/reccomp.py ISLE.EXE Release/ISLE.EXE Release/ISLE.PDB ISLE python3 tools/reccmp/reccmp.py -H ISLEPROGRESS.HTML ISLE.EXE Release/ISLE.EXE Release/ISLE.PDB ISLE
python3 tools/reccomp/reccomp.py LEGO1.DLL Release/LEGO1.DLL Release/LEGO1.PDB LEGO1 python3 tools/reccmp/reccmp.py -H LEGO1PROGRESS.HTML LEGO1.DLL Release/LEGO1.DLL Release/LEGO1.PDB LEGO1
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@master uses: actions/upload-artifact@master
with: with:
name: Win32 name: Win32
path: Release path: |
Release
ISLEPROGRESS.HTML
LEGO1PROGRESS.HTML

View File

@ -37,13 +37,7 @@ Isle::Isle()
m_frameDelta = 10; m_frameDelta = 10;
m_windowActive = 1; m_windowActive = 1;
MxRect32 rect; m_videoParam = MxVideoParam(MxRect32(0, 0, 639, 479), NULL, 1, MxVideoParamFlags());
rect.m_left = 0;
rect.m_top = 0;
rect.m_right = 639;
rect.m_bottom = 479;
m_videoParam = MxVideoParam(rect, NULL, 1, MxVideoParamFlags());
m_videoParam.flags().Enable16Bit(MxDirectDraw::GetPrimaryBitDepth() == 16); m_videoParam.flags().Enable16Bit(MxDirectDraw::GetPrimaryBitDepth() == 16);
m_windowHandle = NULL; m_windowHandle = NULL;
@ -116,7 +110,7 @@ void Isle::Close()
} }
// OFFSET: ISLE 0x402740 // OFFSET: ISLE 0x402740
BOOL ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize) BOOL Isle::ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize)
{ {
HKEY hKey; HKEY hKey;
DWORD valueType; DWORD valueType;
@ -135,7 +129,7 @@ BOOL ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize)
} }
// OFFSET: ISLE 0x4027b0 // OFFSET: ISLE 0x4027b0
int ReadRegBool(LPCSTR name, BOOL *out) int Isle::ReadRegBool(LPCSTR name, BOOL *out)
{ {
char buffer[256]; char buffer[256];
@ -143,28 +137,30 @@ int ReadRegBool(LPCSTR name, BOOL *out)
if (read) { if (read) {
if (strcmp("YES", buffer) == 0) { if (strcmp("YES", buffer) == 0) {
*out = TRUE; *out = TRUE;
return TRUE; return read;
} }
if (strcmp("NO", buffer) == 0) { if (strcmp("NO", buffer) == 0) {
*out = FALSE; *out = FALSE;
return TRUE; return read;
} }
read = FALSE;
} }
return FALSE; return read;
} }
// OFFSET: ISLE 0x402880 // OFFSET: ISLE 0x402880
int ReadRegInt(LPCSTR name, int *out) int Isle::ReadRegInt(LPCSTR name, int *out)
{ {
char buffer[256]; char buffer[256];
if (ReadReg(name, buffer, sizeof(buffer))) { BOOL read = ReadReg(name, buffer, sizeof(buffer));
if (read) {
*out = atoi(buffer); *out = atoi(buffer);
return TRUE;
} }
return FALSE; return read;
} }
// OFFSET: ISLE 0x4028d0 // OFFSET: ISLE 0x4028d0
@ -256,16 +252,18 @@ void Isle::SetupVideoFlags(BOOL fullScreen, BOOL flipSurfaces, BOOL backBuffers,
// OFFSET: ISLE 0x4013b0 // OFFSET: ISLE 0x4013b0
BOOL Isle::SetupLegoOmni() BOOL Isle::SetupLegoOmni()
{ {
BOOL result = FALSE;
char mediaPath[256]; char mediaPath[256];
GetProfileStringA("LEGO Island", "MediaPath", "", mediaPath, sizeof(mediaPath)); GetProfileStringA("LEGO Island", "MediaPath", "", mediaPath, sizeof(mediaPath));
if (Lego()->Create(MxOmniCreateParam(mediaPath, (struct HWND__ *) m_windowHandle, m_videoParam, MxOmniCreateFlags())) != FAILURE) { BOOL failure = Lego()->Create(MxOmniCreateParam(mediaPath, (struct HWND__ *) m_windowHandle, m_videoParam, MxOmniCreateFlags())) == FAILURE;
if (!failure) {
VariableTable()->SetVariable("ACTOR_01", ""); VariableTable()->SetVariable("ACTOR_01", "");
TickleManager()->vtable1c(VideoManager(), 10); TickleManager()->vtable1c(VideoManager(), 10);
return TRUE; result = TRUE;
} }
return FALSE; return result;
} }
// OFFSET: ISLE 0x402e80 // OFFSET: ISLE 0x402e80
@ -283,6 +281,14 @@ void Isle::SetupCursor(WPARAM wParam)
break; break;
case 0xB: case 0xB:
m_cursorCurrent = NULL; m_cursorCurrent = NULL;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 0xA:
break; break;
} }

View File

@ -12,7 +12,11 @@ class Isle
Isle(); Isle();
~Isle(); ~Isle();
static void Close(); void Close();
BOOL ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize);
int ReadRegBool(LPCSTR name, BOOL *out);
int ReadRegInt(LPCSTR name, int *out);
MxResult SetupWindow(HINSTANCE hInstance); MxResult SetupWindow(HINSTANCE hInstance);

View File

@ -4,6 +4,14 @@
class MxRect32 class MxRect32
{ {
public: public:
MxRect32(int p_left, int p_top, int p_right, int p_bottom)
{
this->m_left = p_left;
this->m_top = p_top;
this->m_right = p_right;
this->m_bottom = p_bottom;
}
int m_left; int m_left;
int m_top; int m_top;
int m_right; int m_right;

View File

@ -11,6 +11,60 @@ MxString::MxString()
this->m_length = 0; this->m_length = 0;
} }
// OFFSET: LEGO1 0x100ae2a0
MxString::MxString(const MxString &str)
{
this->m_length = str.m_length;
this->m_data = (char *)malloc(this->m_length + 1);
strcpy(this->m_data, str.m_data);
}
// OFFSET: LEGO1 0x100ae350
MxString::MxString(const char *str)
{
if (str) {
this->m_length = strlen(str);
this->m_data = (char *)malloc(this->m_length + 1);
strcpy(this->m_data, str);
} else {
this->m_data = (char *)malloc(1);
this->m_data[0] = 0;
this->m_length = 0;
}
}
// OFFSET: LEGO1 0x100ae420
MxString::~MxString()
{
free(this->m_data);
}
// OFFSET: LEGO1 0x100ae490
void MxString::ToUpperCase()
{
strupr(this->m_data);
}
// OFFSET: LEGO1 0x100ae4a0
void MxString::ToLowerCase()
{
strlwr(this->m_data);
}
// OFFSET: LEGO1 0x100ae4b0
const MxString &MxString::operator=(MxString *param)
{
if (this->m_data != param->m_data)
{
free(this->m_data);
this->m_length = param->m_length;
this->m_data = (char *)malloc(this->m_length + 1);
strcpy(this->m_data, param->m_data);
}
return *this;
}
// TODO: this *mostly* matches, again weird with the comparison // TODO: this *mostly* matches, again weird with the comparison
// OFFSET: LEGO1 0x100ae510 // OFFSET: LEGO1 0x100ae510
const MxString &MxString::operator=(const char *param) const MxString &MxString::operator=(const char *param)
@ -25,9 +79,3 @@ const MxString &MxString::operator=(const char *param)
return *this; return *this;
} }
// OFFSET: LEGO1 0x100ae420
MxString::~MxString()
{
free(this->m_data);
}

View File

@ -11,6 +11,10 @@ class MxString : public MxCore
__declspec(dllexport) const MxString &operator=(const char *); __declspec(dllexport) const MxString &operator=(const char *);
MxString(); MxString();
MxString(const char *);
void ToUpperCase();
void ToLowerCase();
const MxString &operator=(MxString *);
private: private:
char *m_data; char *m_data;

View File

@ -10,17 +10,18 @@ long MxTimer::s_LastTimeTimerStarted = 0;
// OFFSET: LEGO1 0x100ae060 // OFFSET: LEGO1 0x100ae060
MxTimer::MxTimer() MxTimer::MxTimer()
{ {
this->m_isRunning = MX_FALSE; this->m_isRunning = MX_FALSE;
MxTimer::s_LastTimeCalculated = timeGetTime(); m_startTime = timeGetTime();
this->m_startTime = MxTimer::s_LastTimeCalculated; // yeah this is somehow what the asm is
s_LastTimeCalculated = m_startTime;
} }
// OFFSET: LEGO1 0x100ae160 // OFFSET: LEGO1 0x100ae160
void MxTimer::Start() void MxTimer::Start()
{ {
s_LastTimeTimerStarted = this->GetRealTime();
this->m_isRunning = MX_TRUE; this->m_isRunning = MX_TRUE;
MxTimer::s_LastTimeTimerStarted = timeGetTime();
} }
// OFFSET: LEGO1 0x100ae180 // OFFSET: LEGO1 0x100ae180

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse
from capstone import * from capstone import *
import difflib import difflib
import struct import struct
@ -7,60 +8,40 @@
import os import os
import sys import sys
def print_usage(): parser = argparse.ArgumentParser(allow_abbrev=False,
print('Usage: %s [options] <original-binary> <recompiled-binary> <recompiled-pdb> <decomp-dir>\n' % sys.argv[0]) description='Recompilation Compare: compare an original EXE with a recompiled EXE + PDB.')
print('\t-v, --verbose <offset>\t\t\tPrint assembly diff for specific function (original file\'s offset)') parser.add_argument('original', metavar='original-binary', help='The original binary')
print('\t-h, --html <output-file>\t\t\tGenerate searchable HTML summary of status and diffs') parser.add_argument('recompiled', metavar='recompiled-binary', help='The recompiled binary')
sys.exit(1) parser.add_argument('pdb', metavar='recompiled-pdb', help='The PDB of the recompiled binary')
parser.add_argument('decomp_dir', metavar='decomp-dir', help='The decompiled source tree')
parser.add_argument('--verbose', '-v', metavar='offset', help='Print assembly diff for specific function (original file\'s offset)')
parser.add_argument('--html', '-H', metavar='output-file', help='Generate searchable HTML summary of status and diffs')
args = parser.parse_args()
positional_args = []
verbose = None verbose = None
skip = False if args.verbose:
html = None try:
verbose = int(args.verbose, 16)
except ValueError:
parser.error('invalid verbose argument')
html = args.html
for i, arg in enumerate(sys.argv): original = args.original
if skip:
skip = False
continue
if arg.startswith('-'):
# A flag rather than a positional arg
flag = arg[1:]
if flag == 'v' or flag == '-verbose':
verbose = int(sys.argv[i + 1], 16)
skip = True
elif flag == 'h' or flag == '-html':
html = sys.argv[i + 1]
skip = True
else:
print('Unknown flag: %s' % arg)
print_usage()
else:
positional_args.append(arg)
if len(positional_args) != 5:
print_usage()
original = positional_args[1]
if not os.path.isfile(original): if not os.path.isfile(original):
print('Invalid input: Original binary does not exist') parser.error('Original binary does not exist')
sys.exit(1)
recomp = positional_args[2] recomp = args.recompiled
if not os.path.isfile(recomp): if not os.path.isfile(recomp):
print('Invalid input: Recompiled binary does not exist') parser.error('Recompiled binary does not exist')
sys.exit(1)
syms = positional_args[3] syms = args.pdb
if not os.path.isfile(syms): if not os.path.isfile(syms):
print('Invalid input: Symbols PDB does not exist') parser.error('Symbols PDB does not exist')
sys.exit(1)
source = positional_args[4] source = args.decomp_dir
if not os.path.isdir(source): if not os.path.isdir(source):
print('Invalid input: Source directory does not exist') parser.error('Source directory does not exist')
sys.exit(1)
# Declare a class that can automatically convert virtual executable addresses # Declare a class that can automatically convert virtual executable addresses
# to file addresses # to file addresses

View File

@ -67,6 +67,12 @@
#sortind { #sortind {
margin: 0 0.5em; margin: 0 0.5em;
} }
.filters {
font-size: 10pt;
text-align: center;
margin: 0.5em 0 1em 0;
}
</style> </style>
<script> <script>
var data = [/* INSERT DATA HERE */]; var data = [/* INSERT DATA HERE */];
@ -115,18 +121,25 @@
} }
} }
function filter(text) { const filterOptions = { text: '', hidePerfect: false };
function filter() {
closeAllDiffs(); closeAllDiffs();
var ltext = text.toLowerCase(); var ltext = filterOptions.text.toLowerCase();
const collection = document.getElementsByClassName("funcrow"); const collection = document.getElementsByClassName("funcrow");
var searchCount = 0; var searchCount = 0;
for (var ele of collection) { for (var ele of collection) {
var eledata = data[ele.dataset.index]; var eledata = data[ele.dataset.index];
if (text == ''
const textOk = (ltext == ''
|| eledata.address.toLowerCase().includes(ltext) || eledata.address.toLowerCase().includes(ltext)
|| eledata.name.toLowerCase().includes(ltext)) { || eledata.name.toLowerCase().includes(ltext));
const perfOk = (!filterOptions.hidePerfect || (eledata.matching < 1));
if (textOk && perfOk) {
ele.style.display = ''; ele.style.display = '';
searchCount++; searchCount++;
} else { } else {
@ -219,9 +232,16 @@
var search = document.getElementById('search'); var search = document.getElementById('search');
search.addEventListener('input', function (evt) { search.addEventListener('input', function (evt) {
filter(search.value); filterOptions.text = search.value;
filter();
}); });
const cbHidePerfect = document.getElementById('cbHidePerfect');
cbHidePerfect.addEventListener('change', evt => {
filterOptions.hidePerfect = evt.target.checked;
filter();
})
sortByColumn(0); sortByColumn(0);
}); });
</script> </script>
@ -230,8 +250,10 @@
<div class="main"> <div class="main">
<h1>Decompilation Status</h1> <h1>Decompilation Status</h1>
<input id="search" type="search" placeholder="Search for offset or function name..."> <input id="search" type="search" placeholder="Search for offset or function name...">
<br> <div class="filters">
<br> <label for="cbHidePerfect">Hide 100% match</label>
<input type="checkbox" id="cbHidePerfect" />
</div>
<table id="listing"> <table id="listing">
<tr id='listingheader'><th style='width: 20%'>Address</th><th style="width:60%">Name</th><th style='width: 20%'>Matching</th></tr> <tr id='listingheader'><th style='width: 20%'>Address</th><th style="width:60%">Name</th><th style='width: 20%'>Matching</th></tr>
</table> </table>