Created preliminary usage of all sections
Updated error system
This commit is contained in:
parent
6c8d2c464d
commit
ea8e76dae9
4 changed files with 197 additions and 142 deletions
|
@ -245,7 +245,7 @@ if __name__ == "__main__":
|
||||||
codeHandler.optimizeList = args.optimize
|
codeHandler.optimizeList = args.optimize
|
||||||
|
|
||||||
with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as kernelfile:
|
with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as kernelfile:
|
||||||
geckoKernel = KernelLoader(kernelfile)
|
geckoKernel = KernelLoader(kernelfile, parser)
|
||||||
|
|
||||||
if args.init is not None:
|
if args.init is not None:
|
||||||
geckoKernel.initAddress = int(args.init, 16)
|
geckoKernel.initAddress = int(args.init, 16)
|
||||||
|
@ -270,7 +270,7 @@ if __name__ == "__main__":
|
||||||
if not os.path.isdir(TMPDIR):
|
if not os.path.isdir(TMPDIR):
|
||||||
os.mkdir(TMPDIR)
|
os.mkdir(TMPDIR)
|
||||||
|
|
||||||
geckoKernel.build(parser, args.codelist, dolFile, codeHandler, TMPDIR, dest)
|
geckoKernel.build(args.codelist, dolFile, codeHandler, TMPDIR, dest)
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
58
kernel.py
58
kernel.py
|
@ -8,7 +8,7 @@ from io import BytesIO
|
||||||
|
|
||||||
import tools
|
import tools
|
||||||
from fileutils import *
|
from fileutils import *
|
||||||
from dolreader import DolFile
|
from dolreader import DolFile, SectionCountFullError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import chardet
|
import chardet
|
||||||
|
@ -176,9 +176,7 @@ class GCT(object):
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
self.codeList.seek(-8, 1)
|
self.codeList.seek(-8, 1)
|
||||||
length = GCT.determine_codelength(codetype, info)
|
length = GCT.determine_codelength(codetype, info)
|
||||||
while length > 0:
|
codelist += self.codeList.read(length)
|
||||||
codelist += self.codeList.read(1)
|
|
||||||
length -= 1
|
|
||||||
|
|
||||||
self.codeList = BytesIO(codelist)
|
self.codeList = BytesIO(codelist)
|
||||||
self.size = len(self.codeList.getbuffer())
|
self.size = len(self.codeList.getbuffer())
|
||||||
|
@ -220,7 +218,7 @@ class CodeHandler(object):
|
||||||
|
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
|
|
||||||
def gecko_parser(self, geckoText) -> str:
|
def gecko_self(self, geckoText) -> str:
|
||||||
with open(r'{}'.format(geckoText), 'rb') as gecko:
|
with open(r'{}'.format(geckoText), 'rb') as gecko:
|
||||||
result = chardet.detect(gecko.read())
|
result = chardet.detect(gecko.read())
|
||||||
encodeType = result['encoding']
|
encodeType = result['encoding']
|
||||||
|
@ -315,12 +313,13 @@ class CodeHandler(object):
|
||||||
|
|
||||||
class KernelLoader(object):
|
class KernelLoader(object):
|
||||||
|
|
||||||
def __init__(self, f):
|
def __init__(self, f, cli: tools.CommandLineParser=None):
|
||||||
self._rawData = BytesIO(f.read())
|
self._rawData = BytesIO(f.read())
|
||||||
self._initDataList = None
|
self._initDataList = None
|
||||||
self._gpModDataList = None
|
self._gpModDataList = None
|
||||||
self._gpDiscDataList = None
|
self._gpDiscDataList = None
|
||||||
self._gpKeyAddrList = None
|
self._gpKeyAddrList = None
|
||||||
|
self._cli = cli
|
||||||
self.patchJob = None
|
self.patchJob = None
|
||||||
self.initAddress = None
|
self.initAddress = None
|
||||||
self.protect = False
|
self.protect = False
|
||||||
|
@ -328,6 +327,14 @@ class KernelLoader(object):
|
||||||
self.quiet = False
|
self.quiet = False
|
||||||
self.encrypt = False
|
self.encrypt = False
|
||||||
|
|
||||||
|
def error(self, msg: str, exit=True):
|
||||||
|
if self._cli is not None:
|
||||||
|
self._cli.error(msg, exit)
|
||||||
|
else:
|
||||||
|
print(msg)
|
||||||
|
if exit:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
def set_variables(self, entryPoint: list, baseOffset: int=0):
|
def set_variables(self, entryPoint: list, baseOffset: int=0):
|
||||||
self._rawData.seek(0)
|
self._rawData.seek(0)
|
||||||
|
|
||||||
|
@ -425,7 +432,14 @@ class KernelLoader(object):
|
||||||
self._rawData.seek(0)
|
self._rawData.seek(0)
|
||||||
_kernelData = self._rawData.getvalue()
|
_kernelData = self._rawData.getvalue()
|
||||||
|
|
||||||
dolFile.append_text_sections([(_kernelData, self.initAddress)])
|
try:
|
||||||
|
dolFile.append_text_sections([(_kernelData, self.initAddress)])
|
||||||
|
except SectionCountFullError:
|
||||||
|
try:
|
||||||
|
dolFile.append_data_sections([(_kernelData, self.initAddress)])
|
||||||
|
except SectionCountFullError:
|
||||||
|
self.error(tools.color_text('There are no unused sections left for GeckoLoader to use!', defaultColor=tools.TREDLIT))
|
||||||
|
|
||||||
dolFile.entryPoint = self.initAddress
|
dolFile.entryPoint = self.initAddress
|
||||||
|
|
||||||
def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile):
|
def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile):
|
||||||
|
@ -434,7 +448,13 @@ class KernelLoader(object):
|
||||||
|
|
||||||
_handlerData = codeHandler._rawData.getvalue() + codeHandler.geckoCodes.codeList.getvalue()
|
_handlerData = codeHandler._rawData.getvalue() + codeHandler.geckoCodes.codeList.getvalue()
|
||||||
|
|
||||||
dolFile.append_text_sections([(_handlerData, codeHandler.initAddress)])
|
try:
|
||||||
|
dolFile.append_text_sections([(_handlerData, codeHandler.initAddress)])
|
||||||
|
except SectionCountFullError:
|
||||||
|
try:
|
||||||
|
dolFile.append_data_sections([(_handlerData, codeHandler.initAddress)])
|
||||||
|
except SectionCountFullError:
|
||||||
|
self.error(tools.color_text('There are no unused sections left for GeckoLoader to use!', defaultColor=tools.TREDLIT))
|
||||||
|
|
||||||
def protect_game(self, codeHandler: CodeHandler):
|
def protect_game(self, codeHandler: CodeHandler):
|
||||||
_oldpos = codeHandler.geckoCodes.codeList.tell()
|
_oldpos = codeHandler.geckoCodes.codeList.tell()
|
||||||
|
@ -473,11 +493,11 @@ class KernelLoader(object):
|
||||||
codeHandler.geckoCodes.codeList.seek(_oldpos)
|
codeHandler.geckoCodes.codeList.seek(_oldpos)
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def build(self, parser: tools.CommandLineParser, gctFile, dolFile: DolFile, codeHandler: CodeHandler, tmpdir, dump):
|
def build(self, gctFile, dolFile: DolFile, codeHandler: CodeHandler, tmpdir, dump):
|
||||||
with open(dump, 'wb+') as final:
|
with open(dump, 'wb+') as final:
|
||||||
|
|
||||||
if dolFile.get_full_size() < 0x100:
|
if dolFile.get_full_size() < 0x100:
|
||||||
parser.error(tools.color_text('DOL header is corrupted. Please provide a clean file\n', defaultColor=tools.TREDLIT), exit=False)
|
self.error(tools.color_text('DOL header is corrupted. Please provide a clean file\n', defaultColor=tools.TREDLIT), exit=False)
|
||||||
return
|
return
|
||||||
|
|
||||||
oldStart = dolFile.entryPoint
|
oldStart = dolFile.entryPoint
|
||||||
|
@ -489,7 +509,7 @@ class KernelLoader(object):
|
||||||
if '.' in gctFile:
|
if '.' in gctFile:
|
||||||
if os.path.splitext(gctFile)[1].lower() == '.txt':
|
if os.path.splitext(gctFile)[1].lower() == '.txt':
|
||||||
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
|
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
|
||||||
temp.write(bytes.fromhex('00D0C0DE'*2 + codeHandler.gecko_parser(gctFile) + 'F000000000000000'))
|
temp.write(bytes.fromhex('00D0C0DE'*2 + codeHandler.gecko_self(gctFile) + 'F000000000000000'))
|
||||||
temp.seek(0)
|
temp.seek(0)
|
||||||
codeHandler.geckoCodes = GCT(temp)
|
codeHandler.geckoCodes = GCT(temp)
|
||||||
foundData = True
|
foundData = True
|
||||||
|
@ -505,7 +525,7 @@ class KernelLoader(object):
|
||||||
for file in os.listdir(gctFile):
|
for file in os.listdir(gctFile):
|
||||||
if os.path.isfile(os.path.join(gctFile, file)):
|
if os.path.isfile(os.path.join(gctFile, file)):
|
||||||
if os.path.splitext(file)[1].lower() == '.txt':
|
if os.path.splitext(file)[1].lower() == '.txt':
|
||||||
temp.write(bytes.fromhex(codeHandler.gecko_parser(os.path.join(gctFile, file))))
|
temp.write(bytes.fromhex(codeHandler.gecko_self(os.path.join(gctFile, file))))
|
||||||
foundData = True
|
foundData = True
|
||||||
elif os.path.splitext(file)[1].lower() == '.gct':
|
elif os.path.splitext(file)[1].lower() == '.gct':
|
||||||
with open(os.path.join(gctFile, file), 'rb') as gct:
|
with open(os.path.join(gctFile, file), 'rb') as gct:
|
||||||
|
@ -519,7 +539,7 @@ class KernelLoader(object):
|
||||||
codeHandler.geckoCodes = GCT(temp)
|
codeHandler.geckoCodes = GCT(temp)
|
||||||
|
|
||||||
if not foundData:
|
if not foundData:
|
||||||
parser.error(tools.color_text('No valid gecko code file found\n', defaultColor=tools.TREDLIT), exit=False)
|
self.error(tools.color_text('No valid gecko code file found\n', defaultColor=tools.TREDLIT), exit=False)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.protect and self.patchJob == "ARENA":
|
if self.protect and self.patchJob == "ARENA":
|
||||||
|
@ -563,9 +583,9 @@ class KernelLoader(object):
|
||||||
legacy = False
|
legacy = False
|
||||||
|
|
||||||
if not hooked:
|
if not hooked:
|
||||||
parser.error(tools.color_text('Failed to find a hook address. Try using option --codehook to use your own address\n', defaultColor=tools.TREDLIT))
|
self.error(tools.color_text('Failed to find a hook address. Try using option --codehook to use your own address\n', defaultColor=tools.TREDLIT))
|
||||||
elif codeHandler.allocation < codeHandler.geckoCodes.size:
|
elif codeHandler.allocation < codeHandler.geckoCodes.size:
|
||||||
parser.error(tools.color_text('\n :: Error: Allocated codespace was smaller than the given codelist.\n', defaultColor=tools.TYELLOW))
|
self.error(tools.color_text('\n :: Error: Allocated codespace was smaller than the given codelist.\n', defaultColor=tools.TYELLOW))
|
||||||
|
|
||||||
dolFile.save(final)
|
dolFile.save(final)
|
||||||
|
|
||||||
|
@ -582,13 +602,13 @@ class KernelLoader(object):
|
||||||
print('')
|
print('')
|
||||||
if legacy == False:
|
if legacy == False:
|
||||||
info = [f' :: Start of game modified to address 0x{self.initAddress:X}',
|
info = [f' :: Start of game modified to address 0x{self.initAddress:X}',
|
||||||
f' :: Game function "__init_registers()" located at address 0x{oldStart:X}',
|
f' :: Game function "__start()" located at address 0x{oldStart:X}',
|
||||||
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
||||||
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
||||||
f' :: Codehandler is of type "{codeHandler.type}"',
|
f' :: Codehandler is of type "{codeHandler.type}"',
|
||||||
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
||||||
else:
|
else:
|
||||||
info = [f' :: Game function "__init_registers()" located at address 0x{oldStart:X}',
|
info = [f' :: Game function "__start()" located at address 0x{oldStart:X}',
|
||||||
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
||||||
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
||||||
f' :: Codehandler is of type "{codeHandler.type}"',
|
f' :: Codehandler is of type "{codeHandler.type}"',
|
||||||
|
@ -611,7 +631,7 @@ def resource_path(relative_path: str):
|
||||||
return os.path.join(base_path, relative_path)
|
return os.path.join(base_path, relative_path)
|
||||||
|
|
||||||
def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False):
|
def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False):
|
||||||
if codeHandler.hookAddress == None:
|
if codeHandler.hookAddress is None:
|
||||||
if not assert_code_hook(dolFile, codeHandler):
|
if not assert_code_hook(dolFile, codeHandler):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -665,7 +685,7 @@ def insert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, address: int):
|
||||||
ppc = read_uint32(dolFile)
|
ppc = read_uint32(dolFile)
|
||||||
|
|
||||||
if ((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x48:
|
if ((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x48:
|
||||||
raise NotImplementedError(tools.color_text("Hooking the codehandler to a conditional non spr branch is unsupported", defaultColor=tools.TREDLIT))
|
raise NotImplementedError(tools.color_text('Hooking the codehandler to a conditional non spr branch is unsupported', defaultColor=tools.TREDLIT))
|
||||||
|
|
||||||
dolFile.seek(-4, 1)
|
dolFile.seek(-4, 1)
|
||||||
dolFile.insert_branch(codeHandler.startAddress, address, lk=0)
|
dolFile.insert_branch(codeHandler.startAddress, address, lk=0)
|
273
loader.cpp
273
loader.cpp
|
@ -34,31 +34,32 @@ using f64 = double;
|
||||||
|
|
||||||
__attribute__((noreturn)) int main();
|
__attribute__((noreturn)) int main();
|
||||||
|
|
||||||
struct CodeList {
|
struct CodeList
|
||||||
|
{
|
||||||
|
|
||||||
u16 mBaseASM;
|
u16 mBaseASM;
|
||||||
u16 mUpperBase;
|
u16 mUpperBase;
|
||||||
u16 mOffsetASM;
|
u16 mOffsetASM;
|
||||||
u16 mLowerOffset;
|
u16 mLowerOffset;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Info {
|
struct Info
|
||||||
|
{
|
||||||
|
|
||||||
const u32 allocsize;
|
const u32 allocsize;
|
||||||
const u32 loaderSize;
|
const u32 loaderSize;
|
||||||
const u32 handlerSize;
|
const u32 handlerSize;
|
||||||
const u32 codeSize;
|
const u32 codeSize;
|
||||||
const u32* codehandlerHook;
|
const u32 *codehandlerHook;
|
||||||
const u32 crypted;
|
const u32 crypted;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiscHeader {
|
class DiscHeader
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum class TVMODE
|
||||||
enum class TVMODE {
|
{
|
||||||
NTSC,
|
NTSC,
|
||||||
PAL,
|
PAL,
|
||||||
DEBUG,
|
DEBUG,
|
||||||
|
@ -66,58 +67,64 @@ public:
|
||||||
MPAL,
|
MPAL,
|
||||||
PAL60
|
PAL60
|
||||||
};
|
};
|
||||||
struct MetaData {
|
|
||||||
|
|
||||||
const u8 mDiscID; //0x0000
|
struct MetaData
|
||||||
const u16 mGameCode; //0x0001
|
{
|
||||||
const u8 mRegionCode; //0x0003
|
|
||||||
const u16 mMakerCode; //0x0004
|
const u8 mDiscID; //0x0000
|
||||||
const u8 mDiscNumber; //0x0006
|
const u16 mGameCode; //0x0001
|
||||||
const u8 mDiscVersion; //0x0007
|
const u8 mRegionCode; //0x0003
|
||||||
const u8 mAudioStreaming; //0x0008
|
const u16 mMakerCode; //0x0004
|
||||||
const u8 mStreamBufferSize; //0x0009
|
const u8 mDiscNumber; //0x0006
|
||||||
const u8 _00[12]; //0x000A
|
const u8 mDiscVersion; //0x0007
|
||||||
const u32 mWiiMagic; //0x0018
|
const u8 mAudioStreaming; //0x0008
|
||||||
const u32 mGCNMagic; //0x001C
|
const u8 mStreamBufferSize; //0x0009
|
||||||
const u32 mNinBootCode; //0x0020
|
const u8 _00[12]; //0x000A
|
||||||
const u32 mAppVersion; //0x0024
|
const u32 mWiiMagic; //0x0018
|
||||||
const u32 mPhysicalRAMSize; //0x0028
|
const u32 mGCNMagic; //0x001C
|
||||||
const u32 mBoardModel; //0x002C
|
const u32 mNinBootCode; //0x0020
|
||||||
u8* mOSArenaLo; //0x0030
|
const u32 mAppVersion; //0x0024
|
||||||
u8* mOSArenaHi; //0x0034
|
const u32 mPhysicalRAMSize; //0x0028
|
||||||
u32* mFstStart; //0x0038
|
const u32 mBoardModel; //0x002C
|
||||||
u32 mFstSize; //0x003C
|
u8 *mOSArenaLo; //0x0030
|
||||||
u32 mDebuggerPresent; //0x0040
|
u8 *mOSArenaHi; //0x0034
|
||||||
|
u32 *mFstStart; //0x0038
|
||||||
|
u32 mFstSize; //0x003C
|
||||||
|
u32 mDebuggerPresent; //0x0040
|
||||||
const u32 mDebuggerExceptionMask; //0x0044
|
const u32 mDebuggerExceptionMask; //0x0044
|
||||||
void* mExceptionHookDest; //0x0048
|
void *mExceptionHookDest; //0x0048
|
||||||
const u32 mExceptionReturn; //0x004C
|
const u32 mExceptionReturn; //0x004C
|
||||||
u32 _01[0x10 / 4]; //0x0050
|
u32 _01[0x10 / 4]; //0x0050
|
||||||
u32 mDebuggerHook[0x24 / 4]; //0x0060
|
u32 mDebuggerHook[0x24 / 4]; //0x0060
|
||||||
u32 _02[0x3C / 4]; //0x0084
|
u32 _02[0x3C / 4]; //0x0084
|
||||||
u32 mCurrentOSContext; //0x00C0
|
u32 mCurrentOSContext; //0x00C0
|
||||||
u32 mPreviousOSMask; //0x00C4
|
u32 mPreviousOSMask; //0x00C4
|
||||||
u32 mCurrentOSMask; //0x00C8
|
u32 mCurrentOSMask; //0x00C8
|
||||||
TVMODE mTVMode; //0x00CC
|
DiscHeader::TVMODE mTVMode; //0x00CC
|
||||||
u32 mARAMSize; //0x00D0
|
u32 mARAMSize; //0x00D0
|
||||||
void* mCurOSContextLogical; //0x00D4
|
void *mCurOSContextLogical; //0x00D4
|
||||||
void* mDefaultOSThreadLogical; //0x00D8
|
void *mDefaultOSThreadLogical; //0x00D8
|
||||||
u32* mThreadQueueHead; //0x00DC
|
u32 *mThreadQueueHead; //0x00DC
|
||||||
u32* mThreadQueueTail; //0x00E0
|
u32 *mThreadQueueTail; //0x00E0
|
||||||
u32* mCurrentOSThread; //0x00E4
|
u32 *mCurrentOSThread; //0x00E4
|
||||||
u32 mDebuggerSize; //0x00E8
|
u32 mDebuggerSize; //0x00E8
|
||||||
u32* mDebuggerMonitorLoc; //0x00EC
|
u32 *mDebuggerMonitorLoc; //0x00EC
|
||||||
u32 mSimulatedMemSize; //0x00F0
|
u32 mSimulatedMemSize; //0x00F0
|
||||||
u8* mBi2HeaderLoc; //0x00F4
|
u8 *mBi2HeaderLoc; //0x00F4
|
||||||
u32 mBusClockSpeed; //0x00F8
|
u32 mBusClockSpeed; //0x00F8
|
||||||
u32 mCPUClockSpeed; //00x00FC
|
u32 mCPUClockSpeed; //0x00FC
|
||||||
u32 _04[0x3010 / 4];
|
u32 _04[0x3010 / 4]; //0x0100
|
||||||
u8* mWiiHeap; //0x3110
|
u8 *mWiiHeap; //0x3110
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static MetaData sMetaData;
|
static MetaData sMetaData;
|
||||||
|
|
||||||
enum class CONSOLETYPE { Gamecube, Wii, Unknown };
|
enum class CONSOLETYPE
|
||||||
|
{
|
||||||
|
Gamecube,
|
||||||
|
Wii,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
inline u32 getGameID() { return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | ((u32)sMetaData.mRegionCode); }
|
inline u32 getGameID() { return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | ((u32)sMetaData.mRegionCode); }
|
||||||
inline u16 getMakerID() { return sMetaData.mMakerCode; }
|
inline u16 getMakerID() { return sMetaData.mMakerCode; }
|
||||||
|
@ -126,10 +133,12 @@ public:
|
||||||
|
|
||||||
CONSOLETYPE detectHomeConsole()
|
CONSOLETYPE detectHomeConsole()
|
||||||
{
|
{
|
||||||
if (sMetaData.mGCNMagic) {
|
if (sMetaData.mGCNMagic)
|
||||||
|
{
|
||||||
return CONSOLETYPE::Gamecube;
|
return CONSOLETYPE::Gamecube;
|
||||||
}
|
}
|
||||||
else if (sMetaData.mWiiMagic) {
|
else if (sMetaData.mWiiMagic)
|
||||||
|
{
|
||||||
return CONSOLETYPE::Wii;
|
return CONSOLETYPE::Wii;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,15 +147,19 @@ public:
|
||||||
|
|
||||||
inline void setHeap(u32 alloc)
|
inline void setHeap(u32 alloc)
|
||||||
{
|
{
|
||||||
if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi) {
|
if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi)
|
||||||
|
{
|
||||||
sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc;
|
sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc;
|
||||||
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) {
|
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
|
||||||
|
{
|
||||||
sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc;
|
sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
|
sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
|
||||||
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) {
|
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
|
||||||
|
{
|
||||||
sMetaData.mWiiHeap -= alloc;
|
sMetaData.mWiiHeap -= alloc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,15 +168,16 @@ public:
|
||||||
|
|
||||||
static DiscHeader sDisc;
|
static DiscHeader sDisc;
|
||||||
Info gpModInfo = {
|
Info gpModInfo = {
|
||||||
0x48454150,
|
0x48454150,
|
||||||
0x4C53495A,
|
0x4C53495A,
|
||||||
0x4853495A,
|
0x4853495A,
|
||||||
0x4353495A,
|
0x4353495A,
|
||||||
(const u32*)0x484F4F4B,
|
(const u32 *)0x484F4F4B,
|
||||||
0x43525054,
|
0x43525054,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline u32 extractBranchAddr(u32* bAddr) {
|
inline u32 extractBranchAddr(u32 *bAddr)
|
||||||
|
{
|
||||||
s32 offset;
|
s32 offset;
|
||||||
if (*bAddr & 0x2000000)
|
if (*bAddr & 0x2000000)
|
||||||
offset = (*bAddr & 0x3FFFFFD) - 0x4000000;
|
offset = (*bAddr & 0x3FFFFFD) - 0x4000000;
|
||||||
|
@ -172,74 +186,82 @@ inline u32 extractBranchAddr(u32* bAddr) {
|
||||||
return (u32)bAddr + offset;
|
return (u32)bAddr + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Memory {
|
namespace Memory
|
||||||
|
{
|
||||||
|
|
||||||
static void memcpy(u8* to, u8* from, s32 size)
|
static void memcpy(u8 *to, u8 *from, s32 size)
|
||||||
{
|
{
|
||||||
for (s32 i = 0; i < size; ++i) {
|
for (s32 i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
*to++ = *from++;
|
*to++ = *from++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Cache {
|
namespace Cache
|
||||||
|
{
|
||||||
|
|
||||||
static inline void flushAddr(void* addr)
|
static inline void flushAddr(void *addr)
|
||||||
{
|
{
|
||||||
dcbf(addr);
|
dcbf(addr);
|
||||||
icbi(addr);
|
icbi(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flushRange(u8* addr, s32 size)
|
static void flushRange(u8 *addr, s32 size)
|
||||||
{
|
{
|
||||||
size += 31 + (((u32)addr & 31) > 0);
|
size += 31 + (((u32)addr & 31) > 0);
|
||||||
|
|
||||||
for (u32 i = 0; i < (size >> 5); ++i) {
|
for (u32 i = 0; i < (size >> 5); ++i)
|
||||||
flushAddr((void*)(addr + (i << 5)));
|
{
|
||||||
|
flushAddr((void *)(addr + (i << 5)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void storeAddr(void* addr)
|
static void storeAddr(void *addr)
|
||||||
{
|
{
|
||||||
dcbst(addr);
|
dcbst(addr);
|
||||||
icbi(addr);
|
icbi(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void storeRange(u8* addr, s32 size)
|
static void storeRange(u8 *addr, s32 size)
|
||||||
{
|
{
|
||||||
size += 31 + (((u32)addr & 31) > 0);
|
size += 31 + (((u32)addr & 31) > 0);
|
||||||
|
|
||||||
for (u32 i = 0; i < (size >> 5); ++i) {
|
for (u32 i = 0; i < (size >> 5); ++i)
|
||||||
storeAddr((void*)(addr + (i << 5)));
|
{
|
||||||
|
storeAddr((void *)(addr + (i << 5)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace Cache
|
||||||
|
|
||||||
namespace Direct {
|
namespace Direct
|
||||||
|
{
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline void write(T* addr, T value)
|
static inline void write(T *addr, T value)
|
||||||
{
|
{
|
||||||
*addr = value;
|
*addr = value;
|
||||||
Cache::flushAddr(addr);
|
Cache::flushAddr(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/
|
/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/
|
||||||
static inline void branch(void* addr, void* to, bool lk)
|
static inline void branch(void *addr, void *to, bool lk)
|
||||||
{
|
{
|
||||||
Direct::write<u32>((u32*)(addr), ((((u32)(to)-(u32)(addr)) & 0x3ffffff) | 0x48000000 | lk));
|
Direct::write<u32>((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | 0x48000000 | lk));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace Direct
|
||||||
|
|
||||||
namespace Search {
|
namespace Search
|
||||||
|
{
|
||||||
|
|
||||||
static u32* array(u32* start, u32* end, u32 arrayLength, const u32* hookData)
|
static u32 *array(u32 *start, u32 *end, u32 arrayLength, const u32 *hookData)
|
||||||
{
|
{
|
||||||
u32 index = 0;
|
u32 index = 0;
|
||||||
|
|
||||||
/*Loop through the games RAM, make sure we don't find our own hook data by accident*/
|
/*Loop through the games RAM, make sure we don't find our own hook data by accident*/
|
||||||
for (u32 i = 0; &start[i] < end; ++i) {
|
for (u32 i = 0; &start[i] < end; ++i)
|
||||||
|
{
|
||||||
/*If the data matches, increase the index counter and continue search,
|
/*If the data matches, increase the index counter and continue search,
|
||||||
else set index to 0 and continue searching*/
|
else set index to 0 and continue searching*/
|
||||||
if (start[i] == hookData[index])
|
if (start[i] == hookData[index])
|
||||||
|
@ -248,17 +270,19 @@ namespace Memory {
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|
||||||
/*If the data has matched the whole array, return the address of the match*/
|
/*If the data has matched the whole array, return the address of the match*/
|
||||||
if (index >= (arrayLength) && (&start[i] < (u32*)&gpModInfo || &start[i] > (u32*) & gpModInfo + sizeof(Info)))
|
if (index >= (arrayLength) && (&start[i] < (u32 *)&gpModInfo || &start[i] > (u32 *)&gpModInfo + sizeof(Info)))
|
||||||
return &start[i];
|
return &start[i];
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static T* single(T* start, T* end, T match)
|
static T *single(T *start, T *end, T match)
|
||||||
{
|
{
|
||||||
for (u32 i = 0; &start[i] < end; ++i) {
|
for (u32 i = 0; &start[i] < end; ++i)
|
||||||
if (start[i] == match) {
|
{
|
||||||
|
if (start[i] == match)
|
||||||
|
{
|
||||||
return &start[i];
|
return &start[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,14 +291,15 @@ namespace Memory {
|
||||||
|
|
||||||
/*Call this after viHook, finds the address of the first instance
|
/*Call this after viHook, finds the address of the first instance
|
||||||
of targetVal, and hooks it to the pointer hookTo*/
|
of targetVal, and hooks it to the pointer hookTo*/
|
||||||
static inline void hookFunction(u32* start, u32 targetVal, void* hookTo, bool lk)
|
static inline void hookFunction(u32 *start, u32 targetVal, void *hookTo, bool lk)
|
||||||
{
|
{
|
||||||
Direct::branch(Search::single<u32>(start, start + 0x500, targetVal), hookTo, lk);
|
Direct::branch(Search::single<u32>(start, start + 0x500, targetVal), hookTo, lk);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace Search
|
||||||
|
|
||||||
class Crypt {
|
class Crypt
|
||||||
|
{
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u32 key;
|
u32 key;
|
||||||
|
@ -309,74 +334,84 @@ namespace Memory {
|
||||||
this->key = key;
|
this->key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void xorCrypt(u32* dest, u32* buffer, u32 size)
|
inline void xorCrypt(u32 *dest, u32 *buffer, u32 size)
|
||||||
{
|
{
|
||||||
auto key = this->getKey();
|
auto key = this->getKey();
|
||||||
|
|
||||||
for (u32 i = 0; i < size; ++i) {
|
for (u32 i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
dest[i] = buffer[i] ^ key;
|
dest[i] = buffer[i] ^ key;
|
||||||
key += i << 3;
|
key += i << 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Space : u32 { Start = 0x80000000, End = 0x81800000, Size = 0x1800000 };
|
enum class Space : u32
|
||||||
|
{
|
||||||
|
Start = 0x80000000,
|
||||||
|
End = 0x81800000,
|
||||||
|
Size = 0x1800000
|
||||||
|
};
|
||||||
|
|
||||||
}
|
} // namespace Memory
|
||||||
|
|
||||||
Memory::Crypt gpCryptor = { 0x43595054 };
|
Memory::Crypt gpCryptor = {0x43595054};
|
||||||
|
|
||||||
static void initMods()
|
static void initMods()
|
||||||
{
|
{
|
||||||
sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/
|
sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/
|
||||||
|
|
||||||
/*Change codelist pointer to the new address in the allocation*/
|
/*Change codelist pointer to the new address in the allocation*/
|
||||||
CodeList* codelistPointer = (CodeList*)((u32)&gpModInfo + sizeof(Info) + 0xFC);
|
CodeList *codelistPointer = (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC);
|
||||||
codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF;
|
codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF;
|
||||||
codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF;
|
codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF;
|
||||||
|
|
||||||
/*Copy codelist to the new allocation*/
|
/*Copy codelist to the new allocation*/
|
||||||
if (gpModInfo.crypted) {
|
if (gpModInfo.crypted)
|
||||||
Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8*)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize);
|
{
|
||||||
gpCryptor.xorCrypt((u32*)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32*)((u8*)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2);
|
Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize);
|
||||||
|
gpCryptor.xorCrypt((u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8*)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize);
|
{
|
||||||
|
Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Get codehandler hook resources*/
|
/*Get codehandler hook resources*/
|
||||||
auto fillInField = Memory::Search::single<u32>((u32*)sDisc.sMetaData.mOSArenaHi, (u32*)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE);
|
auto fillInField = Memory::Search::single<u32>((u32 *)sDisc.sMetaData.mOSArenaHi, (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE);
|
||||||
auto returnAddress = extractBranchAddr((u32*)gpModInfo.codehandlerHook);
|
auto returnAddress = extractBranchAddr((u32 *)gpModInfo.codehandlerHook);
|
||||||
auto ppc = *gpModInfo.codehandlerHook;
|
auto ppc = *gpModInfo.codehandlerHook;
|
||||||
|
|
||||||
|
|
||||||
/*Write hook branch*/
|
/*Write hook branch*/
|
||||||
Memory::Direct::branch((void*)gpModInfo.codehandlerHook, (void*)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); //entryhook
|
Memory::Direct::branch((void *)gpModInfo.codehandlerHook, (void *)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); //entryhook
|
||||||
|
|
||||||
/*Temporary nop*/
|
/*Temporary nop*/
|
||||||
*fillInField = 0x60000000;
|
*fillInField = 0x60000000;
|
||||||
|
|
||||||
/*Flush the cache so that the instructions update*/
|
/*Flush the cache so that the instructions update*/
|
||||||
Memory::Cache::flushRange((u8*)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize);
|
Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize);
|
||||||
|
|
||||||
/*Call the codehandler*/
|
/*Call the codehandler*/
|
||||||
call((void*)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))();
|
call((void *)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))();
|
||||||
|
|
||||||
/*Write original instruction or translate offset data if a branch*/
|
/*Write original instruction or translate offset data if a branch*/
|
||||||
if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C) {
|
if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C)
|
||||||
Memory::Direct::branch((void*)fillInField, (void*)returnAddress, ppc & 1);
|
{
|
||||||
} else {
|
Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Memory::Direct::write(fillInField, ppc);
|
Memory::Direct::write(fillInField, ppc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Write branch back to the hook address + 4*/
|
/*Write branch back to the hook address + 4*/
|
||||||
Memory::Direct::branch((void*)&fillInField[1], (void*)(&gpModInfo.codehandlerHook[1]), false); //return
|
Memory::Direct::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); //return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
if (sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown) {
|
if (sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown)
|
||||||
|
{
|
||||||
initMods();
|
initMods();
|
||||||
}
|
}
|
||||||
__start();
|
__start();
|
||||||
|
|
|
@ -9,13 +9,13 @@ class Updater(object):
|
||||||
self.gitReleases = 'https://github.com/{}/{}/releases'
|
self.gitReleases = 'https://github.com/{}/{}/releases'
|
||||||
|
|
||||||
def request_release_data(self):
|
def request_release_data(self):
|
||||||
'''Returns "soup" data of the repository releases tab'''
|
""" Returns soup data of the repository releases tab """
|
||||||
with request.urlopen(self.gitReleases.format(self.owner, self.repo)) as response:
|
with request.urlopen(self.gitReleases.format(self.owner, self.repo)) as response:
|
||||||
html = response.read()
|
html = response.read()
|
||||||
return html
|
return html
|
||||||
|
|
||||||
def get_newest_version(self) -> str:
|
def get_newest_version(self) -> str:
|
||||||
'''Returns newest release version'''
|
""" Returns newest release version """
|
||||||
try:
|
try:
|
||||||
response = self.request_release_data()
|
response = self.request_release_data()
|
||||||
soup = BeautifulSoup(response, 'html.parser')
|
soup = BeautifulSoup(response, 'html.parser')
|
||||||
|
|
Reference in a new issue