diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc28b45..bd1f147 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: [push] jobs: Wii: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest timeout-minutes: 10 container: @@ -19,7 +19,7 @@ jobs: - name: Install BrainSlug run: | - git clone https://github.com/Chadderz121/brainslug-wii + git clone -b atof https://github.com/InvoxiPlayGames/brainslug-wii.git cd brainslug-wii make install cd .. @@ -30,7 +30,7 @@ jobs: make wii -j2 DEBUG=1 - name: Upload binaries (debug) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: RB3Enhanced-Wii-Debug path: out/RB3Enhanced.mod @@ -41,14 +41,25 @@ jobs: make wii -j2 - name: Upload binaries (no-debug) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: RB3Enhanced-Wii-NoDebug path: out/RB3Enhanced.mod + - name: Compile (bank8-debug) + run: | + make clean + make wii -j2 DEBUG=1 BANK8=1 + + - name: Upload binaries (bank8-debug) + uses: actions/upload-artifact@v4 + with: + name: RB3Enhanced-Wii-Bank8-Debug + path: out/RB3Enhanced.mod + Xbox: if: github.repository == 'RBEnhanced/RB3Enhanced' - runs-on: windows-2019 + runs-on: windows-2022 timeout-minutes: 10 steps: @@ -72,7 +83,7 @@ jobs: make xbox -j2 DEBUG=1 - name: Upload binaries (debug) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: RB3Enhanced-Xbox-Debug path: out/RB3Enhanced.dll @@ -85,7 +96,7 @@ jobs: make xbox -j2 - name: Upload binaries (no-debug) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: RB3Enhanced-Xbox-NoDebug path: out/RB3Enhanced.dll diff --git a/.gitignore b/.gitignore index 9783ef0..46bc31a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,8 +11,17 @@ build_xbox/ # wii build artifacts build_wii/ *.mod +# ps3 build artifacts +build_ps3/ +*.sprx +*.prx +# gcc build artifacts *.elf *.map *.o # automatically generated files -version.h \ No newline at end of file +version.h +# OS artifacts +Thumbs.db +desktop.ini +.DS_Store diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 5ce7404..44f26ac 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,6 +9,7 @@ "defines": [ "_DEBUG", "_XBOX", + "_PPC_", "RB3E", "RB3E_XBOX", "RB3EDEBUG" @@ -35,6 +36,26 @@ "compilerPath": "C:/devkitPro/devkitPPC/bin/powerpc-eabi-gcc.exe", "cStandard": "c89", "intelliSenseMode": "gcc-x86" + }, + { + "name": "Wii (Bank 8)", + "includePath": [ + "${workspaceFolder}/include", + "C:/devkitPro/bslug/include" + ], + "defines": [ + "_DEBUG", + "GEKKO", + "HW_RVL", + "__wii__", + "RB3E", + "RB3E_WII", + "RB3E_WII_BANK8", + "RB3EDEBUG" + ], + "compilerPath": "C:/devkitPro/devkitPPC/bin/powerpc-eabi-gcc.exe", + "cStandard": "c89", + "intelliSenseMode": "gcc-x86" } ], "version": 4 diff --git a/BUILDING.md b/BUILDING.md index ae04e2c..e240e18 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,31 +1,42 @@ # Building RB3Enhanced -RB3Enhanced is a cross-platform mod for both Xbox 360 and Wii. It's designed to be compiled on a Windows system, however both versions are able to compile on macOS and Linux. +RB3Enhanced is a cross-platform mod for both Xbox 360 and Wii. It's designed to +be compiled on a Windows system, however both versions are able to compile on +macOS and Linux via Wine. ## Prerequisites -* [devkitPPC](https://devkitpro.org/wiki/Getting_Started) (for Wii compilation) -* [BrainSlug](https://github.com/Chadderz121/brainslug-wii/blob/master/INSTALLING) (for Wii compilation) +* [devkitPro with devkitPPC](https://devkitpro.org/wiki/Getting_Started) +* [InvoxiPlayGames/BrainSlug](https://github.com/InvoxiPlayGames/brainslug-wii/blob/master/INSTALLING) * Xbox 360 SDK, with build tools installed (for Xbox compilation) -* Make \*, \*\* +* Make \* -You do not need devkitPPC to compile the mod for Xbox, and you do not need the Xbox 360 SDK to compile the mod for Wii. +The environment variable `XEDK` must be set to the install directory for the +Xbox 360 SDK, and the environment variable `DEVKITPPC` must be set to the +install directory for devkitPPC. The installers for the respective toolchains +will automatically do this for you. -The environment variable `XEDK` must be set to the install directory for the Xbox 360 SDK, and the environment variable `DEVKITPPC` must be set to the install directory for devkitPPC. The installers for the respective toolchains will automatically do this for you. - -If compiling the Xbox 360 version under macOS or Linux, set the environment variable `WINDOWS_SHIM` to the path to your Wine executable. Note that some Wine versions may not work correctly with MSVC. You will also have to ensure `XEDK` is set to a path accessible under Wine, e.g. `Z:\Users\emma\xedk\`. - -*\* There is a build script provided as `make_xbox.bat` that will compile using only the Xbox 360 SDK. This is not recommended.* - -*\*\* On Windows, the only tested configuration is with Make installed via MSYS2. Any other configuration has not been tested, and may not work.* +If compiling the Xbox 360 version under macOS or Linux, set the environment +variable `WINDOWS_SHIM` to the path to your Wine executable. Note that some Wine +versions may not work correctly with MSVC. You will also have to ensure `XEDK` +is set to a path accessible under Wine, e.g. `Z:\Users\emma\xedk\`. +*\* On Windows, the only tested configuration is with Make installed via +devkitPro's MSYS2. Any other configuration has not been tested, and may not +work.* ## Compilation -Open a Terminal (or Command Prompt) window in the same folder you have cloned the source code into. +Open a Terminal (or Command Prompt) window in the same folder you have cloned +the source code into. -* To build for Xbox 360, type `make xbox`. A DLL file will be output at `out/RB3Enhanced.dll`, that can then be loaded with RB3ELoader or a modified Rock Band 3 XEX. -* To build for Wii, type `make wii`. A MOD file will be output at `out/RB3Enhanced.mod`, that can then be loaded with RB3ELoader or the BrainSlug channel. -* Typing `make` or `make all` will compile both. Adding `-jX` (where X is your processor's thread count) will speed up compliation times. +* To build for Xbox 360, type `make xbox`. A DLL file will be output at + `out/RB3Enhanced.dll`, that can then be loaded with the RB3ELoader Dashlaunch + plugin, or the Xenia patch. +* To build for Wii, type `make wii`. A MOD file will be output at + `out/RB3Enhanced.mod`, that can then be loaded with the RB3Enhanced launcher. +* Typing `make` or `make all` will compile both. Adding `-jX` (where X is your + processor's thread count) will speed up compliation times. -Adding `DEBUG=1` to the make command will build a version of RB3E that will output more debugging information to the logs. \ No newline at end of file +Adding `DEBUG=1` to the make command will build a version of RB3E that will +output more debugging information to the logs. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7bfdd1b..0276d1d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,24 +1,45 @@ # Contributing to RB3Enhanced -We welcome all contributions to help make RB3Enhanced a better mod, by improving existing features or adding new ones. +We welcome all contributions to help make RB3Enhanced a better mod, by improving +existing features or adding new ones. ## Codebase -* Visual Studio Code config files are included in the repository, so if you are using VSCode as your IDE, make sure it is following this file. -* For cross-platform compatibility reasons, the codebase aims to be C89 / ANSI C. Variables must be defined at the start of functions rather than inline. - * Wii-specific implementations (must be guarded by #ifdef RB3E_WII) may be compliant GNU C99. - * Xbox-specific implementations (must be guarded by #ifdef RB3E_XBOX) may be slightly non-compliant, as MSVC's C89 implementation is non-standard. +* Visual Studio Code config files are included in the repository, so if you are + using VSCode as your IDE, make sure it is following this file. +* For cross-platform compatibility reasons, the codebase aims to be C89/ANSI C. + Variables must be defined at the start of functions rather than inline. + * Xbox-specific implementations (must be guarded by `#ifdef RB3E_XBOX`) may + be slightly non-compliant, as MSVC's C89 implementation is non-standard. + * Wii or PS3-specific implementations (must be guarded by `#ifdef RB3E_WII` + and/or `#ifdef RB3E_PS3`) may be compliant GNU C99. * All indentations should be 4 spaces. * Try to adhere to any existing code styling already used in the codebase. -* Function addresses should be added to `include/ports.h` in the respective platform section. -* When adding a function reference (whether as a hook or as a callee), make sure to add a unique stub in `source/_functions.c` and definitions of the original function in a header file in `include/rb3`. -* Please make sure all your contributions are able to be licensed under the GPLv2 or later. This means not using non-free or incompatibly licensed libraries or external code. +* Function addresses should be added to the applicable + `include/ports_{platform}.h` header file. +* When adding a function reference (whether as a hook or as a callee), make sure + to add a unique stub in `source/_functions.c` and definitions of the original + function in a header file in `include/rb3`. +* Please make sure all your contributions are able to be licensed under the + GPLv2 or later. This means not using non-free or incompatibly licensed + libraries or external code. + * The output of LLMs (e.g. ChatGPT) is not compatible with the GPLv2. Please + don't use these to generate code when working on code in RB3Enhanced! ## Pull requests -* Test your changes before submitting them as a pull request, to ensure that they don't crash. - * Emulators may mask fault behaviour, or return different results compared to a real console, so it's advised to use real hardware if possible. -* If applicable, try to include port addresses for both Xbox 360 and Wii in your contribution. - * If you can't do this, guard the implementation behind #ifdef RB3E_{PLATFORM}. -* It's recommended that you enable "Allow contributors to edit code" in your pull request, so quick fixes can be added by us before merging. -* Try to keep a clean Git history. \ No newline at end of file +* Test your changes before submitting them as a pull request, to ensure that + they don't crash. + * Emulators may mask fault behaviour, or return different results compared + to a real console, so it's advised to use real hardware if possible. + * Let us know what platform you've tested on. +* If possible, try to include port addresses for Xbox 360, Wii, PS3 and Bank 8 + in your contribution. + * If you can't do this, guard the implementation behind + `#ifdef RB3E_{PLATFORM}`. +* It's recommended that you enable "Allow contributors to edit code" in your + pull request, so quick fixes can be added by us before merging. +* Try to keep a clean Git history. + * Don't make excessive commits and don't force push too often. + * If your PR hasn't been merged, try to keep it up to date with changes + in the `master` branch. diff --git a/Makefile b/Makefile index 5ec9070..d9d317b 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,11 @@ ifeq ($(strip $(DEBUG)),1) DEFINES += RB3EDEBUG endif +# if BANK8=1 then compile builds for bank 8 dev build +ifeq ($(strip $(BANK8)),1) + DEFINES += RB3E_WII_BANK8 +endif + # Wii variables # ================= # build directory for Wii compilation diff --git a/README.md b/README.md index 77b315e..3a0748d 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,43 @@ # RB3Enhanced -RB3Enhanced is a mod for Rock Band 3 that adds new features, fixes some flaws and overall makes it a nicer experience, compatible with Xbox 360 and Wii. _(Note: RB3Enhanced requires a modified console. It will not work on a retail console.)_ +RB3Enhanced is a mod for Rock Band 3 that adds new features, fixes some flaws +and overall makes it a nicer experience, compatible with Xbox 360 and Wii. +_(Note: RB3Enhanced requires a modified console. It will not work on a retail +console.)_ -For more information, check out https://rb3e.rbenhanced.rocks or join the [RBEnhanced Discord](https://discord.gg/6rRUWXPYwb). +For more information, check out https://rb3e.rbenhanced.rocks, join the +[RBEnhanced Discord](https://discord.gg/6rRUWXPYwb) or `#rbenhanced` on +irc.libera.chat. -If you want to contribute, check BUILDING.md and CONTRIBUTING.md for more information. +If you want to contribute, check BUILDING.md and CONTRIBUTING.md for more +information. ## Downloading -* **(Recommended)** For the most tested and stable version, download from https://rb3e.rbenhanced.rocks/download.html -* For the bleeding edge latest commit, download the latest debug build from https://nightly.link/RBEnhanced/RB3Enhanced/workflows/build/master +* **(Recommended)** For the most tested and stable version, download from + https://rb3e.rbenhanced.rocks/download.html +* For the bleeding edge latest commit, download the latest debug build from + https://nightly.link/RBEnhanced/RB3Enhanced/workflows/build/master * You will need files from the stable release to use debug builds. ## Features * Raised song limit, allowing you to have up to 8000 songs in your library. * Play custom songs on the latest update of Rock Band 3. -* Access unlockables such as keys on guitar and in-game cosmetic items, without having to grind. +* Unlock additional features such as keys on guitar and in-game cosmetic items. * Additional modifiers, like black backgrounds and gem colour shuffle. * Game origin icons in the setlist to see what game your songs came from. -* Fixes the infamous infinite loading bug on modified Corona consoles and development kits. -* Easy mod support by placing raw files in the game directory without repacking the archives. +* Support and integration with [Rock Band 3 Deluxe](https://rb3dx.milohax.org) * Online multiplayer without using Xbox Live or Nintendo WFC. -* Sending data from the game to another device, for stream overlays and status indicators. +* Integration with [GoCentral](https://github.com/ihatecompvir/GoCentral) * ...and more! ## License -RB3Enhanced is licensed under the GNU General Public License version 2 (or later, at your choice) as found in the LICENSE.txt file. All source files are subject to this license unless specified otherwise in the header. +RB3Enhanced is licensed under the GNU General Public License version 2 (or +later, at your choice) as found in the LICENSE.txt file. All source files are +subject to this license unless specified otherwise in the header. -RB3Enhanced makes use of the [inih library by Ben Hoyt](https://github.com/benhoyt/inih), under the 3-clause BSD license. +RB3Enhanced makes use of the +[inih library by Ben Hoyt](https://github.com/benhoyt/inih), under the 3-clause +BSD license. diff --git a/assets/rawfiles/xbox/ui/resource/list/gen/list_song_select_browser.milo_xbox b/assets/rawfiles/xbox/ui/resource/list/gen/list_song_select_browser.milo_xbox new file mode 100644 index 0000000..3630e0b Binary files /dev/null and b/assets/rawfiles/xbox/ui/resource/list/gen/list_song_select_browser.milo_xbox differ diff --git a/assets/rb3e_index.html b/assets/rb3e_index.html new file mode 100644 index 0000000..3060512 --- /dev/null +++ b/assets/rb3e_index.html @@ -0,0 +1,648 @@ + + + + + + RB3Enhanced + + + + +
+ + +
+ +
+
+ + + + + + + +
+
+ +
+ (Don't press this until you're in the Music Library!) +
+
+ + diff --git a/assets/wii_default_rb3.ini b/assets/wii_default_rb3.ini new file mode 100644 index 0000000..c09bab7 --- /dev/null +++ b/assets/wii_default_rb3.ini @@ -0,0 +1,47 @@ +[General] +# The multiplier to apply to the speed of the note highway. +# 1.0 = 100%, 1.2 = 120%, 1.5 = 150% +TrackSpeedMultiplier = 1.0 +# The multiplier to apply to the speed of the song audio. +# 1.0 = 100%, 1.2 = 120%, 1.5 = 150% +SongSpeedMultiplier = 1.0 +# The name of the venue to force. (e.g. "none" for black background) +# To disable this and use default venue selection, set this to "false" +ForcedVenue = false +# Whether to unlock all clothing and other unlockables by default. +UnlockClothing = false +# Whether to disable all menu background ambient music by default. +DisableMenuMusic = false +# The folder to check on the SD card for raw files. +# The default is "rb3", uncomment this line to change it +#RawfilesDir = rb3 + +[Events] +# Enables sending events (song start, etc) to a computer or other networked device. +EnableEvents = false +# The IP address to send these events to (set to 255.255.255.255 to broadcast to the entire local network.) +BroadcastTarget = 255.255.255.255 + +[Network] +# Enables either using NATPMP or PCP protocols to open port 9103 on your Wii for +# a more reliable online multiplayer experience. Only turn this off if you don't play +# online or if it causes issues. +EnableNATPMP=true + +[GoCentral] +# Enables redirecting Rock Central servers to a custom server. +EnableGoCentral = true +# The IP address of the GoCentral server. +GoCentralAddress = gocentral-wii.rbenhanced.rocks + +[Wii] +# The device authentication server to use for GoCentral. +NASServer = naswii.rbenhanced.rocks + +# Whether to disable RB3Enhanced's SD card support at boot. +# This will allow SD card DLC songs installed from the Wii Shop and C3 CON Tools to work, however +# it will also disable rawfiles and LavaManager songs. +# Remove the # from the start of this line to enable. +#LegacySDMode = true + +# End of rb3.ini diff --git a/assets/xbox_default_rb3.ini b/assets/xbox_default_rb3.ini new file mode 100644 index 0000000..2ded2f4 --- /dev/null +++ b/assets/xbox_default_rb3.ini @@ -0,0 +1,64 @@ +[General] +# The multiplier to apply to the speed of the note highway. +# 1.0 = 100%, 1.2 = 120%, 1.5 = 150% +TrackSpeedMultiplier = 1.0 +# The multiplier to apply to the speed of the song audio. +# 1.0 = 100%, 1.2 = 120%, 1.5 = 150% +SongSpeedMultiplier = 1.0 +# The name of the venue to force. (e.g. "none" for black background) +# To disable this and use default venue selection, set this to "false" +ForcedVenue = false +# Whether to enable game origin icons in the setlist menu. +# This requires the correct font files in the rawfiles directory. +GameOriginIcons = true +# Whether to unlock all clothing and other unlockables by default. +UnlockClothing = false +# Whether to disable all menu background ambient music by default. +DisableMenuMusic = false +# The folder to check on the Hard Drive and USB drives for raw files. +# The default is "rb3", remove the # at the start of this line to change it +#RawfilesDir = rb3 + +[Events] +# Enables sending events (song start, etc) to a computer or other networked device. +EnableEvents = false +# The IP address to send these events to (set to 255.255.255.255 to broadcast to the entire local network.) +BroadcastTarget = 255.255.255.255 + +[GoCentral] +# Enables redirecting Rock Central servers to a custom server. +# This also re-enables Audition Mode if enabled. +# (This setting is ignored if using an Xbox 360 and connected to official services.) +EnableGoCentral = true +# The IP address of the GoCentral server. +GoCentralAddress = gocentral-xbox.rbenhanced.rocks + +[Network] +# Enables either using NATPMP, PCP or UPnP protocols to open port 9103 on your Xbox for +# a more reliable online multiplayer experience. Only turn this off if you don't play +# online or if it causes issues. +EnableNATPMP=true +EnableUPnP=true + +[HTTP] +# Enables a website accessible on http://[your xbox ip]:21070/ from the same local network +# to search for and jump to songs. Only enable this on personal home Wi-Fi networks. +EnableHTTPServer=false + +[Xbox360] +# Enables playing online multiplayer without the need for Xbox Live. +# (This setting is ignored if using an Xbox 360 and connected to official services.) +EnableLiveless = true +# The RB3Enhanced Liveless Rooms server to use, to join others via room codes instead of direct IP connection. +LivelessAddress = liveless-testing.ipg.pw +# The IP to direct connect to when searching for a game. If hosting a game, set this to "127.0.0.1" +# It is recommended that you use Liveless Rooms instead. +DirectConnectIP = 127.0.0.1 +# The STUN server to contact to request your public IP address, for liveless multiplayer. (TCP required.) +# Remove the # from the start of these lines to enable the feature. +#STUNServer = stun.l.google.com +#STUNServerPort = 19302 +# Your external IPv4 address, for liveless multiplayer. Overrode by STUN, Liveless Rooms or UPnP, if enabled. +ExternalIP = 0.0.0.0 + +# End of rb3.ini diff --git a/include/DataDebug.h b/include/DataDebug.h new file mode 100644 index 0000000..48dd96b --- /dev/null +++ b/include/DataDebug.h @@ -0,0 +1,11 @@ +/* + RB3Enhanced - DataDebug.h + Hooks for debugging scripting errors, similar to debug. +*/ + +#include "rb3/Data.h" + +DataNode *DataSetHook(DataNode *ret, DataArray *array); +DataNode *DataSetElemHook(DataNode *ret, DataArray *array); +DataNode *DataOnElemHook(DataNode *ret, DataArray *array); +void *DataNodeGetObjHook(DataNode *node); diff --git a/include/FileSD.h b/include/FileSD.h new file mode 100644 index 0000000..e7b06d1 --- /dev/null +++ b/include/FileSD.h @@ -0,0 +1,25 @@ +#ifdef RB3E_WII + +#include +#include "rb3/File.h" + +typedef struct _FileSD +{ + File_vtable *vtable; // 0x0 + + char *mFilePath; + char mWriteEnabled; + char mFailed; + int mFd; + FILE_STRUCT mFileStruct; + int mFilesize; + int mCurrentPosition; + int mLastBytesRead; + int mLastBytesWrote; + +} FileSD; + +void FileSD_InitVtable(); +FileSD *FileSD_New(const char *filepath, int flags); + +#endif diff --git a/include/FileWiiNAND.h b/include/FileWiiNAND.h new file mode 100644 index 0000000..fd490cc --- /dev/null +++ b/include/FileWiiNAND.h @@ -0,0 +1,23 @@ +#ifdef RB3E_WII + +#include +#include "rb3/File.h" + +typedef struct _FileWiiNAND +{ + File_vtable *vtable; // 0x0 + + char *mFilePath; + char mWriteEnabled; + char mFailed; + ios_fd_t mFd; + int mFilesize; + int mCurrentPosition; + int mLastBytesRead; + int mLastBytesWrote; +} FileWiiNAND; + +void FileWiiNAND_InitVtable(); +FileWiiNAND *FileWiiNAND_New(const char *filepath); + +#endif diff --git a/include/GlobalSymbols.h b/include/GlobalSymbols.h index 787c727..b1196cb 100644 --- a/include/GlobalSymbols.h +++ b/include/GlobalSymbols.h @@ -9,8 +9,15 @@ void InitGlobalSymbols(); typedef struct _GlobalSymbols { + // rb3e version + Symbol buildtag; + Symbol commit; + // dta functions Symbol print_debug; + Symbol rb3e_api_version; + Symbol rb3e_build_tag; + Symbol rb3e_commit; Symbol rb3e_change_music_speed; Symbol rb3e_change_track_speed; Symbol rb3e_get_music_speed; @@ -20,6 +27,13 @@ typedef struct _GlobalSymbols Symbol rb3e_relaunch_game; Symbol rb3e_get_song_count; Symbol rb3e_send_event_string; + Symbol rb3e_get_song_name; + Symbol rb3e_get_artist; + Symbol rb3e_get_album; + Symbol rb3e_get_origin; + Symbol rb3e_get_genre; + Symbol rb3e_delete_songcache; + Symbol rb3e_local_ip; // modifiers Symbol forceHopos; @@ -27,6 +41,7 @@ typedef struct _GlobalSymbols Symbol mirrorMode; Symbol blackBackground; Symbol gemShuffle; + Symbol doubleBass; // gem widgets Symbol greenGem; diff --git a/include/MiloSceneHooks.h b/include/MiloSceneHooks.h index 67b889e..629698e 100644 --- a/include/MiloSceneHooks.h +++ b/include/MiloSceneHooks.h @@ -6,6 +6,9 @@ #include "rb3/BinStream.h" #include "rb3/DirLoader.h" #include "rb3/Object.h" +#include "rb3/Rnd/Transform.h" + // void DirLoaderOpenFileHook(DirLoader *thisDirLoader); -void LoadObj(Object *object, BinStream *stream); \ No newline at end of file +void LoadObj(Object *object, BinStream *stream); +void VertexReadHook(BinStream *thisBinStream, Vector3 *vec3); \ No newline at end of file diff --git a/include/QuazalHooks.h b/include/QuazalHooks.h index a546dc7..e419a05 100644 --- a/include/QuazalHooks.h +++ b/include/QuazalHooks.h @@ -4,9 +4,9 @@ Quazal is the creator of the networking middleware used by RB3. */ -#include "rb3/Quazal/Step.h" +#include "rb3/Quazal/StepSequenceJobStep.h" -extern void OperatorEqualsFmt(char *thisString, char *szString); -void OperatorEqualsFmtHook(char *thisString, char *szString); -extern int StepSequenceJobSetStep(int *thisStepSequenceJob, Step *oStep); -int StepSequenceJobSetStepHook(int *thisStepSequenceJob, Step *oStep); +extern void OperatorEqualsFmt(char *r3, char *r4); +void OperatorEqualsFmtHook(char *r3, char *r4); +extern int StepSequenceJobSetStep(int *unk, StepSequenceJobStep *step); +int StepSequenceJobSetStepHook(int *unk, StepSequenceJobStep *step); diff --git a/include/SetlistHooks.h b/include/SetlistHooks.h index 98dc84b..5bd5e00 100644 --- a/include/SetlistHooks.h +++ b/include/SetlistHooks.h @@ -13,4 +13,4 @@ void SetSongAndArtistNameHook(BandLabel *label, SortNode *sortNode); void SetSongNameFromNodeHook(BandLabel *label, SortNode *sortNode); RndMat *MusicLibraryMatHook(MusicLibrary *thisMusicLibrary, int unk, int unk2, int *listSlot); SongMetadata *SongMetadataConstructorHook(SongMetadata *thisSongMetadata, DataArray *data, DataArray *backupData, char isOnDisc); -char SongMetadataLoadHook(SongMetadata *thisSongMetadata, BinStream *stream); +char SongMetadataLoadHook(SongMetadata *thisSongMetadata, BinStream *stream); \ No newline at end of file diff --git a/include/SongParserHooks.h b/include/SongParserHooks.h new file mode 100644 index 0000000..567021d --- /dev/null +++ b/include/SongParserHooks.h @@ -0,0 +1,9 @@ +/* + RB3Enhanced - SongParserHooks.h + Hooks related to the parsing of song MIDIs. +*/ + +#include "rb3/SongParser.h" +#include "ports.h" + +int SongParserPitchToSlotHook(SongParser *thisSongParser, int pitch, int *difficulty, int tick); \ No newline at end of file diff --git a/include/aes.h b/include/aes.h new file mode 100644 index 0000000..4867d1a --- /dev/null +++ b/include/aes.h @@ -0,0 +1,95 @@ +#ifdef RB3E_WII + +#ifndef _AES_H_ +#define _AES_H_ + +#include +#include + +// #define the macros below to 1/0 to enable/disable the mode of operation. +// +// CBC enables AES encryption in CBC-mode of operation. +// CTR enables encryption in counter-mode. +// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. + +// The #ifndef-guard allows it to be configured before #include'ing or at compile time. +#ifndef CBC + #define CBC 1 +#endif + +#ifndef ECB + #define ECB 1 +#endif + +#ifndef CTR + #define CTR 0 +#endif + + +#define AES128 1 +//#define AES192 1 +//#define AES256 1 + +#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only + +#if defined(AES256) && (AES256 == 1) + #define AES_KEYLEN 32 + #define AES_keyExpSize 240 +#elif defined(AES192) && (AES192 == 1) + #define AES_KEYLEN 24 + #define AES_keyExpSize 208 +#else + #define AES_KEYLEN 16 // Key length in bytes + #define AES_keyExpSize 176 +#endif + +struct AES_ctx +{ + uint8_t RoundKey[AES_keyExpSize]; +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) + uint8_t Iv[AES_BLOCKLEN]; +#endif +}; + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); +#endif + +#if defined(ECB) && (ECB == 1) +// buffer size is exactly AES_BLOCKLEN bytes; +// you need only AES_init_ctx as IV is not used in ECB +// NB: ECB is considered insecure for most uses +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); + +#endif // #if defined(ECB) && (ECB == !) + + +#if defined(CBC) && (CBC == 1) +// buffer size MUST be mutile of AES_BLOCKLEN; +// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); + +#endif // #if defined(CBC) && (CBC == 1) + + +#if defined(CTR) && (CTR == 1) + +// Same function for encrypting as for decrypting. +// IV is incremented for every block, and used after encryption as XOR-compliment for output +// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); + +#endif // #if defined(CTR) && (CTR == 1) + + +#endif // _AES_H_ + +#endif // RB3E_WII diff --git a/include/config.h b/include/config.h index 0ec5443..bae2377 100644 --- a/include/config.h +++ b/include/config.h @@ -23,10 +23,12 @@ typedef struct _RB3E_Config char GameOriginIcons; char LogFileAccess; char UnlockClothing; + char DisableMenuMusic; char LanguageOverride[RB3E_LANG_LEN + 1]; char RawfilesDir[RB3E_MAX_CONFIG_LEN]; char DisableRawfiles; char QuazalLogging; + char ContentLogging; // [Graphics] int RenderResX; int RenderResY; @@ -44,6 +46,7 @@ typedef struct _RB3E_Config // [HTTP] char EnableHTTPServer; char AllowCORS; + char AllowScripts; #ifdef RB3E_XBOX // [Xbox360] char EnableLiveless; @@ -56,6 +59,7 @@ typedef struct _RB3E_Config // [Wii] char NASServer[RB3E_MAX_DOMAIN]; char LegacySDMode; + char ModernSDMode; #endif #ifdef RB3EDEBUG // [Debug] diff --git a/include/exceptions.h b/include/exceptions.h new file mode 100644 index 0000000..6680256 --- /dev/null +++ b/include/exceptions.h @@ -0,0 +1,63 @@ +/* + RB3Enhanced - exceptions.h + Structures and function definitions for exception handling. +*/ + +#ifndef _EXCEPTIONS_H +#define _EXCEPTIONS_H + +#include + +#ifdef RB3E_XBOX +#include +#endif + +#ifdef RB3E_WII +typedef struct OSContext { + uint32_t gprs[32]; // at 0x0 + uint32_t cr; // at 0x80 + uint32_t lr; // at 0x84 + uint32_t ctr; // at 0x88 + uint32_t xer; // at 0x8C + double fprs[32]; // at 0x90 + uint32_t fpscr_pad; // at 0x190 + uint32_t fpscr; // at 0x194 + uint32_t srr0; // at 0x198 + uint32_t srr1; // at 0x19C + uint16_t mode; // at 0x1A0 + uint16_t state; // at 0x1A2 + uint32_t gqrs[8]; // at 0x1A4 + uint32_t psf_pad; // at 0x1C4 + double psfs[32]; // at 0x1C8 +} OSContext; +typedef struct _rb3e_exception_wii { + uint32_t rb3e_base_address; + uint8_t error; + uint32_t dsisr; + uint32_t dar; + OSContext thread_ctx; +} rb3e_exception_wii; +#elif RB3E_XBOX +#define EXCEPTION_CONTEXT_SIZE 560 +#endif + +// '3EXx' where x is W for Wumbo, X for Xbox and P for PS3 +#define EXCEPTIONPACK_HEADER_WII 0x33455857 +#define EXCEPTIONPACK_HEADER_XBOX 0x33455858 +#define EXCEPTIONPACK_HEADER_PS3 0x33455850 + +typedef struct _rb3e_exception_header { + uint32_t magic; + uint32_t version; + char rb3e_buildtag[48]; + char rb3e_commit[48]; + uint16_t num_stackwalk; + uint16_t num_memchunks; +} rb3e_exception_header; + +typedef struct _rb3e_exception_memchunk { + uint32_t address; + uint32_t length; +} rb3e_exception_memchunk; + +#endif // _EXCEPTIONS_H diff --git a/include/ports.h b/include/ports.h index 0cb397d..7108fa1 100644 --- a/include/ports.h +++ b/include/ports.h @@ -2,322 +2,11 @@ RB3Enhanced - ports.h Defines port addresses and platform-specific function definitions. */ -#ifdef RB3E_XBOX // Rock Band 3 Xbox 360 Title Update 5 -// instruction patch addresses -#define PORT_SONGLIMIT 0x82579880 // call to "max_song_count" DataNode::_value -#define PORT_APP_CALL 0x82272e88 // call to App::_ct from main() -#define PORT_SONGBLACKLIST 0x82579098 // call to a function that checks song blacklist -#define PORT_DATAINITFUNCS_TAIL 0x82765980 // blr of DataInitFuncs -#define PORT_FASTSTART_CHECK 0x82270f40 // beq after OptionBool("fast",0) in App::_ct -#define PORT_SYSTEMINIT_BLANK 0x825113a4 // call to a stub function in SystemInit -#define PORT_XL_BYPASS_SG 0x82a88268 // lbz r11, bypassSecureGateway in Quazal::XboxClient::Login2 -#define PORT_RCG_POLL_LOGGEDIN 0x824f7178 // check for login status in RockCentralGateway::Poll -#define PORT_AUD_PATCH_CRASH 0x82b8c928 // patch somewhere to prevent Audition Mode crashes - blr -#define PORT_AUD_PATCH_NET1 0x82b8f7fc // patch somewhere to force Audition Mode to be connected (1) - nop -#define PORT_AUD_PATCH_NET2 0x82b8f80c // patch somewhere to force Audition Mode to be connected (2) - nop -#define PORT_AUD_PATCH_NET3 0x82b8f814 // patch somewhere to force Audition Mode to be connected (3) - nop -#define PORT_AUD_PATCH_NET4 0x82b8f81c // patch somewhere to force Audition Mode to be connected (4) - li r3, 0 -#define PORT_AUD_PATCH_NET5 0x82562638 // patch somewhere to force Audition Mode to be connected (5) - li r3, 1 -#define PORT_AUD_PATCH_REPL 0x82b8e978 // patch to allow Audition to recieve insecure packets from Magma -#define PORT_AUD_PATCH_HDD 0x82515dd4 // patch to make has_hard_drive always return true, probably broken -#define PORT_AUD_PATCH_UNK 0x823f6074 // idk -#define PORT_AUD_HANDLE_MESSAGES 0x82563edc // part of AuditionMgr::Handle that handles disconnected from Rock Central/XBL/etc. messages -#define PORT_AUD_HANDLE_MSG_JUMP 0x82564048 // address to jump PORT_AUD_HANDLE_MESSAGES to, to allow audition mode without GoCentral -#define PORT_AUD_INVALID_DATA_CHECK 0x825629fc // check for invalid data in a song submitted to audition mode -#define PORT_XNQOS_PROBE1 0x823ee7f8 // instruction that checks xnqos probe results -#define PORT_XNQOS_PROBE2 0x823ee800 // instruction that checks xnqos probe results -#define PORT_XNQOS_PROBE3 0x823ee80c // instruction that checks xnqos probe results -#define PORT_VDP_DISABLE1 0x82b39ba0 // nop over VDP packet sending -#define PORT_VDP_DISABLE2 0x82b39e60 // nop over VDP packet sending -#define PORT_VDP_DISABLE3 0x82b3a5e4 // nop over VDP packet sending -#define PORT_VDP_DISABLE4 0x82b3a5f0 // nop over VDP packet sending -#define PORT_SESSION_MASK_CHECK 0x82652acc // beq in while loop for instrument mask check -#define PORT_CHARACTER_CLOTHES_CHECK 0x82618120 // check to see if the goal required to select a piece of clothing has been achieved or not -#define PORT_FACE_PAINT_CHECK 0x82614a60 // check to see if face paint is unlocked -#define PORT_TATTOO_CHECK 0x82614a9c // check to see if tattoos are unlocked -#define PORT_VIDEO_VENUE_CHECK 0x82581634 // check to see if video venues are unlocked -#define PORT_OPTIONSTR_DEFINE 0x82510cc8 // bl to OptionStr("define", NULL) in PreInitSystem -#define PORT_RENDER_RES_X_PATCH1 0x8273bf20 // instruction patch to force render width -#define PORT_RENDER_RES_X_PATCH2 0x8273bf24 // instruction patch to force render width -#define PORT_RENDER_RES_Y_PATCH1 0x8273bedc // instruction patch to force render height -#define PORT_RUNLOOP_SPARE 0x822700e0 // branch to a function that only has a "blr" in App::Run(WithoutDebugging) -#define PORT_SONG_ID_EVALUATE 0x827aa7d4 // branch to DataNode::Evaluate in SongMetadata::__ct -#define PORT_LOADOBJS_BCTRL 0x827562e4 // bctrl to Object::PreLoad insie of DirLoader::LoadObjs -#define PORT_SONGMGR_ISDEMO_CHECK 0x82575f9c // "bne" after IsUGC check inside SongMgr::IsDemo -#define PORT_STAGEKIT_EXISTS 0x8228d03c // StageKit check. nop over to allow for fog command without a physical StageKit connected. -#define PORT_ADDTRACKVECTOR_BL 0x82777b70 // bl to vector_push_back inside of SongData::AddTrack -#define PORT_GETGAMELIST 0x82770730 // SongData::GetGameList -// function patch addresses -#define PORT_SETDISKERROR 0x82516320 // PlatformMgr::SetDiskError -#define PORT_APP_RUN 0x82272e90 // App::Run -#define PORT_APP_RUNNODEBUG 0x82270080 // App::RunWithoutDebugging -#define PORT_APP_CT 0x82270e68 // App::_ct -#define PORT_NEWFILE 0x825173e0 // NewFile -#define PORT_SETTRACKSPEED 0x827dd118 // TrackPanelDirBase::UpdateTrackSpeed -#define PORT_SETSONGSPEED 0x82678C88 // Game::SetMusicSpeed -#define PORT_MODIFIERMGR_CT 0x82589c48 // ModifierManager::__ct -#define PORT_MODIFIERMGR_ACTIVE 0x82588d80 // ModifierManager::ModifierActive -#define PORT_SYMBOL_CT 0x827c0728 // Symbol::Symbol -#define PORT_LOCALIZE 0x827c96d8 // Locale::Localize -#define PORT_ADDGAMEGEM 0x8278e530 // GameGemList::AddGameGem -#define PORT_WILLBENOSTRUM 0x8278cbb0 // GameGemList::WillBeNoStrum -#define PORT_SETVENUE 0x8257d1c0 // MetaPerformer::SetVenue(?) (actual func name is not known) -#define PORT_ISUGCPLUS 0x8259e890 // function that checks song source(?) -#define PORT_KEYSONGUITAR 0x825b50f8 // function that checks "key_keys_on_guitar" -#define PORT_EXECUTEDTA 0x824f7e50 // RockCentralGateway::ExecuteConfig -#define PORT_BANDLABELSETDISPLAYTEXT 0x823406f8 // BandLabel::SetDisplayText -#define PORT_SETSONGANDARTISTNAME 0x825c66f8 // BandLabel::SetSongAndArtistName -#define PORT_SETSONGNAMEFROMNODE 0x825c56a0 // BandLabel::SetSongNameFromNode -#define PORT_DATANODEEVALUATE 0x8274ae98 // DataNode::Evaluate -#define PORT_DATAARRAYFINDARRAY 0x8274c5a0 // DataArray::FindArray -#define PORT_DATAARRAYFINDDATA 0x8274c7f0 // DataArray::FindData -#define PORT_HMXFACTORYFUNCAT 0x82359f28 // HmxObjectFactoryFunc::_at -#define PORT_SETADDRESS 0x82aeb888 // Quazal::InetAddress::SetAddress -#define PORT_XL_USESECURESOCKETS 0x82a8eca8 // Inet::UseSecureSockets -#define PORT_XL_XSESSIONCREATE 0x82a69c90 // XSessionCreate -#define PORT_XL_XSESSIONJOINREMOTE 0x82a69fb0 // XSessionJoinRemote -#define PORT_XL_XSESSIONMODIFY 0x82a69e40 // XSessionModify -#define PORT_XL_XSESSIONSEARCHEX 0x82a6a490 // XSessionSearchEx -#define PORT_XL_XINVITEGETACCEPTEDINFO 0x82a6a7c8 // XInviteGetAcceptedInfo -#define PORT_RANDOMINT 0x824f2f90 // RandomInt(min, max) -#define PORT_GETWIDGETBYNAME 0x82b9b880 // GemManager::GetWidgetByName -#define PORT_GETSLOTCOLOR 0x82baa308 // TrackConfig::GetSlotColor -#define PORT_ADDSMASHERPLATETOVECTOR 0x82356980 // AddSmasherPlateToVector -#define PORT_ARCHIVE_CT 0x82514408 // Archive::_ct -#define PORT_ARCHIVE_SETLOCATIONHARDDRIVE 0x82512b00 // Archive::SetLocationHardDrive -#define PORT_ARCHIVE_MERGE 0x82513ee8 // Archive::Merge -#define PORT_ARCHIVE_DT 0x82513af8 // Archive::_dt -#define PORT_FILE_EXISTS 0x825175b0 // FileExists -#define PORT_QUEUEMESSAGE 0x82628e50 // PassiveMessagesPanel::QueueMessage -#define PORT_SETSYSTEMLANGUAGE 0x82510590 // SetSystemLanguage -#define PORT_ISSUPPORTEDLANGUAGE 0x82510510 // IsSupportedLanguage -#define PORT_DATAREADFILE 0x8276c700 // DataReadFile -#define PORT_STAGEKIT_SET_STATE 0x82524DE0 // StageKit::SetState(?) - actual name not known -#define PORT_GETSONGIDFROMSHORTNAME 0x82577140 // BandSongMgr::GetSongIDFromShortname -#define PORT_GETMETADATA 0x827a8e30 // BandSongMgr::Data (function renamed from the original name to avoid any confusion with Data.h) -#define PORT_GETSONGSYMBOL 0x8257c498 // MetaPerformer::GetSongSymbol -#define PORT_GAME_CT 0x8267bf30 // Game::__ct -#define PORT_GAME_DT 0x8267b1f0 // Game::__dt -#define PORT_GAMEGETACTIVEPLAYER 0x82678e88 // Game::GetActivePlayer -#define PORT_GETBANDUSERS 0x82683b78 // BandUserMgr::GetBandUsers -#define PORT_GETBANDUSERFROMSLOT 0x82682b60 // BandUserMgr::GetBandUserFromSlot -#define PORT_GETSONGID 0x827a87f0 // GetSongID, function used when adding songs to BandSongMgr -#define PORT_SONGMGRGETRANKEDSONGS 0x82577340 // BandSongMgr::GetRankedSongs(?) - not sure on the real name of the function -#define PORT_OVERSHELLPARTSELECTPROVIDERRELOAD 0x82668c70 // OvershellPartSelectProvider::Reload -#define PORT_PREPARESOMEVECTORMAYBE 0x82796d90 // Prepares some vector, used by OvershellPartSelectProvider::Reload -#define PORT_SOMEVECTORPUSHBACKMAYBE 0x82b6aa10 // vector,class_stlpmtx_std::StlNodeAlloc_>_>::push_back -#define PORT_VECTORPUSHBACK 0x82b5f808 // vector_push_back -#define PORT_POSTPROC_DOPOST 0x82b89a08 // NgPostProc::DoPost -#define PORT_MUSICLIBRARYSELECTMAYBE 0x8253EB00 // Selects an entry in the Music Library screen - actual name not known -#define PORT_GETSYMBOLBYGAMEORIGIN 0x8265bb78 // SongSortByRecent::GetSymbolByGameOrigin -#define PORT_GETGAMEORIGINBYSYMBOL 0x8265b910 // SongSortByRecent::GetGameOriginBySymbol -#define PORT_RECENTCMP_CT 0x8265bde8 // RecentCmp::__ct -#define PORT_FILESTREAM_CT 0x827c3340 // FileStream::__ct (the one that takes a char * path instead of a File object) -#define PORT_CHUNKSTREAM_CT 0x827ca488 // ChunkStream::__ct -#define PORT_RNDPROPANIMSETFRAME 0x82426dd0 // RndPropAnim::SetFrame -#define PORT_DYNAMICCAST 0x8282a0c8 // dynamic_cast -#define PORT_OBJECTFINDUIPANEL 0x82537430 // Object::Find -#define PORT_JOYPADGETCACHEDXINPUTCAPS 0x82531F08 // JoypadGetCachedXInputCaps -#define PORT_JOYPADGETPADDATA 0x82524998 // JoypadGetPadData -#define PORT_MEMFREE 0x827bc430 // MemFree -#define PORT_MEMALLOC 0x827bcd38 // MemAlloc -#define PORT_SYMBOLPREINIT 0x827c04f8 // Symbol::PreInit -#define PORT_QUEUINGSOCKET_BIND 0x82b397b0 // Quazal::QueuingSocket::Bind -#define PORT_QUAZALSOCKET_BIND 0x82b1a830 // Quazal::Socket::Bind -#define PORT_MEMPRINTOVERVIEW 0x827bc838 // MemPrintOverview -#define PORT_MEMPRINT 0x827bc970 // MemPrint -#define PORT_MEMNUMHEAPS 0x827bb628 // MemNumHeaps -#define PORT_INITSONGMETADATA 0x827aa450 // InitSongMetadata -#define PORT_SONGMETADATACONSTRUCTOR 0x827aa6e8 // SongMetadata::__ct -#define PORT_SONGMETADATALOAD 0x825a3f58 // SongMetadata::Load -#define PORT_UPDATEPRESENCE 0x82680430 // PresenceMgr::UpdatePresence -#define PORT_STEPSEQUENCEJOBSETSTEP 0x82af92b8 // Quazal::StepSequenceJob::SetStep -#define PORT_RNDTEXNEWOBJECT 0x82273de0 // RndTex::NewObject -#define PORT_RNDMATNEWOBJECT 0x8240f5d0 // RndMat::NewObject -#define PORT_RNDTEXSETBITMAP 0x823ff678 // RndTex::SetBitmap -#define PORT_RNDTEXSETBITMAP2 0x823ff240 // RndTex::SetBitmap2 -#define PORT_RNDTEXSETBITMAP3 0x823ff510 // RndTex::SetBitmap3 -#define PORT_FILEPATHCONSTRUCTOR 0x82270210 // FilePath::__ct -#define PORT_MUSICLIBRARY_CT 0x825451c8 // MusicLibrary::__ct -#define PORT_MUSICLIBRARYMAT 0x8253b440 // MusicLibrary::Mat -#define PORT_NODESORTGETNODE 0x825bf708 // MusicLibrary::GetNodeByIndex -#define PORT_GAMEGEMDB_CT 0x827931e0 // GameGemDB::__ct -#define PORT_ADDMULTIGEM 0x827930d8 // GameGemDB::AddMultiGem -#define PORT_SONGSORTMGRGETSORT 0x82595ff8 // SongSortMgr::GetSort -#define PORT_DYNAMICTEX_CT 0x825f2318 // DynamicTex::__ct -#define PORT_DYNAMICTEX_DT 0x825f2210 // DynamicTex::__dt -#define PORT_RNDMATSETDIFFUSETEX 0x8238b130 // RndMat::SetDiffuseTex -#define PORT_MUSICLIBRARYONENTER 0x82542238 // MusicLibrary::OnEnter -#define PORT_MUSICLIBRARYONUNLOAD 0x82540450 // MusicLibrary::OnExit -// instance addresses -#define PORT_MODIFIERMGR_POINTER 0x82dfec08 // pointer to ModifierManager -#define PORT_ROCKCENTRALGATEWAY 0x82cc8f60 // address of RockCentralGateway -#define PORT_GDATAFUNCS 0x82e05d30 // address of gDataFuncs -#define PORT_THEARCHIVE 0x82cc9c60 // address of TheArchive (main ARK) -#define PORT_THEBANDUI 0x82dfd2b0 // address of TheBandUI -#define PORT_NULLSYMBOL 0x82c71838 // address of gNullSymbol -#define PORT_THESONGDB 0x82e023f8 // address of TheSongDB -#define PORT_THESONGMGR 0x82dfe7b4 // address of TheSongMgr -#define PORT_THEMETAPERFORMER 0x82dfe954 // address of TheMetaPerformer -#define PORT_THEBANDUSERMGR 0x82e023b8 // address of TheBandUserMgr -#define PORT_THESONGSORTMGR 0x82dfee5c // pointer to TheSongSortMgr -#define PORT_THEMUSICLIBRARY 0x82dfd3a8 // pointer to TheMusicLibrary -#define PORT_THEGAME 0x82e02128 // pointer to TheGame (you lost) -#define PORT_OBJECTDIRMAINDIR 0x82e054b8 // ObjectDir::sMainDir -// import function stubs -#define PORT_XEKEYSSETKEY_STUB 0x82c4c47c -#define PORT_XEKEYSAESCBC_STUB 0x82c4c48c -#define PORT_SOCKET_STUB 0x8284da48 -#define PORT_XNETSTARTUP 0x8284d7e8 -#define PORT_XNETXNADDRTOINADDR 0x8284d840 -#define PORT_XNETCONNECT 0x8284d890 -#define PORT_XNETUNREGISTERKEY 0x8284d830 -#define PORT_XNETUNREGISTERINADDR 0x8284d870 -#define PORT_XNETREGISTERKEY 0x8284d820 -#define PORT_XNETGETTITLEXNADDR 0x8284d968 -#define PORT_XNETQOSLOOKUP 0x8284d8f8 -#define PORT_XAMUSERGETSIGNININFO 0x82c4be1c -#define PORT_XAMUSERGETSIGNINSTATE 0x82c4bcfc -#define PORT_XAMUSERCHECKPRIVILEGE 0x82c4bd1c -#define PORT_XAMSHOWFRIENDSUI 0x8283d710 - -// define logging functions -void DbgPrint(const char *s, ...); -#define RB3E_PRINT DbgPrint - -#endif // RB3E_XBOX - -#ifdef RB3E_WII // Rock Band 3 Wii - -#include - -// instruction patch addresses -#define PORT_SONGLIMIT 0x801cedac // call to "max_song_count" DataNode::_value -#define PORT_SONGBLACKLIST 0x801d2148 // call to a function that checks song blacklist -#define PORT_DATAINITFUNCS_TAIL 0x80321b7c // blr of DataInitFuncs -#define PORT_FASTSTART_CHECK 0x8000e2f0 // beq after OptionBool("fast",0) in App::_ct -#define PORT_STRAPSCREEN_1 0x8000e40c // branch to CustomSplash::Show in App::_ct -#define PORT_STRAPSCREEN_2 0x8000e41c // branch to CustomSplash::EndShow in App::_ct -#define PORT_NASWII_HOST 0x807e94a0 // branch to the add header function in the DWCDL login function -#define PORT_CHARACTER_CLOTHES_CHECK 0x801fec58 // check to see if the goal required to select a piece of clothing has been unlocked -#define PORT_FACE_PAINT_CHECK 0x801fd9a8 // check to see if face paint is unlocked -#define PORT_TATTOO_CHECK 0x801fd9c4 // check to see if tattoos are unlocked -#define PORT_VIDEO_VENUE_CHECK 0x80227e34 // check to see if video venues are unlocked -#define PORT_OPTIONSTR_DEFINE 0x8030e418 // bl to OptionStr("define", NULL) in PreInitSystem -#define PORT_RUNLOOP_SPARE 0x8000f740 // branch to a function that only has a "blr" in App::Run(WithoutDebugging) -#define PORT_MICCHECK 0x8024a4e8 // a bne that throws an error on the song select screen if the mic is not connected -#define PORT_BIGSYMBOLFUNC_TAIL 0x8037a3d4 // blr after a function that initialises a bunch of symbols -#define PORT_UPDATEPRESENCEBLOCK_B 0x80188194 // branch after the failure case in a function that calls UpdatePresence -#define PORT_ADDTRACKVECTOR_BL 0x80480a88 // bl to vector_push_back inside of SongData::AddTrack -// #define PORT_LOADOBJS_BCTRL 0x827562e4 -// function patch addresses -#define PORT_SETDISKERROR 0x8030ce7c // PlatformMgr::SetDiskError -#define PORT_NEWFILE 0x802f9ed0 // NewFile -#define PORT_SETTRACKSPEED 0x80441ee0 // TrackPanelDirBase::UpdateTrackSpeed -#define PORT_SETSONGSPEED 0x801130d0 // Game::SetMusicSpeed -#define PORT_MODIFIERMGR_CT 0x8022c1b4 // ModifierManager::__ct -#define PORT_MODIFIERMGR_ACTIVE 0x8022c830 // ModifierManager::ModifierActive -#define PORT_SYMBOL_CT 0x80363f60 // Symbol::Symbol -#define PORT_LOCALIZE 0x803506f4 // Locale::Localize -#define PORT_SETVENUE 0x802282dc // MetaPerformer::SetVenue(?) (actual func name is not known) -#define PORT_EXECUTEDTA 0x802cf7e0 // RockCentralGateway::ExecuteConfig -#define PORT_BANDLABELSETDISPLAYTEXT 0x803b1858 // BandLabel::SetDisplayText -#define PORT_SETSONGANDARTISTNAME 0x801b68a8 // BandLabel::SetSongAndArtistName -#define PORT_SETSONGNAMEFROMNODE 0x801b6358 // BandLabel::SetSongNameFromNode -#define PORT_KEYSONGUITAR 0x80242ab4 // function that checks "key_keys_on_guitar" -#define PORT_HMXFACTORYFUNCAT 0x8031b2f8 // HmxObjectFactoryFunc::_at -#define PORT_WILLBENOSTRUM 0x80463010 // GameGemList::WillBeNoStrum -#define PORT_ADDGAMEGEM 0x80463198 // GameGemList::AddGameGem -#define PORT_SETADDRESS 0x8001bf74 // Quazal::InetAddress::SetAddress -#define PORT_RANDOMINT 0x802ddd60 // RandomInt(min, max) -#define PORT_GETWIDGETBYNAME 0x800d59b0 // GemManager::GetWidgetByName -#define PORT_DATANODEEVALUATE 0x80322e9c // DataNode::Evaluate -#define PORT_GETSLOTCOLOR 0x800e42a4 // TrackConfig::GetSlotColor -#define PORT_ADDSMASHERPLATETOVECTOR 0x804316d4 // AddSmasherPlateToVector -#define PORT_USBWIIGETTYPE 0x806c1a3c // UsbWii::GetType -#define PORT_FILE_EXISTS 0x802fa134 // FileExists -#define PORT_QUEUEMESSAGE 0x80253c50 // PassiveMessagesPanel::QueueMessage -#define PORT_SETSYSTEMLANGUAGE 0x8030f308 // SetSystemLanguage -#define PORT_ISSUPPORTEDLANGUAGE 0x8030f280 // IsSupportedLanguage -#define PORT_DATAREADFILE 0x80319bdc // DataReadFile -#define PORT_GAME_CT 0x80110f20 // Game::__ct -#define PORT_GAME_DT 0x80111614 // Game::__dt -#define PORT_GAMEGETACTIVEPLAYER 0x8011346c // Game::GetActivePlayer -#define PORT_WIINETINIT_DNSLOOKUP 0x8030c3a0 // WiiNetInit::StartDNSLookup -#define PORT_OVERSHELLPARTSELECTPROVIDERRELOAD 0x802478a8 // OvershellPartSelectProvider::Reload -#define PORT_PREPARESOMEVECTORMAYBE 0x80247c58 // Prepares some vector, used by BuildInstrumentSelectionList -#define PORT_SOMEVECTORPUSHBACKMAYBE 0x802484a8 // vector,class_stlpmtx_std::StlNodeAlloc_>_>::push_back -#define PORT_VECTORPUSHBACK 0x800a6ef4 // vector_push_back -#define PORT_POSTPROC_DOPOST 0x806b52b4 // WiiPostProc::DoPost -#define PORT_MUSICLIBRARYSELECTMAYBE 0x80230d64 // Selects an entry in the Music Library screen - actual name not known -#define PORT_GETSYMBOLBYGAMEORIGIN 0x8027dd3c // SongSortByRecent::GetSymbolByGameOrigin -#define PORT_GETGAMEORIGINBYSYMBOL 0x8027dc58 // SongSortByRecent::GetGameOriginBySymbol -#define PORT_RECENTCMP_CT 0x8027dba8 // RecentCmp::__ct -#define PORT_FILESTREAM_CT 0x8034c9f8 // FileStream::__ct (the one that takes a char * path instead of a File object) -#define PORT_CHUNKSTREAM_CT 0x8034aa90 // ChunkStream::__ct -#define PORT_GETBANDUSERFROMSLOT 0x8010021c // BandUserMgr::GetBandUserFromSlot -#define PORT_GETBANDUSERS 0x80100558 // BandUserMgr::GetBandUsers -#define PORT_GETSONGSYMBOL 0x80224edc // MetaPerformer::GetSongSymbol -#define PORT_GETMETADATA 0x80515510 // BandSongMgr::Data (function renamed from the original name to avoid any confusion with Data.h) -#define PORT_GETSONGID 0x8051513c // GetSongID, function used when adding songs to BandSongMgr -#define PORT_SONGMGRGETRANKEDSONGS 0x801d1590 // BandSongMgr::GetRankedSongs(?) - not sure on the real name of the function -#define PORT_GETSONGIDFROMSHORTNAME 0x801d0b44 // BandSongMgr::GetSongIDFromShortname -#define PORT_RNDPROPANIMSETFRAME 0x80632790 // RndPropAnim::SetFrame -#define PORT_DYNAMICCAST 0x806f5e78 // dynamic_cast -#define PORT_OBJECTFINDUIPANEL 0x80101d74 // Object::Find -#define PORT_JOYPADGETPADDATA 0x80302eec // JoypadGetPadData -#define PORT_MEMALLOC 0x80353e18 // MemAlloc -#define PORT_MEMFREE 0x80354238 // MemFree -#define PORT_SYMBOLPREINIT 0x80364c74 // Symbol::PreInit -#define PORT_QUEUINGSOCKET_BIND 0x800478d4 // Quazal::QueuingSocket::Bind -#define PORT_QUAZALSOCKET_BIND 0x8001cd10 // Quazal::Socket::Bind -#define PORT_INITSONGMETADATA 0x805147a4 // InitSongMetadata -#define PORT_SONGMETADATACONSTRUCTOR 0x80514880 // SongMetadata::__ct -#define PORT_SONGMETADATALOAD 0x801d2090 // SongMetadata::Load -#define PORT_UPDATEPRESENCE 0x801879d4 // PresenceMgr::UpdatePresence -#define PORT_STEPSEQUENCEJOBSETSTEP 0x80025364 // Quazal::StepSequenceJob::SetStep -#define PORT_RNDTEXNEWOBJECT 0x80639904 // RndTex::NewObject -#define PORT_RNDMATNEWOBJECT 0x8063996c // RndMat::NewObject -#define PORT_RNDTEXSETBITMAP 0x8063fccc // RndTex::SetBitmap -#define PORT_RNDTEXSETBITMAP2 0x8063f830 // RndTex::SetBitmap2 -#define PORT_RNDTEXSETBITMAP3 0x8063fb2c // RndTex::SetBitmap3 -#define PORT_FILEPATHCONSTRUCTOR 0x8000ec5c // FilePath::__ct -#define PORT_MUSICLIBRARY_CT 0x8022d978 // MusicLibrary::__ct -#define PORT_MUSICLIBRARYMAT 0x80231c5c // MusicLibrary::Mat -#define PORT_NODESORTGETNODE 0x80279314 // MusicLibrary::GetNodeByIndex -#define PORT_GAMEGEMDB_CT 0x80460f64 // GameGemDB::__ct -#define PORT_ADDMULTIGEM 0x80461160 // GameGemDB::AddMultiGem -#define PORT_GETGAMELIST 0x8048553c // SongData::GetGameList -#define PORT_SONGSORTMGRGETSORT 0x80281b20 // SongSortMgr::GetSort -#define PORT_RNDMATSETDIFFUSETEX 0x8025ab90 // RndMat::SetDiffuseTex -#define PORT_DYNAMICTEX_CT 0x80292a70 // DynamicTex::__ct -#define PORT_DYNAMICTEX_DT 0x80292bcc // DynamicTex::__dt -#define PORT_MUSICLIBRARYONENTER 0x8022dd24 // MusicLibrary::OnEnter -#define PORT_MUSICLIBRARYONUNLOAD 0x8022e87c // MusicLibrary::OnExit -// instance addresses -#define PORT_MODIFIERMGR_POINTER 0x808fda68 // pointer to ModifierManager -#define PORT_ROCKCENTRALGATEWAY 0x80900870 // address of RockCentralGateway -#define PORT_GDATAFUNCS 0x8091a528 // address of gDataFuncs -#define PORT_THEARCHIVE 0x80902234 // address of TheArchive (main ARK) -#define PORT_THEBANDUI 0x808fc2b0 // address of TheBandUI -#define PORT_NULLSYMBOL 0x808540e0 // address of gNullSymbol -#define PORT_THESONGDB 0x808fb170 // address of TheSongDB - TODO: check -#define PORT_THEMUSICLIBRARY 0x808fda84 // pointer to TheMusicLibrary -#define PORT_THESONGSORTMGR 0x808ff988 // pointer to TheSongSortMgr -#define PORT_THESONGMGR 0x808fbda4 // address of TheSongMgr -#define PORT_THEMETAPERFORMER 0x808fd6f8 // address of TheMetaPerformer -#define PORT_THEBANDUSERMGR 0x808f9350 // pointer to TheBandUserMgr -#define PORT_THEGAME 0x808f9758 // pointer to TheGame (you lost) -#define PORT_OBJECTDIRMAINDIR 0x8091b1e8 // ObjectDir::sMainDir -// string pointers -#define PORT_NASWII_AC_URL 0x808e2310 -#define PORT_NASWII_PR_URL 0x808e2390 - -// define logging functions -#define RB3E_PRINT printf - -#endif // RB3E_WII +#include "ports_xbox360.h" +#include "ports_wii.h" +#include "ports_ps3.h" +#include "ports_wii_bank8.h" #define RB3E_MSG(x, ...) RB3E_PRINT("[RB3E:MSG] " x "\n", __VA_ARGS__) #ifdef RB3EDEBUG diff --git a/include/ports_ps3.h b/include/ports_ps3.h new file mode 100644 index 0000000..a9f808b --- /dev/null +++ b/include/ports_ps3.h @@ -0,0 +1,136 @@ +/* + RB3Enhanced - ports_ps3.h + Defines port addresses and platform-specific function definitions (for PS3 1.06 EUR) +*/ + +#ifdef RB3E_PS3 // Rock Band 3 PS3 BLES00986 1.06 + +// instruction patch addresses +#define PORT_SONGLIMIT 0x00281580 // call to "max_song_count" DataNode::_value +#define PORT_APP_CALL 0x00017384 // call to App::_ct from main() +#define PORT_SONGBLACKLIST 0x00280BE4 // call to BandSongMgr::IsInExclusionList +#define PORT_DATAINITFUNCS_TAIL 0x008F1D48 // blr of DataInitFuncs +#define PORT_FASTSTART_CHECK 0x00012744 // beq after OptionBool("fast",0) in App::_ct +#define PORT_SYSTEMINIT_BLANK 0x008BFBD0 // call to a stub function in SystemInit +#define PORT_CHARACTER_CLOTHES_CHECK 0x002A92DC // check to see if the goal required to select a piece of clothing has been achieved or not +#define PORT_FACE_PAINT_CHECK 0x002A4C60 // check to see if face paint is unlocked +#define PORT_TATTOO_CHECK 0x002A4CA0 // check to see if tattoos are unlocked +#define PORT_VIDEO_VENUE_CHECK 0x002D06A8 // check to see if video venues are unlocked +#define PORT_OPTIONSTR_DEFINE 0x008C005C // bl to OptionStr("define", NULL) in PreInitSystem +#define PORT_RUNLOOP_SPARE 0x00010960 // branch to a function that only has a "blr" in App::Run(WithoutDebugging) +#define PORT_SONG_ID_EVALUATE 0x0063E6B8 // branch to DataNode::Evaluate in SongMetadata::__ct +// #define PORT_LOADOBJS_BCTRL 0 // bctrl to Object::PreLoad insie of DirLoader::LoadObjs +#define PORT_STAGEKIT_EXISTS 0x00442FAC // StageKit check. nop over to allow for fog command without a physical StageKit connected. +#define PORT_PS3_VORBISREADER_CTR_CHECK 0x00883F40 // check in the SPU VorbisReader decoder that checks if there is a CTR state +#define PORT_NPDRM_UNAVAILABLE_FALLTHROUGH 0x008C8BD0 // fallthrough after sceNpDrmIsAvailable (r == CELL_OK) beq instruction +#define PORT_NPDRM_FSOPEN_JUMP 0x008C8C6C // statement that runs after sceNpDrmIsAvailable returns CELL_OK (starts "li r0, 2") +#define PORT_NPDRM_FSOPEN_RETURN 0x008C8C80 // branch to a "extsw r4, r9" after the npdrm cellFsOpen completes +#define PORT_NPDRM_SETERRORCODE_JUMP 0x008C8C5C // "extsw r4, r3" after the call to cellFsOpen for unencrypted midi files +// function patch addresses +#define PORT_SETDISKERROR 0x008CB888 // PlatformMgr::SetDiskError +#define PORT_APP_RUN 0x000108C8 // App::Run +#define PORT_APP_CT 0x000125CC // App::_ct +#define PORT_NEWFILE 0x008AB024 // NewFile +#define PORT_SETTRACKSPEED 0x0051A3D8 // TrackPanelDirBase::UpdateTrackSpeed +#define PORT_SETSONGSPEED 0x0017BBE0 // Game::SetMusicSpeed +#define PORT_MODIFIERMGR_CT 0x002D7718 // ModifierManager::__ct +#define PORT_MODIFIERMGR_ACTIVE 0x002D5B64 // ModifierManager::ModifierActive +#define PORT_SYMBOL_CT 0x00956C00 // Symbol::Symbol +#define PORT_LOCALIZE 0x00937554 // Locale::Localize +// #define PORT_ADDGAMEGEM 0 // GameGemList::AddGameGem +// #define PORT_WILLBENOSTRUM 0 // GameGemList::WillBeNoStrum +#define PORT_SETVENUE 0x002CC1E0 // MetaPerformer::SetVenue(?) (actual func name is not known) +#define PORT_ISUGCPLUS 0x00276470 // function that checks song source(?) +#define PORT_KEYSONGUITAR 0x0030028C // function that checks "key_keys_on_guitar" +#define PORT_EXECUTEDTA 0x00416A58 // RockCentralGateway::ExecuteConfig +#define PORT_BANDLABELSETDISPLAYTEXT 0x00465D78 // BandLabel::SetDisplayText +#define PORT_SETSONGANDARTISTNAME 0x00265A44 // BandLabel::SetSongAndArtistName +#define PORT_SETSONGNAMEFROMNODE 0x00263C28 // BandLabel::SetSongNameFromNode +#define PORT_DATANODEEVALUATE 0x008FBE24 // DataNode::Evaluate +#define PORT_DATAARRAYFINDARRAY 0x008E95B4 // DataArray::FindArray +#define PORT_DATAARRAYFINDDATA 0x008E9D14 // DataArray::FindData +#define PORT_DATAREGISTERFUNC 0x008F0AC0 // DataRegisterFunc +#define PORT_SETADDRESS 0x000D12B4 // Quazal::InetAddress::SetAddress +#define PORT_RANDOMINT 0x0096F5D4 // RandomInt(min, max) +#define PORT_GETWIDGETBYNAME 0x009CC848 // GemManager::GetWidgetByName +#define PORT_GETSLOTCOLOR 0x009DC30C // TrackConfig::GetSlotColor +#define PORT_ARCHIVE_CT 0x008A0934 // Archive::_ct +#define PORT_ARCHIVE_SETLOCATIONHARDDRIVE 0x0089E46C // Archive::SetLocationHardDrive +#define PORT_ARCHIVE_MERGE 0x008A0CB4 // Archive::Merge +// #define PORT_ARCHIVE_DT 0 // Archive::_dt +#define PORT_FILE_EXISTS 0x008AB2AC // FileExists +#define PORT_QUEUEMESSAGE 0x00323570 // PassiveMessagesPanel::QueueMessage +#define PORT_SETSYSTEMLANGUAGE 0x008BEDB0 // SetSystemLanguage +#define PORT_ISSUPPORTEDLANGUAGE 0x008BE934 // IsSupportedLanguage +#define PORT_DATAREADFILE 0x008EF84C // DataReadFile +#define PORT_STAGEKIT_SET_STATE 0x008B2FBC // StageKit::SetState(?) - actual name not known +// #define PORT_GETSONGIDFROMSHORTNAME 0 // BandSongMgr::GetSongIDFromShortname +// #define PORT_GETMETADATA 0 // BandSongMgr::Data (function renamed from the original name to avoid any confusion with Data.h) +#define PORT_GETSONGSHORTNAME 0x002C9960 // MetaPerformer::GetSongShortname(?) - actual name not known +#define PORT_GAME_CT 0x00181CE4 // Game::__ct +#define PORT_GAME_DT 0x0018337C // Game::__dt +#define PORT_GAMEGETACTIVEPLAYER 0x0017BB48 // Game::GetActivePlayer +#define PORT_GETBANDUSERS 0x00178E00 // BandUserMgr::GetBandUsers +#define PORT_GETBANDUSERFROMSLOT 0x00176FA0 // BandUserMgr::GetBandUserFromSlot +#define PORT_GETSONGID 0x0063F7A8 // GetSongID, function used when adding songs to BandSongMgr +#define PORT_SONGMGRGETRANKEDSONGS 0x002802B8 // BandSongMgr::GetRankedSongs(?) - not sure on the real name of the function +#define PORT_BUILDINSTRUMENTSELECTION 0x003E4EC4 // BuildInstrumentSelectionList(?) - actual name not known +#define PORT_PREPARESOMEVECTORMAYBE 0x003E4EAC // Prepares some vector, used by BuildInstrumentSelectionList +#define PORT_SOMEVECTORPUSHBACKMAYBE 0x003E4E1C // vector,class_stlpmtx_std::StlNodeAlloc_>_>::push_back +#define PORT_POSTPROC_DOPOST 0x00A04218 // NgPostProc::DoPost +#define PORT_MUSICLIBRARYSELECTMAYBE 0x002E09A0 // Selects an entry in the Music Library screen - actual name not known +#define PORT_GETSYMBOLBYGAMEORIGIN 0x003EA7F8 // SongSortByRecent::GetSymbolByGameOrigin +#define PORT_GETGAMEORIGINBYSYMBOL 0x003EA5E8 // SongSortByRecent::GetGameOriginBySymbol +#define PORT_SONGSORTBYRECENT 0x003EAA9C // RecentCmp::__ct +#define PORT_FILESTREAM_CT 0x00935764 // FileStream::__ct (the one that takes a char * path instead of a File object) +#define PORT_CHUNKSTREAM_CT 0x0093100C // ChunkStream::__ct +// #define PORT_RNDPROPANIMSETFRAME 0 // RndPropAnim::SetFrame +#define PORT_DYNAMICCAST 0x00A3BA7C // dynamic_cast +#define PORT_OBJECTFINDUIPANEL 0x008FFF64 // Object::Find +#define PORT_JOYPADGETPADDATA 0x008B18C0 // JoypadGetPadData +#define PORT_MEMFREE 0x0093EEC0 // MemFree +#define PORT_MEMALLOC 0x0093FAE0 // MemAlloc +#define PORT_SYMBOLPREINIT 0x00956F68 // Symbol::PreInit +// #define PORT_QUEUINGSOCKET_BIND 0 // Quazal::QueuingSocket::Bind +// #define PORT_QUAZALSOCKET_BIND 0 // Quazal::Socket::Bind +// #define PORT_MEMPRINTOVERVIEW 0 // MemPrintOverview +// #define PORT_MEMPRINT 0 // MemPrint +// #define PORT_MEMNUMHEAPS 0 // MemNumHeaps +#define PORT_INITSONGMETADATA 0x0063DAB4 // InitSongMetadata +// #define PORT_UPDATEPRESENCE 0 // PresenceMgr::UpdatePresence +#define PORT_STEPSEQUENCEJOBSETSTEP 0x000DD0D8 // Quazal::StepSequenceJob::SetStep +#define PORT_CTR_DECRYPT 0x00A1FBF8 // ctr_decrypt +#define PORT_TITLEIDREGISTER 0x008A6224 // TitleIDRegister(?) - function that gets passed title IDs at startup / config parsing +#define PORT_CANPLAYSONG_PARENTAL 0x00391648 // broken function that checks against parental controls whether a song is playable +// instance addresses +#define PORT_MODIFIERMGR_POINTER 0x00F2B870 // pointer to ModifierManager +#define PORT_ROCKCENTRALGATEWAY 0x00F33948 // address of RockCentralGateway +// #define PORT_GDATAFUNCS 0 // address of gDataFuncs +// #define PORT_THEARCHIVE 0 // address of TheArchive (main ARK) +// #define PORT_THEBANDUI 0 // address of TheBandUI +#define PORT_NULLSYMBOL 0x00F1BEF0 // address of gNullSymbol +// #define PORT_THESONGDB 0 // address of TheSongDB +// #define PORT_THESONGMGR 0 // address of TheSongMgr +// #define PORT_THEMETAPERFORMER 0 // address of TheMetaPerformer +#define PORT_THEBANDUSERMGR 0x00F23460 // address of TheBandUserMgr +// #define PORT_THESONGSORTMGR 0 // pointer to TheSongSortMgr +// #define PORT_THEMUSICLIBRARY 0 // pointer to TheMusicLibrary +// #define PORT_THEGAME 0 // pointer to TheGame (you lost) +// #define PORT_OBJECTDIRMAINDIR 0 // ObjectDir::sMainDir +// ps3 usb specific hooks +#define PORT_LDDREGISTERTAIL 0x008e4240 // the "ld r2" instruction after the final call to cellUsbdRegisterExtraLdd2 +#define PORT_LDDADDRESS 0x00f1bf1c // just one of the LDDs. doesn't matter which. this one is RockBandKeyboard +#define PORT_ISUSBDEVICEVALID 0x008e1074 // function called by the Probe LDD function that checks if a device is valid + +// define logging functions +extern int _sys_printf(char *fmt, ...); +#define RB3E_PRINT _sys_printf + +// do some definitions of stuff +extern int _sys_sprintf(char *buf, char *fmt, ...); +#define printf _sys_printf +#define sprintf _sys_sprintf + +#define PLUGIN_PTR(x) (uint32_t)(*(uint32_t *)&x) + +#endif // RB3E_PS3 diff --git a/include/ports_wii.h b/include/ports_wii.h new file mode 100644 index 0000000..2131c48 --- /dev/null +++ b/include/ports_wii.h @@ -0,0 +1,184 @@ +/* + RB3Enhanced - ports_wii.h + Defines port addresses and platform-specific function definitions (for Wii retail) +*/ + +#ifdef RB3E_WII // Rock Band 3 Wii +#ifndef RB3E_WII_BANK8 + +#include + +// instruction patch addresses +#define PORT_SONGLIMIT 0x801cedac // call to "max_song_count" DataNode::_value +#define PORT_SONGBLACKLIST 0x801d2148 // call to BandSongMgr::IsInExclusionList +#define PORT_DATAINITFUNCS_TAIL 0x80321b7c // blr of DataInitFuncs +#define PORT_FASTSTART_CHECK 0x8000e2f0 // beq after OptionBool("fast",0) in App::_ct +#define PORT_STRAPSCREEN_1 0x8000e40c // branch to CustomSplash::Show in App::_ct +#define PORT_STRAPSCREEN_2 0x8000e41c // branch to CustomSplash::EndShow in App::_ct +#define PORT_NASWII_HOST 0x807e94a0 // branch to the add header function in the DWCDL login function +#define PORT_CHARACTER_CLOTHES_CHECK 0x802607bc // check to see if the goal required to select a piece of clothing has been unlocked +#define PORT_CHARACTER_CLOTHES_CHECK2 0x802607c0 // check to see if the goal required to select a piece of clothing has been unlocked 2 +#define PORT_FACE_PAINT_CHECK 0x801fd9a8 // check to see if face paint is unlocked +#define PORT_TATTOO_CHECK 0x801fd9c4 // check to see if tattoos are unlocked +#define PORT_VIDEO_VENUE_CHECK 0x80227e34 // check to see if video venues are unlocked +#define PORT_OPTIONSTR_DEFINE 0x8030e418 // bl to OptionStr("define", NULL) in PreInitSystem +#define PORT_RUNLOOP_SPARE 0x8000f740 // branch to a function that only has a "blr" in App::Run(WithoutDebugging) +#define PORT_MICCHECK 0x8024a4e8 // a bne that throws an error on the song select screen if the mic is not connected +#define PORT_BIGSYMBOLFUNC_TAIL 0x8037a3d4 // blr after a function that initialises a bunch of symbols +#define PORT_UPDATEPRESENCEBLOCK_B 0x80188194 // branch after the failure case in a function that calls UpdatePresence +#define PORT_MULTIPLAYER_CRASH 0x80018a78 // branch to a function that can crash in online multiplayer +#define PORT_MULTIPLAYER_FIX 0x806ec0e8 // the function that doesn't crash +#define PORT_ADDTRACKVECTOR_BL 0x80480a88 // bl to vector_push_back inside of SongData::AddTrack +// #define PORT_LOADOBJS_BCTRL 0x827562e4 +// function patch addresses +#define PORT_SETDISKERROR 0x8030ce7c // PlatformMgr::SetDiskError +#define PORT_NEWFILE 0x802f9ed0 // NewFile +#define PORT_SETTRACKSPEED 0x80441ee0 // TrackPanelDirBase::UpdateTrackSpeed +#define PORT_SETSONGSPEED 0x801130d0 // Game::SetMusicSpeed +#define PORT_MODIFIERMGR_CT 0x8022c1b4 // ModifierManager::__ct +#define PORT_MODIFIERMGR_ACTIVE 0x8022c830 // ModifierManager::ModifierActive +#define PORT_SYMBOL_CT 0x80363f60 // Symbol::Symbol +#define PORT_LOCALIZE 0x803506f4 // Locale::Localize +#define PORT_SETVENUE 0x802282dc // MetaPerformer::SetVenue(?) (actual func name is not known) +#define PORT_EXECUTEDTA 0x802cf7e0 // RockCentralGateway::ExecuteConfig +#define PORT_BANDLABELSETDISPLAYTEXT 0x803b1858 // BandLabel::SetDisplayText +#define PORT_SETSONGANDARTISTNAME 0x801b68a8 // BandLabel::SetSongAndArtistName +#define PORT_SETSONGNAMEFROMNODE 0x801b6358 // BandLabel::SetSongNameFromNode +#define PORT_KEYSONGUITAR 0x80242ab4 // function that checks "key_keys_on_guitar" +#define PORT_HMXFACTORYFUNCAT 0x8031b2f8 // HmxObjectFactoryFunc::_at +#define PORT_WILLBENOSTRUM 0x80463010 // GameGemList::WillBeNoStrum +#define PORT_ADDGAMEGEM 0x80463198 // GameGemList::AddGameGem +#define PORT_SETADDRESS 0x8001bf74 // Quazal::InetAddress::SetAddress +#define PORT_RANDOMINT 0x802ddd60 // RandomInt(min, max) +#define PORT_GETWIDGETBYNAME 0x800d59b0 // GemManager::GetWidgetByName +#define PORT_DATANODEEVALUATE 0x80322e9c // DataNode::Evaluate +#define PORT_GETSLOTCOLOR 0x800e42a4 // TrackConfig::GetSlotColor +#define PORT_ADDSMASHERPLATETOVECTOR 0x804316d4 // AddSmasherPlateToVector +#define PORT_USBWIIGETTYPE 0x806c1a3c // UsbWii::GetType +#define PORT_FILE_EXISTS 0x802fa134 // FileExists +#define PORT_QUEUEMESSAGE 0x80253c50 // PassiveMessagesPanel::QueueMessage +#define PORT_SETSYSTEMLANGUAGE 0x8030f308 // SetSystemLanguage +#define PORT_ISSUPPORTEDLANGUAGE 0x8030f280 // IsSupportedLanguage +#define PORT_DATAREADFILE 0x80319bdc // DataReadFile +#define PORT_GAME_CT 0x80110f20 // Game::__ct +#define PORT_GAME_DT 0x80111614 // Game::__dt +#define PORT_GAMEGETACTIVEPLAYER 0x8011346c // Game::GetActivePlayer +#define PORT_WIINETINIT_DNSLOOKUP 0x8030c3a0 // WiiNetInit::StartDNSLookup +#define PORT_BUILDINSTRUMENTSELECTION 0x802478a8 // BuildInstrumentSelectionList(?) - actual name not known +#define PORT_PREPARESOMEVECTORMAYBE 0x80247c58 // Prepares some vector, used by BuildInstrumentSelectionList +#define PORT_SOMEVECTORPUSHBACKMAYBE 0x802484a8 // vector,class_stlpmtx_std::StlNodeAlloc_>_>::push_back +#define PORT_VECTORPUSHBACK 0x800a6ef4 // vector_push_back +#define PORT_POSTPROC_DOPOST 0x806b52b4 // WiiPostProc::DoPost +#define PORT_MUSICLIBRARYSELECTMAYBE 0x80230d64 // Selects an entry in the Music Library screen - actual name not known +#define PORT_GETSYMBOLBYGAMEORIGIN 0x8027dd3c // SongSortByRecent::GetSymbolByGameOrigin +#define PORT_GETGAMEORIGINBYSYMBOL 0x8027dc58 // SongSortByRecent::GetGameOriginBySymbol +#define PORT_SONGSORTBYRECENT 0x8027dba8 // RecentCmp::__ct +#define PORT_FILESTREAM_CT 0x8034c9f8 // FileStream::__ct (the one that takes a char * path instead of a File object) +#define PORT_CHUNKSTREAM_CT 0x8034aa90 // ChunkStream::__ct +#define PORT_GETBANDUSERFROMSLOT 0x8010021c // BandUserMgr::GetBandUserFromSlot +#define PORT_OVERSHELLPARTSELECTPROVIDERRELOAD 0x802478a8 // OvershellPartSelectProvider::Reload +#define PORT_GETBANDUSERS 0x80100558 // BandUserMgr::GetBandUsers +#define PORT_GETSONGSHORTNAME 0x80224edc // MetaPerformer::GetSongShortname(?) - actual name not known +#define PORT_GETMETADATA 0x80515510 // BandSongMgr::Data (function renamed from the original name to avoid any confusion with Data.h) +#define PORT_GETSONGID 0x8051513c // GetSongID, function used when adding songs to BandSongMgr +#define PORT_SONGMGRGETRANKEDSONGS 0x801d1590 // BandSongMgr::GetRankedSongs(?) - not sure on the real name of the function +#define PORT_GETSONGIDFROMSHORTNAME 0x801d0b44 // BandSongMgr::GetSongIDFromShortname +#define PORT_RNDPROPANIMSETFRAME 0x80632790 // RndPropAnim::SetFrame +#define PORT_DYNAMICCAST 0x806f5e78 // dynamic_cast +#define PORT_OBJECTFINDUIPANEL 0x80101d74 // Object::Find +#define PORT_JOYPADGETPADDATA 0x80302eec // JoypadGetPadData +#define PORT_MEMALLOC 0x80353e18 // MemAlloc +#define PORT_MEMFREE 0x80354238 // MemFree +#define PORT_SYMBOLPREINIT 0x80364c74 // Symbol::PreInit +#define PORT_QUEUINGSOCKET_BIND 0x800478d4 // Quazal::QueuingSocket::Bind +#define PORT_QUAZALSOCKET_BIND 0x8001cd10 // Quazal::Socket::Bind +#define PORT_METAMUSICISLOADED 0x80678cec // MetaMusic::Loaded +#define PORT_METAMUSICLOAD 0x80678990 // MetaMusic::Load +#define PORT_METAMUSICPOLL 0x80678d20 // MetaMusic::Poll +#define PORT_METAMUSICSTART 0x80678fa4 // MetaMusic::Start +#define PORT_INITSONGMETADATA 0x805147a4 // InitSongMetadata +#define PORT_SONGMETADATACONSTRUCTOR 0x80514880 // SongMetadata::__ct +#define PORT_SONGMETADATALOAD 0x801d2090 // SongMetadata::Load +#define PORT_UPDATEPRESENCE 0x801879d4 // PresenceMgr::UpdatePresence +#define PORT_STEPSEQUENCEJOBSETSTEP 0x80025364 // Quazal::StepSequenceJob::SetStep +#define PORT_RNDTEXNEWOBJECT 0x80639904 // RndTex::NewObject +#define PORT_RNDMATNEWOBJECT 0x8063996c // RndMat::NewObject +#define PORT_RNDTEXSETBITMAP 0x8063fccc // RndTex::SetBitmap +#define PORT_RNDTEXSETBITMAP2 0x8063f830 // RndTex::SetBitmap2 +#define PORT_RNDTEXSETBITMAP3 0x8063fb2c // RndTex::SetBitmap3 +#define PORT_FILEPATHCONSTRUCTOR 0x8000ec5c // FilePath::__ct +#define PORT_MUSICLIBRARY_CT 0x8022d978 // MusicLibrary::__ct +#define PORT_MUSICLIBRARYMAT 0x80231c5c // MusicLibrary::Mat +#define PORT_NODESORTGETNODE 0x80279314 // MusicLibrary::GetNodeByIndex +#define PORT_GAMEGEMDB_CT 0x80460f64 // GameGemDB::__ct +#define PORT_ADDMULTIGEM 0x80461160 // GameGemDB::AddMultiGem +#define PORT_GETGAMELIST 0x8048553c // SongData::GetGameList +#define PORT_SONGSORTMGRGETSORT 0x80281b20 // SongSortMgr::GetSort +#define PORT_RNDMATSETDIFFUSETEX 0x8025ab90 // RndMat::SetDiffuseTex +#define PORT_DYNAMICTEX_CT 0x80292a70 // DynamicTex::__ct +#define PORT_DYNAMICTEX_DT 0x80292bcc // DynamicTex::__dt +#define PORT_MUSICLIBRARYONENTER 0x8022dd24 // MusicLibrary::OnEnter +#define PORT_MUSICLIBRARYONUNLOAD 0x8022e87c // MusicLibrary::OnExit +#define PORT_BINSTREAMWRITE 0x80342f48 // BinStream::Write +#define PORT_BINSTREAMREAD 0x80342e7c // BinStream::Read +#define PORT_BINSTREAMREADENDIAN 0x803430bc // BinStream::ReadEndian +#define PORT_BINSTREAMWRITEENDIAN 0x80343190 // BinStream::WriteEndian +#define PORT_SONGPARSERPITCHTOSLOT 0x8048e298 // SongParser::PitchToSlot +#define PORT_DATASET 0x8031b86c // DataSetSet +#define PORT_DATASETELEM 0x8031e9a0 // DataSetElem +#define PORT_DATAONELEM 0x8031dc40 // DataOnElem +#define PORT_HEAPINIT 0x80352cbc // Heap::Init +#define PORT_DATAREGISTERFUNC 0x8031b2b8 // DataRegisterFunc +#define PORT_FILEISLOCAL 0x802fb548 // FileIsLocal +#define PORT_FILEISDLC 0x802fb54c // FileIsDLC +#define PORT_SDMODECHECK 0x802F5638 // WiiContentMgr::SDModeCheck +// instance addresses +#define PORT_MODIFIERMGR_POINTER 0x808fda68 // pointer to ModifierManager +#define PORT_ROCKCENTRALGATEWAY 0x80900870 // address of RockCentralGateway +#define PORT_GDATAFUNCS 0x8091a528 // address of gDataFuncs +#define PORT_THEARCHIVE 0x80902234 // address of TheArchive (main ARK) +#define PORT_THEBANDUI 0x808fc2b0 // address of TheBandUI +#define PORT_NULLSYMBOL 0x808540e0 // address of gNullSymbol +#define PORT_THESONGDB 0x808fb170 // address of TheSongDB - TODO: check +#define PORT_THEMUSICLIBRARY 0x808fda84 // pointer to TheMusicLibrary +#define PORT_THESONGSORTMGR 0x808ff988 // pointer to TheSongSortMgr +#define PORT_THESONGMGR 0x808fbda4 // address of TheSongMgr +#define PORT_THEMETAPERFORMER 0x808fd6f8 // address of TheMetaPerformer +#define PORT_THEBANDUSERMGR 0x808f9350 // pointer to TheBandUserMgr +#define PORT_THEGAME 0x808f9758 // pointer to TheGame (you lost) +#define PORT_OBJECTDIRMAINDIR 0x8091b1e8 // ObjectDir::sMainDir +// string pointers +#define PORT_NASWII_AC_URL 0x808e2310 +#define PORT_NASWII_PR_URL 0x808e2390 +// wii stuff +#define PORT_WII_OS0_GLOBALS 0x80000000 // "os0"/"early" globals +#define PORT_WII_OS1_GLOBALS 0x80003000 // "os1"/"late" globals +#define PORT_CONFIGMEM2_52MB_INST 0x80769340 // ConfigMEM2_52MB 0x90000000 length param +#define PORT_CONFIGMEM2_56MB_INST 0x80769420 // ConfigMEM2_56MB 0x90000000 length param +#define PORT_CONFIGMEM2_64MB_INST 0x80769500 // ConfigMEM2_64MB 0x90000000 length param +#define PORT_ENABLEINSTSONMEM2LO16MB_INST 0x80769740 // EnableInstsOnMEM2Lo16MB 0x90000000 length param +#define PORT_VISETMODE_LI_28 0x8077b2ac // "li r28, 0x1" in VISetRevolutionModeSimple +#define PORT_VISETMODE_STB_28 0x8077b2b4 // "stb r28, 0x21(r1)" in VISetRevolutionModeSimple +#define PORT_VISETMODE_PATCH_CODE 0x800027a0 // stub area to insert the 480p fix code for VISetRevolutionModeSimple +#define PORT_GXSETCOPYFILTER_BEQ 0x807464b0 // "beq 0x40" in GXSetCopyFilter +#define PORT_OSFATAL_HALT_X_OFFSET 0x807673fc // 'li r7, 48' for the X offset in Halt (OSFatal) +#define PORT_OSFATAL_HALT_Y_OFFSET 0x80767404 // 'li r8, 100' for the Y offset in Halt (OSFatal) +#define PORT_SCREENREPORT_X_NEWLINE 0x80766894 // 'subi rX, rX, 48' for the X newline cutoff in ScreenReport +#define PORT_OSFATAL_HALT_OSREPORT 0x80767460 // call to OSReport in Halt (OSFatal) +#define PORT_OSFATAL_HALT_PPCHALT 0x80767464 // call to PPCHalt in Halt (OSFatal) +#define PORT_OSFATAL 0x80766cb0 // OSFatal +#define PORT_OSSETERRORHANDLER 0x80764b80 // OSSetErrorHandler +#define PORT_PPCHALT 0x80706390 // PPCHalt +#define PORT_OSRETURNTOMENU 0x8076a610 // OSReturnToMenu +#define PORT_OSREADROM 0x8076ae20 // OSReadROM +#define PORT_ARCINITHANDLE 0x806ffdb0 // ARCInitHandle +#define PORT_CONTENTINITHANDLETITLENAND 0x80733d60 // contentInitHandleTitleNAND +#define PORT_CNTRELEASEHANDLE 0x80734620 // CNTReleaseHandle +#define PORT_CNTREAD 0x807349d0 // CNTRead +#define PORT_ECGETCONTENTINFOS 0x807a9680 // EC_GetContentInfos + +// define logging functions +#define RB3E_PRINT printf + +#endif // RB3E_WII_BANK8 +#endif // RB3E_WII diff --git a/include/ports_wii_bank8.h b/include/ports_wii_bank8.h new file mode 100644 index 0000000..64a3271 --- /dev/null +++ b/include/ports_wii_bank8.h @@ -0,0 +1,192 @@ +/* + RB3Enhanced - ports_wii.h + Defines port addresses and platform-specific function definitions (for Wii "bank 8"/100901_A) + + Commented out defines are either not needed or not +*/ + +#ifdef RB3E_WII // Rock Band 3 Wii 2010-09-01 "bank 8" build +#ifdef RB3E_WII_BANK8 + +#include + +// instruction patch addresses +#define PORT_SONGLIMIT 0x8026fbf0 // call to "max_song_count" DataNode::_value +#define PORT_SONGBLACKLIST 0x80273298 // call to BandSongMgr::IsInExclusionList +#define PORT_DATAINITFUNCS_TAIL 0x8045d080 // blr of DataInitFuncs +#define PORT_FASTSTART_CHECK 0x8000e464 // beq after OptionBool("fast",0) in App::_ct +#define PORT_STRAPSCREEN_1 0x8000e5a4 // branch to CustomSplash::Show in App::_ct +#define PORT_STRAPSCREEN_2 0x8000e5b4 // branch to CustomSplash::EndShow in App::_ct +#define PORT_NASWII_HOST 0x80b2df90 // branch to the add header function in the DWCDL login function +#define PORT_CHARACTER_CLOTHES_CHECK 0x80348410 // (ProfileAssets::HasAsset) check to see if the goal required to select a piece of clothing has been unlocked +#define PORT_CHARACTER_CLOTHES_CHECK2 0x80348414 // check to see if the goal required to select a piece of clothing has been unlocked 2 +#define PORT_FACE_PAINT_CHECK 0x802b58b0 // check to see if face paint is unlocked (key_unlocked_face_paint BandProfile::HasCampaignKey) +#define PORT_TATTOO_CHECK 0x802b58cc // check to see if tattoos are unlocked (key_unlocked_tattoos BandProfile::HasCampaignKey) +#define PORT_VIDEO_VENUE_CHECK 0x802f0ce8 // check to see if video venues are unlocked (key_video_venues BandProfile::HasCampaignKey) +#define PORT_OPTIONSTR_DEFINE 0x8044219c // bl to OptionStr("define", NULL) in PreInitSystem +#define PORT_RUNLOOP_SPARE 0x80010788 // branch to a function that only has a "blr" in App::Run(WithoutDebugging) (branch to UIStats::Poll) +// #define PORT_MICCHECK 0x8024a4e8 // NOT NEEDED? a bne that throws an error on the song select screen if the mic is not connected +#define PORT_BIGSYMBOLFUNC_TAIL 0x804d3bac // blr after a function that initialises a bunch of symbols +#define PORT_UPDATEPRESENCEBLOCK_B 0x8021cbd0 // branch after the failure case in a function that calls UpdatePresence +// FIX ME INLINED? #define PORT_ADDTRACKVECTOR_BL 0x8064e288 // bl to vector_push_back inside of SongData::AddTrack // actually SongDB::AddTrack +#define PORT_GETGAMELIST 0x801dbfa0 // SongData::GetGameList // actually SongDB::GetGems +#define PORT_MULTIPLAYER_CRASH 0x80023fec // branch to a function that can crash in online multiplayer +#define PORT_MULTIPLAYER_FIX 0x80a2f0c4 // the function that doesn't crash +// #define PORT_LOADOBJS_BCTRL 0x827562e4 +// function patch addresses +#define PORT_SETDISKERROR 0x804400a0 // PlatformMgr::SetDiskError +#define PORT_NEWFILE 0x804204a0 // NewFile +#define PORT_SETTRACKSPEED 0x805f9060 // TrackPanelDirBase::UpdateTrackSpeed +#define PORT_SETSONGSPEED 0x8017fa80 // Game::SetMusicSpeed +#define PORT_MODIFIERMGR_CT 0x802f7130 // ModifierMgr::__ct +#define PORT_MODIFIERMGR_ACTIVE 0x802f7ab0 // ModifierMgr::GetModifier +#define PORT_SYMBOL_CT 0x804bd1c0 // Symbol::Symbol +#define PORT_LOCALIZE 0x8049b7f0 // Locale::Localize +#define PORT_SETVENUE 0x802f12f0 // MetaPerformer::SetVenue +#define PORT_EXECUTEDTA 0x803edbd0 // RockCentralGateway::ExecuteConfig +#define PORT_BANDLABELSETDISPLAYTEXT 0x80518360 // BandLabel::SetDisplayText +#define PORT_SETSONGANDARTISTNAME 0x80254650 // BandLabel::SetSongAndArtistName +#define PORT_SETSONGNAMEFROMNODE 0x80253ec0 // BandLabel::SetSongNameFromNode +#define PORT_KEYSONGUITAR 0x8031c1b0 // OvershellPanel::CanGuitarPlayKeys +// #define PORT_HMXFACTORYFUNCAT 0x8031b2f8 // FIXME HmxObjectFactoryFunc::_at - inlined in bank8? +#define PORT_WILLBENOSTRUM 0x8062b2d0 // GameGemList::WillBeNoStrum +#define PORT_ADDGAMEGEM 0x8062b460 // GameGemList::AddGameGem +#define PORT_SETADDRESS 0x80028d00 // Quazal::InetAddress::SetAddress +#define PORT_RANDOMINT 0x803fef30 // RandomInt(min, max) +#define PORT_GETWIDGETBYNAME 0x80135f30 // GemManager::GetWidgetByName +#define PORT_DATANODEEVALUATE 0x8045e030 // DataNode::Evaluate +#define PORT_GETSLOTCOLOR 0x801484d0 // TrackConfig::GetSlotColor +// FIX ME INLINED? #define PORT_ADDSMASHERPLATETOVECTOR 0x80bf0828 // AddSmasherPlateToVector +#define PORT_USBWIIGETTYPE 0x809fd840 // UsbWii::GetType +#define PORT_FILE_EXISTS 0x804207c0 // FileExists +#define PORT_QUEUEMESSAGE 0x80333140 // PassiveMessagesPanel::QueueMessage +#define PORT_SETSYSTEMLANGUAGE 0x80443570 // SetSystemLanguage +#define PORT_ISSUPPORTEDLANGUAGE 0x804434e0 // IsSupportedLanguage +#define PORT_DATAREADFILE 0x80452ef0 // DataReadFile +#define PORT_GAME_CT 0x8017d250 // Game::__ct +#define PORT_GAME_DT 0x8017dbd0 // Game::__dt +#define PORT_GAMEGETACTIVEPLAYER 0x8017fe40 // Game::GetActivePlayer +#define PORT_WIINETINIT_DNSLOOKUP 0x8043f1c0 // WiiNetInit::StartDNSLookup +// #define PORT_BUILDINSTRUMENTSELECTION 0x802478a8 // FIXME BuildInstrumentSelectionList(?) - actual name not known +// #define PORT_PREPARESOMEVECTORMAYBE 0x80247c58 // FIXME Prepares some vector, used by BuildInstrumentSelectionList +// #define PORT_SOMEVECTORPUSHBACKMAYBE 0x802484a8 // FIXME vector,class_stlpmtx_std::StlNodeAlloc_>_>::push_back +// FIX ME (?) #define PORT_VECTORPUSHBACK 0x801dced0 // vector_push_back +#define PORT_POSTPROC_DOPOST 0x809ef2d0 // WiiPostProc::DoPost +#define PORT_MUSICLIBRARYSELECTMAYBE 0x802ff140 // UNSURE MusicLibrary::TryToSetHighlight, Selects an entry in the Music Library screen - actual name not known +#define PORT_GETSYMBOLBYGAMEORIGIN 0x80374910 // RecentCmp::RecentTypeToOrigin +#define PORT_GETGAMEORIGINBYSYMBOL 0x80374870 // RecentCmp::OriginToRecentType +#define PORT_SONGSORTBYRECENT 0x803747d0 // RecentCmp::__ct +#define PORT_FILESTREAM_CT 0x80494fd0 // FileStream::__ct (the one that takes a char * path instead of a File object) +#define PORT_CHUNKSTREAM_CT 0x804929a0 // ChunkStream::__ct +#define PORT_GETBANDUSERFROMSLOT 0x80168010 // BandUserMgr::GetUserFromSlot +#define PORT_OVERSHELLPARTSELECTPROVIDERRELOAD 0x80322620 // OvershellPartSelectProvider::Reload +#define PORT_GETBANDUSERS 0x801683e0 // BandUserMgr::GetBandUsers +#define PORT_GETSONGSHORTNAME 0x802f5dd0 // MetaPerformer::GetSongSymbol +#define PORT_GETMETADATA 0x80271de0 // BandSongMgr::Data (function renamed from the original name to avoid any confusion with Data.h) +#define PORT_GETSONGID 0x8075af40 // DataArray::GetSongID ???, function used when adding songs to BandSongMgr +#define PORT_SONGMGRGETRANKEDSONGS 0x802726a0 // BandSongMgr::GetRankedSongs(?) - not sure on the real name of the function +#define PORT_GETSONGIDFROMSHORTNAME 0x80271ad0 // BandSongMgr::GetSongIDFromShortname +#define PORT_RNDPROPANIMSETFRAME 0x80928020 // RndPropAnim::SetFrame +#define PORT_DYNAMICCAST 0x80a38f54 // dynamic_cast +// #define PORT_OBJECTFINDUIPANEL 0x80101d74 // FIXME Object::Find - inlined on bank8? +#define PORT_JOYPADGETPADDATA 0x8042f6f0 // JoypadGetPadData +#define PORT_MEMALLOC 0x804a0a70 // MemAlloc +#define PORT_MEMFREE 0x804a12c0 // MemFree +#define PORT_SYMBOLPREINIT 0x804be150 // Symbol::PreInit +#define PORT_QUEUINGSOCKET_BIND 0x80068820 // Quazal::QueuingSocket::Bind +#define PORT_QUAZALSOCKET_BIND 0x80029dc0 // Quazal::Socket::Bind +#define PORT_METAMUSICISLOADED 0x809975d0 // MetaMusic::Loaded +#define PORT_METAMUSICLOAD 0x809970d0 // MetaMusic::Load +#define PORT_METAMUSICPOLL 0x80997610 // MetaMusic::Poll +#define PORT_METAMUSICSTART 0x80997990 // MetaMusic::Start +#define PORT_INITSONGMETADATA 0x8075a0c0 // InitSongMetadata +#define PORT_SONGMETADATACONSTRUCTOR 0x8075a1a0 // SongMetadata::__ct +#define PORT_SONGMETADATALOAD 0x8075a910 // SongMetadata::Load +#define PORT_UPDATEPRESENCE 0x8021c3d0 // PresenceMgr::UpdatePresence +#define PORT_STEPSEQUENCEJOBSETSTEP 0x80035cf0 // Quazal::StepSequenceJob::SetStep +#define PORT_RNDTEXNEWOBJECT 0x80931050 // RndTex::NewObject +#define PORT_RNDMATNEWOBJECT 0x80931010 // RndMat::NewObject +#define PORT_RNDTEXSETBITMAP 0x8093b810 // RndTex::SetBitmap +#define PORT_RNDTEXSETBITMAP2 0x8093b430 // RndTex::SetBitmap2 +#define PORT_RNDTEXSETBITMAP3 0x8093b5b0 // RndTex::SetBitmap3 +#define PORT_FILEPATHCONSTRUCTOR 0x80462460 // FilePath::__ct +#define PORT_MUSICLIBRARY_CT 0x802f9e10 // MusicLibrary::__ct +#define PORT_MUSICLIBRARYMAT 0x80300390 // MusicLibrary::Mat +#define PORT_NODESORTGETNODE 0x8036f5e0 // MusicLibrary::GetNodeByIndex //actually NodeSort::GetNode +#define PORT_GAMEGEMDB_CT 0x80628230 // GameGemDB::__ct +#define PORT_ADDMULTIGEM 0x806284e0 // GameGemDB::AddMultiGem +#define PORT_SONGSORTMGRGETSORT 0x8037a510 // SongSortMgr::GetSort +#define PORT_DYNAMICTEX_CT 0x80393bd0 // DynamicTex::__ct +#define PORT_DYNAMICTEX_DT 0x80393ef0 // DynamicTex::__dt +#define PORT_MUSICLIBRARYONENTER 0x802fa750 // MusicLibrary::OnEnter +#define PORT_MUSICLIBRARYONUNLOAD 0x802fb2a0 // MusicLibrary::OnExit +#define PORT_RNDMATSETDIFFUSETEX 0x808d36b0 // RndMat::SetDiffuseTex +#define PORT_BINSTREAMWRITE 0x804892b0 // BinStream::Write +#define PORT_BINSTREAMREAD 0x80489140 // BinStream::Read +#define PORT_BINSTREAMREADENDIAN 0x804894f0 // BinStream::ReadEndian +#define PORT_BINSTREAMWRITEENDIAN 0x80489640 // BinStream::WriteEndian +#define PORT_SONGPARSERPITCHTOSLOT 0x8065d3d0 // SongParser::PitchToSlot +#define PORT_DATASET 0x80454b00 // DataSet +#define PORT_DATASETELEM 0x80459240 // DataSetElem +#define PORT_DATAONELEM 0x80457bf0 // DataOnElem +#define PORT_HEAPINIT 0x8049f030 // Heap::Init +#define PORT_DATAREGISTERFUNC 0x804545e0 // DataRegisterFunc +#define PORT_FILEISLOCAL 0x80422560 // FileIsLocal +#define PORT_FILEISDLC 0x80422570 // FileIsDLC +#define PORT_SDMODECHECK 0x8041a790 // WiiContentMgr::SDModeCheck +// instance addresses +#define PORT_MODIFIERMGR_POINTER 0x80c904a8 // pointer to ModifierManager +#define PORT_ROCKCENTRALGATEWAY 0x80c91818 // address of RockCentralGateway +#define PORT_GDATAFUNCS 0x80cab890 // address of gDataFuncs +#define PORT_THEARCHIVE 0x80c92dd0 // address of TheArchive (main ARK) +#define PORT_THEBANDUI 0x80c8f800 // address of TheBandUI +#define PORT_NULLSYMBOL 0x80bb5d30 // address of gNullSymbol +#define PORT_THESONGDB 0x80c8f048 // address of TheSongDB - TODO: check +#define PORT_THEMUSICLIBRARY 0x80c904b8 // pointer to TheMusicLibrary +#define PORT_THESONGSORTMGR 0x80c91248 // pointer to TheSongSortMgr +#define PORT_THESONGMGR 0x80c8f61c // address of gSongMgr +#define PORT_THEMETAPERFORMER 0x80c90470 // address of TheMetaPerformer / MetaPerformer::sMetaPerformer +#define PORT_THEBANDUSERMGR 0x80c8e9b8 // pointer to TheBandUserMgr +#define PORT_THEGAME 0x80c8eb18 // pointer to TheGame (you lost) +#define PORT_OBJECTDIRMAINDIR 0x80cac538 // ObjectDir::sMainDir +// string pointers +#define PORT_NASWII_AC_URL 0x80c77fdc +#define PORT_NASWII_PR_URL 0x80c7805c +// wii stuff +#define PORT_WII_OS0_GLOBALS 0x80000000 // "os0"/"early" globals +#define PORT_WII_OS1_GLOBALS 0x80003000 // "os1"/"late" globals +#define PORT_CONFIGMEM2_52MB_INST 0x80aac4e0 // ConfigMEM2_52MB 0x90000000 length param +#define PORT_CONFIGMEM2_56MB_INST 0x80aac5c0 // ConfigMEM2_56MB 0x90000000 length param +#define PORT_CONFIGMEM2_64MB_INST 0x80aac6a0 // ConfigMEM2_64MB 0x90000000 length param +#define PORT_ENABLEINSTSONMEM2LO16MB_INST 0x80aac8e0 // EnableInstsOnMEM2Lo16MB 0x90000000 length param +#define PORT_VISETMODE_LI_28 0x80abee6c // "li r28, 0x1" in VISetRevolutionModeSimple +#define PORT_VISETMODE_STB_28 0x80abee74 // "stb r28, 0x21(r1)" in VISetRevolutionModeSimple +#define PORT_VISETMODE_PATCH_CODE 0x800027a0 // stub area to insert the 480p fix code for VISetRevolutionModeSimple +#define PORT_GXSETCOPYFILTER_BEQ 0x80a89650 // "beq 0x40" in GXSetCopyFilter +#define PORT_OSFATAL_HALT_X_OFFSET 0x80aaa59c // 'li r7, 48' for the X offset in Halt (OSFatal) +#define PORT_OSFATAL_HALT_Y_OFFSET 0x80aaa59c // 'li r8, 100' for the Y offset in Halt (OSFatal) +#define PORT_SCREENREPORT_X_NEWLINE 0x80aaa5a4 // 'subi rX, rX, 48' for the X newline cutoff in ScreenReport +#define PORT_OSFATAL_HALT_OSREPORT 0x80aaa600 // call to OSReport in Halt (OSFatal) +#define PORT_OSFATAL_HALT_PPCHALT 0x80aaa604 // call to PPCHalt in Halt (OSFatal) +#define PORT_OSFATAL 0x80aa9e50 // OSFatal +#define PORT_OSSETERRORHANDLER 0x80aa7d20 // OSSetErrorHandler +#define PORT_PPCHALT 0x80a49600 // PPCHalt +#define PORT_OSRETURNTOMENU 0x80aad970 // OSReturnToMenu +#define PORT_OSREADROM 0x80aae180 // OSReadROM +#define PORT_ARCINITHANDLE 0x80a43020 // ARCInitHandle +#define PORT_CONTENTINITHANDLETITLENAND 0x80a76fd0 // contentInitHandleTitleNAND +#define PORT_CNTRELEASEHANDLE 0x80a77890 // CNTReleaseHandle +#define PORT_CNTREAD 0x80a77c40 // CNTRead +#define PORT_ECGETCONTENTINFOS 0x80aed870 // EC_GetContentInfos +// bank8 specific stuff +#define PORT_BANK8_MEM2_RSO_ASSERT1 0x804428e8 +#define PORT_BANK8_MEM2_RSO_ASSERT2 0x80442940 +#define PORT_BANK8_MEM2_RSO_ASSERT3 0x804b42a8 +#define PORT_BANK8_MEM2_RSO_ASSERT4 0x804b4198 +#define PORT_BANK8_KEYBOARD_RESOLVED 0x80448750 // ResolvedModule? + +// define logging functions +#define RB3E_PRINT printf + +#endif // RB3E_WII_BANK8 +#endif // RB3E_WII diff --git a/include/ports_xbox360.h b/include/ports_xbox360.h new file mode 100644 index 0000000..4dc442b --- /dev/null +++ b/include/ports_xbox360.h @@ -0,0 +1,219 @@ +/* + RB3Enhanced - ports_xbox360.h + Defines port addresses and platform-specific function definitions (for 360 TU5) +*/ + +#ifdef RB3E_XBOX // Rock Band 3 Xbox 360 Title Update 5 + +// instruction patch addresses +#define PORT_SONGLIMIT 0x82579880 // call to "max_song_count" DataNode::_value +#define PORT_APP_CALL 0x82272e88 // call to App::_ct from main() +#define PORT_SONGBLACKLIST 0x82579098 // call to BandSongMgr::IsInExclusionList +#define PORT_DATAINITFUNCS_TAIL 0x82765980 // blr of DataInitFuncs +#define PORT_FASTSTART_CHECK 0x82270f40 // beq after OptionBool("fast",0) in App::_ct +#define PORT_SYSTEMINIT_BLANK 0x825113a4 // call to a stub function in SystemInit +#define PORT_XL_BYPASS_SG 0x82a88268 // lbz r11, bypassSecureGateway in Quazal::XboxClient::Login2 +#define PORT_RCG_POLL_LOGGEDIN 0x824f7178 // check for login status in RockCentralGateway::Poll +#define PORT_AUD_PATCH_CRASH 0x82b8c928 // patch somewhere to prevent Audition Mode crashes - blr +#define PORT_AUD_PATCH_NET1 0x82b8f7fc // patch somewhere to force Audition Mode to be connected (1) - nop +#define PORT_AUD_PATCH_NET2 0x82b8f80c // patch somewhere to force Audition Mode to be connected (2) - nop +#define PORT_AUD_PATCH_NET3 0x82b8f814 // patch somewhere to force Audition Mode to be connected (3) - nop +#define PORT_AUD_PATCH_NET4 0x82b8f81c // patch somewhere to force Audition Mode to be connected (4) - li r3, 0 +#define PORT_AUD_PATCH_NET5 0x82562638 // patch somewhere to force Audition Mode to be connected (5) - li r3, 1 +#define PORT_AUD_PATCH_REPL 0x82b8e978 // patch to allow Audition to recieve insecure packets from Magma +#define PORT_AUD_PATCH_HDD 0x82515dd4 // patch to make has_hard_drive always return true, probably broken +#define PORT_AUD_PATCH_UNK 0x823f6074 // idk +#define PORT_AUD_HANDLE_MESSAGES 0x82563edc // part of AuditionMgr::Handle that handles disconnected from Rock Central/XBL/etc. messages +#define PORT_AUD_HANDLE_MSG_JUMP 0x82564048 // address to jump PORT_AUD_HANDLE_MESSAGES to, to allow audition mode without GoCentral +#define PORT_AUD_INVALID_DATA_CHECK 0x825629fc // check for invalid data in a song submitted to audition mode +#define PORT_XNQOS_PROBE1 0x823ee7f8 // instruction that checks xnqos probe results +#define PORT_XNQOS_PROBE2 0x823ee800 // instruction that checks xnqos probe results +#define PORT_XNQOS_PROBE3 0x823ee80c // instruction that checks xnqos probe results +#define PORT_VDP_DISABLE1 0x82b39ba0 // nop over VDP packet sending +#define PORT_VDP_DISABLE2 0x82b39e60 // nop over VDP packet sending +#define PORT_VDP_DISABLE3 0x82b3a5e4 // nop over VDP packet sending +#define PORT_VDP_DISABLE4 0x82b3a5f0 // nop over VDP packet sending +#define PORT_SESSION_MASK_CHECK 0x82652acc // beq in while loop for instrument mask check +#define PORT_CHARACTER_CLOTHES_CHECK 0x82655148 // check to see if the goal required to select a piece of clothing has been achieved or not +#define PORT_CHARACTER_CLOTHES_CHECK2 0x8265514c // check to see if the goal required to select a piece of clothing has been achieved or not 2 +#define PORT_FACE_PAINT_CHECK 0x82614a60 // check to see if face paint is unlocked +#define PORT_TATTOO_CHECK 0x82614a9c // check to see if tattoos are unlocked +#define PORT_VIDEO_VENUE_CHECK 0x82581634 // check to see if video venues are unlocked +#define PORT_OPTIONSTR_DEFINE 0x82510cc8 // bl to OptionStr("define", NULL) in PreInitSystem +#define PORT_RENDER_RES_X_PATCH1 0x8273bf20 // instruction patch to force render width +#define PORT_RENDER_RES_X_PATCH2 0x8273bf24 // instruction patch to force render width +#define PORT_RENDER_RES_Y_PATCH1 0x8273bedc // instruction patch to force render height +#define PORT_RUNLOOP_SPARE 0x822700e0 // branch to a function that only has a "blr" in App::Run(WithoutDebugging) +#define PORT_SONG_ID_EVALUATE 0x827aa7d4 // branch to DataNode::Evaluate in SongMetadata::__ct +#define PORT_LOADOBJS_BCTRL 0x827562e4 // bctrl to Object::PreLoad insie of DirLoader::LoadObjs +#define PORT_SONGMGR_ISDEMO_CHECK 0x82575f9c // "bne" after IsUGC check inside SongMgr::IsDemo +#define PORT_STAGEKIT_EXISTS 0x8228d03c // StageKit check. nop over to allow for fog command without a physical StageKit connected. +#define PORT_ADDTRACKVECTOR_BL 0x82777b70 // bl to vector_push_back inside of SongData::AddTrack +#define PORT_GETGAMELIST 0x82770730 // SongData::GetGameList +#define PORT_VERTEX_READ_1 0x82418704 // call to Vector3::operator>> to read vertex position +#define PORT_VERTEX_READ_2 0x82418748 // call to Vector3::operator>> to read vertex normals +#define PORT_MULTIPLAYER_CRASH 0x82ae6880 // branch to a function that can crash in online multiplayer +#define PORT_MULTIPLAYER_FIX 0x8282b238 // the function that doesn't crash +#define PORT_QUAZAL_BREAKPOINT 0x828410c0 // address to DbgBreakPoint in Quazal::Platform::Breakpoint +#define PORT_MAINSEH 0x82272e60 // address to __CxxFrameHandler above the main() function +// function patch addresses +#define PORT_SETDISKERROR 0x82516320 // PlatformMgr::SetDiskError +#define PORT_APP_RUN 0x82272e90 // App::Run +#define PORT_APP_RUNNODEBUG 0x82270080 // App::RunWithoutDebugging +#define PORT_APP_CT 0x82270e68 // App::_ct +#define PORT_NEWFILE 0x825173e0 // NewFile +#define PORT_SETTRACKSPEED 0x827dd118 // TrackPanelDirBase::UpdateTrackSpeed +#define PORT_SETSONGSPEED 0x82678C88 // Game::SetMusicSpeed +#define PORT_MODIFIERMGR_CT 0x82589c48 // ModifierManager::__ct +#define PORT_MODIFIERMGR_ACTIVE 0x82588d80 // ModifierManager::ModifierActive +#define PORT_SYMBOL_CT 0x827c0728 // Symbol::Symbol +#define PORT_LOCALIZE 0x827c96d8 // Locale::Localize +#define PORT_ADDGAMEGEM 0x8278e530 // GameGemList::AddGameGem +#define PORT_SONGDATAADDMULTIGEM 0x827719b0 // SongData::AddMultiGem +#define PORT_WILLBENOSTRUM 0x8278cbb0 // GameGemList::WillBeNoStrum +#define PORT_SETVENUE 0x8257d1c0 // MetaPerformer::SetVenue(?) (actual func name is not known) +#define PORT_ISUGCPLUS 0x8259e890 // function that checks song source(?) +#define PORT_KEYSONGUITAR 0x825b50f8 // function that checks "key_keys_on_guitar" +#define PORT_EXECUTEDTA 0x824f7e50 // RockCentralGateway::ExecuteConfig +#define PORT_BANDLABELSETDISPLAYTEXT 0x823406f8 // BandLabel::SetDisplayText +#define PORT_SETSONGANDARTISTNAME 0x825c66f8 // BandLabel::SetSongAndArtistName +#define PORT_SETSONGNAMEFROMNODE 0x825c56a0 // BandLabel::SetSongNameFromNode +#define PORT_DATANODEEVALUATE 0x8274ae98 // DataNode::Evaluate +#define PORT_DATAARRAYFINDARRAY 0x8274c5a0 // DataArray::FindArray +#define PORT_DATAARRAYFINDDATA 0x8274c7f0 // DataArray::FindData +#define PORT_HMXFACTORYFUNCAT 0x82359f28 // HmxObjectFactoryFunc::_at +#define PORT_SETADDRESS 0x82aeb888 // Quazal::InetAddress::SetAddress +#define PORT_XL_USESECURESOCKETS 0x82a8eca8 // Inet::UseSecureSockets +#define PORT_XL_XSESSIONCREATE 0x82a69c90 // XSessionCreate +#define PORT_XL_XSESSIONJOINREMOTE 0x82a69fb0 // XSessionJoinRemote +#define PORT_XL_XSESSIONMODIFY 0x82a69e40 // XSessionModify +#define PORT_XL_XSESSIONSEARCHEX 0x82a6a490 // XSessionSearchEx +#define PORT_XL_XINVITEGETACCEPTEDINFO 0x82a6a7c8 // XInviteGetAcceptedInfo +#define PORT_RANDOMINT 0x824f2f90 // RandomInt(min, max) +#define PORT_GETWIDGETBYNAME 0x82b9b880 // GemManager::GetWidgetByName +#define PORT_GETSLOTCOLOR 0x82baa308 // TrackConfig::GetSlotColor +#define PORT_ADDSMASHERPLATETOVECTOR 0x82356980 // AddSmasherPlateToVector +#define PORT_ARCHIVE_CT 0x82514408 // Archive::_ct +#define PORT_ARCHIVE_SETLOCATIONHARDDRIVE 0x82512b00 // Archive::SetLocationHardDrive +#define PORT_ARCHIVE_MERGE 0x82513ee8 // Archive::Merge +#define PORT_ARCHIVE_DT 0x82513af8 // Archive::_dt +#define PORT_FILE_EXISTS 0x825175b0 // FileExists +#define PORT_QUEUEMESSAGE 0x82628e50 // PassiveMessagesPanel::QueueMessage +#define PORT_SETSYSTEMLANGUAGE 0x82510590 // SetSystemLanguage +#define PORT_ISSUPPORTEDLANGUAGE 0x82510510 // IsSupportedLanguage +#define PORT_DATAREADFILE 0x8276c700 // DataReadFile +#define PORT_STAGEKIT_SET_STATE 0x82524DE0 // StageKit::SetState(?) - actual name not known +#define PORT_GETSONGIDFROMSHORTNAME 0x82577140 // BandSongMgr::GetSongIDFromShortname +#define PORT_GETMETADATA 0x827a8e30 // BandSongMgr::Data (function renamed from the original name to avoid any confusion with Data.h) +#define PORT_GETSONGSHORTNAME 0x8257c498 // MetaPerformer::GetSongShortname(?) - actual name not known +#define PORT_GAME_CT 0x8267bf30 // Game::__ct +#define PORT_GAME_DT 0x8267b1f0 // Game::__dt +#define PORT_GAMEGETACTIVEPLAYER 0x82678e88 // Game::GetActivePlayer +#define PORT_OVERSHELLPARTSELECTPROVIDERRELOAD 0x82668c70 // OvershellPartSelectProvider::Reload +#define PORT_GETBANDUSERS 0x82683b78 // BandUserMgr::GetBandUsers +#define PORT_GETBANDUSERFROMSLOT 0x82682b60 // BandUserMgr::GetBandUserFromSlot +#define PORT_GETSONGID 0x827a87f0 // GetSongID, function used when adding songs to BandSongMgr +#define PORT_SONGMGRGETRANKEDSONGS 0x82577340 // BandSongMgr::GetRankedSongs(?) - not sure on the real name of the function +#define PORT_BUILDINSTRUMENTSELECTION 0x82668c70 // BuildInstrumentSelectionList(?) - actual name not known +#define PORT_PREPARESOMEVECTORMAYBE 0x82796d90 // Prepares some vector, used by BuildInstrumentSelectionList +#define PORT_SOMEVECTORPUSHBACKMAYBE 0x82b6aa10 // vector,class_stlpmtx_std::StlNodeAlloc_>_>::push_back +#define PORT_VECTORPUSHBACK 0x82b5f808 // vector_push_back +#define PORT_POSTPROC_DOPOST 0x82b89a08 // NgPostProc::DoPost +#define PORT_MUSICLIBRARYSELECTMAYBE 0x8253EB00 // Selects an entry in the Music Library screen - actual name not known +#define PORT_GETSYMBOLBYGAMEORIGIN 0x8265bb78 // SongSortByRecent::GetSymbolByGameOrigin +#define PORT_GETGAMEORIGINBYSYMBOL 0x8265b910 // SongSortByRecent::GetGameOriginBySymbol +#define PORT_SONGSORTBYRECENT 0x8265bde8 // RecentCmp::__ct +#define PORT_FILESTREAM_CT 0x827c3340 // FileStream::__ct (the one that takes a char * path instead of a File object) +#define PORT_CHUNKSTREAM_CT 0x827ca488 // ChunkStream::__ct +#define PORT_RNDPROPANIMSETFRAME 0x82426dd0 // RndPropAnim::SetFrame +#define PORT_DYNAMICCAST 0x8282a0c8 // dynamic_cast +#define PORT_OBJECTFINDUIPANEL 0x82537430 // Object::Find +#define PORT_JOYPADGETCACHEDXINPUTCAPS 0x82531F08 // JoypadGetCachedXInputCaps +#define PORT_JOYPADGETPADDATA 0x82524998 // JoypadGetPadData +#define PORT_MEMFREE 0x827bc430 // MemFree +#define PORT_MEMALLOC 0x827bcd38 // MemAlloc +#define PORT_SYMBOLPREINIT 0x827c04f8 // Symbol::PreInit +#define PORT_QUEUINGSOCKET_BIND 0x82b397b0 // Quazal::QueuingSocket::Bind +#define PORT_QUAZALSOCKET_BIND 0x82b1a830 // Quazal::Socket::Bind +#define PORT_MEMPRINTOVERVIEW 0x827bc838 // MemPrintOverview +#define PORT_MEMPRINT 0x827bc970 // MemPrint +#define PORT_MEMNUMHEAPS 0x827bb628 // MemNumHeaps +#define PORT_METAMUSICISLOADED 0x8270fab8 // MetaMusic::Loaded +#define PORT_METAMUSICLOAD 0x82710ab0 // MetaMusic::Load +#define PORT_METAMUSICPOLL 0x82711438 // MetaMusic::Poll +#define PORT_METAMUSICSTART 0x82711a00 // MetaMusic::Start +#define PORT_INITSONGMETADATA 0x827aa450 // InitSongMetadata +#define PORT_SONGMETADATACONSTRUCTOR 0x827aa6e8 // SongMetadata::__ct +#define PORT_SONGMETADATALOAD 0x825a3f58 // SongMetadata::Load +#define PORT_UPDATEPRESENCE 0x82680430 // PresenceMgr::UpdatePresence +#define PORT_STEPSEQUENCEJOBSETSTEP 0x82af92b8 // Quazal::StepSequenceJob::SetStep +#define PORT_RNDTEXNEWOBJECT 0x82273de0 // RndTex::NewObject +#define PORT_RNDMATNEWOBJECT 0x8240f5d0 // RndMat::NewObject +#define PORT_RNDTEXSETBITMAP 0x823ff678 // RndTex::SetBitmap +#define PORT_RNDTEXSETBITMAP2 0x823ff240 // RndTex::SetBitmap2 +#define PORT_RNDTEXSETBITMAP3 0x823ff510 // RndTex::SetBitmap3 +#define PORT_FILEPATHCONSTRUCTOR 0x82270210 // FilePath::__ct +#define PORT_MUSICLIBRARY_CT 0x825451c8 // MusicLibrary::__ct +#define PORT_MUSICLIBRARYMAT 0x8253b440 // MusicLibrary::Mat +#define PORT_NODESORTGETNODE 0x825bf708 // MusicLibrary::GetNodeByIndex +#define PORT_GAMEGEMDB_CT 0x827931e0 // GameGemDB::__ct +#define PORT_ADDMULTIGEM 0x827930d8 // GameGemDB::AddMultiGem +#define PORT_SONGSORTMGRGETSORT 0x82595ff8 // SongSortMgr::GetSort +#define PORT_DYNAMICTEX_CT 0x825f2318 // DynamicTex::__ct +#define PORT_DYNAMICTEX_DT 0x825f2210 // DynamicTex::__dt +#define PORT_RNDMATSETDIFFUSETEX 0x8238b130 // RndMat::SetDiffuseTex +#define PORT_MUSICLIBRARYONENTER 0x82542238 // MusicLibrary::OnEnter +#define PORT_MUSICLIBRARYONUNLOAD 0x82540450 // MusicLibrary::OnExit +#define PORT_BINSTREAMWRITE 0x827c4f58 // BinStream::Write +#define PORT_BINSTREAMREAD 0x827c4ea8 // BinStream::Read +#define PORT_BINSTREAMREADENDIAN 0x827c5058 // BinStream::ReadEndian +#define PORT_BINSTREAMWRITEENDIAN 0x827c5098 // BinStream::WriteEndian +#define PORT_SONGPARSERPITCHTOSLOT 0x82783c20 // SongParser::PitchToSlot +#define PORT_DATASET 0x8275d670 // DataSet +#define PORT_DATASETELEM 0x82760b38 // DataSetElem +#define PORT_DATAONELEM 0x8275ff50 // DataOnElem +#define PORT_DATANODEGETOBJ 0x8274b088 // DataNode::GetObj +#define PORT_DATAARRAYEXECUTE 0x8274d198 // DataArray::Execute +#define PORT_DXRND_SUSPEND 0x8273A370 // DxRnd::Suspend +#define PORT_XBOXCONTENT_CONSTRUCTOR 0x8251fb40 // XboxContent::__ct +#define PORT_CACHEMGRXBOX_MOUNTASYNC 0x827d7b38 // CacheMgrXbox::MountAsync +// instance addresses +#define PORT_MODIFIERMGR_POINTER 0x82dfec08 // pointer to ModifierManager +#define PORT_ROCKCENTRALGATEWAY 0x82cc8f60 // address of RockCentralGateway +#define PORT_GDATAFUNCS 0x82e05d30 // address of gDataFuncs +#define PORT_THEARCHIVE 0x82cc9c60 // address of TheArchive (main ARK) +#define PORT_THEBANDUI 0x82dfd2b0 // address of TheBandUI +#define PORT_NULLSYMBOL 0x82c71838 // address of gNullSymbol +#define PORT_THESONGDB 0x82e023f8 // address of TheSongDB +#define PORT_THESONGMGR 0x82dfe7b4 // address of TheSongMgr +#define PORT_THEMETAPERFORMER 0x82dfe954 // address of TheMetaPerformer +#define PORT_THEBANDUSERMGR 0x82e023b8 // address of TheBandUserMgr +#define PORT_THESONGSORTMGR 0x82dfee5c // pointer to TheSongSortMgr +#define PORT_THEMUSICLIBRARY 0x82dfd3a8 // pointer to TheMusicLibrary +#define PORT_THEGAME 0x82e02128 // pointer to TheGame (you lost) +#define PORT_OBJECTDIRMAINDIR 0x82e054b8 // ObjectDir::sMainDir +#define PORT_MESH_GREV 0x82cc2638 // address of RndMesh::gRev +#define PORT_DXRND 0x82e04b38 // address of TheDxRnd +// import function stubs +#define PORT_XEKEYSSETKEY_STUB 0x82c4c47c +#define PORT_XEKEYSAESCBC_STUB 0x82c4c48c +#define PORT_SOCKET_STUB 0x8284da48 +#define PORT_XNETSTARTUP 0x8284d7e8 +#define PORT_XNETXNADDRTOINADDR 0x8284d840 +#define PORT_XNETCONNECT 0x8284d890 +#define PORT_XNETUNREGISTERKEY 0x8284d830 +#define PORT_XNETUNREGISTERINADDR 0x8284d870 +#define PORT_XNETREGISTERKEY 0x8284d820 +#define PORT_XNETGETTITLEXNADDR 0x8284d968 +#define PORT_XNETQOSLOOKUP 0x8284d8f8 +#define PORT_XAMUSERGETSIGNININFO 0x82c4be1c +#define PORT_XAMUSERGETSIGNINSTATE 0x82c4bcfc +#define PORT_XAMUSERCHECKPRIVILEGE 0x82c4bd1c +#define PORT_XAMSHOWFRIENDSUI 0x8283d710 +#define PORT_XAMLOADERTERMINATETITLE 0x82c4bccc + +// define logging functions +void DbgPrint(const char *s, ...); +#define RB3E_PRINT DbgPrint + +#endif // RB3E_XBOX diff --git a/include/rb3/App.h b/include/rb3/App.h index 71d3569..470ca4e 100644 --- a/include/rb3/App.h +++ b/include/rb3/App.h @@ -2,6 +2,6 @@ #define _APP_H extern void AppConstructor(void *thisApp, int argc, char **argv); -extern void *NewFile(char *iFilename, int iMode); // doesn't belong to a namespace? +extern void *NewFile(char *fileName, int flags); // doesn't belong to a namespace? #endif // _APP_H \ No newline at end of file diff --git a/include/rb3/BandUser.h b/include/rb3/BandUser.h index 0e01eb6..b3c7720 100644 --- a/include/rb3/BandUser.h +++ b/include/rb3/BandUser.h @@ -60,11 +60,11 @@ typedef struct _BandUser // BandUser members int unknown_0x0; int unknown_0x4; - Difficulty mDifficulty; + Difficulty difficulty; char mUnknown; char pad[3]; - TrackType mTrackType; - ControllerType mControllerType; + TrackType trackType; + ControllerType controllerType; char mTrackSelected; char mUnknown2; char pad2[2]; diff --git a/include/rb3/BinStream.h b/include/rb3/BinStream.h index a8f4e4e..4b90d2e 100644 --- a/include/rb3/BinStream.h +++ b/include/rb3/BinStream.h @@ -35,4 +35,9 @@ struct _BinStream BinStream_vtable *vtable; }; +extern void BinstreamWrite(void *thisBinStream, void *data, int size); +extern void BinstreamRead(void *thisBinStream, void *data, int size); +extern void BinstreamReadEndian(void *thisBinStream, void *data, int size); +extern void BinstreamWriteEndian(void *thisBinStream, void *data, int size); + #endif // _BINSTREAM_H \ No newline at end of file diff --git a/include/rb3/Cache.h b/include/rb3/Cache.h new file mode 100644 index 0000000..94231a4 --- /dev/null +++ b/include/rb3/Cache.h @@ -0,0 +1,20 @@ + +#ifndef _CACHE_H +#define _CACHE_H + +typedef struct _Cache +{ + void *vtable; + int mOpCur; + int mLastResult; +} Cache; + +typedef struct _CacheMgr +{ + void *vtable; + int mCacheIDStore[3]; // vector + int mOpCur; + int mLastResult; +} CacheMgr; + +#endif // _CACHE_H diff --git a/include/rb3/ChunkStream.h b/include/rb3/ChunkStream.h index 08f9ba5..a40a79c 100644 --- a/include/rb3/ChunkStream.h +++ b/include/rb3/ChunkStream.h @@ -38,6 +38,6 @@ struct _ChunkStream ChunkStream_vtable *vtable; }; -ChunkStream *ChunkStreamConstructor(ChunkStream *thisChunkStream, char *fileName, FileType fileType, int chunkSize, char compress, Platform platform, char cached); +ChunkStream *ChunkStreamConstructor(ChunkStream *thisChunkStream, char *fileName, int fileType, int chunkSize, char compress, Platform platform, char cached); #endif // _CHUNKSTREAM_H \ No newline at end of file diff --git a/include/rb3/Data.h b/include/rb3/Data.h index 873026c..946c59d 100644 --- a/include/rb3/Data.h +++ b/include/rb3/Data.h @@ -7,20 +7,28 @@ typedef union _DataNode_Value { int intVal; float floatVal; - int *dataArray; + void *dataArray; int *object; char *string; } DataNode_Value; typedef enum _DataNode_Type { +#ifndef RB3E_WII INT_VALUE = 0, +#else + EMPTY = 0, +#endif FLOAT_VALUE = 1, VAR = 2, FUNC = 3, OBJECT = 4, SYMBOL = 5, +#ifndef RB3E_WII EMPTY = 6, +#else + INT_VALUE = 6, +#endif IFDEF = 7, ELSE = 8, ENDIF = 9, @@ -70,4 +78,16 @@ extern DataArray *DataReadFile(char *file, int dtb); extern DataArray *DataFindArray(DataArray *data, Symbol name); extern int DataFindData(DataArray *data, Symbol name, DataNode *out); +extern DataNode *DataSet(DataNode *ret, DataArray *array); +extern DataNode *DataSetElem(DataNode *ret, DataArray *array); +extern DataNode *DataOnElem(DataNode *ret, DataArray *array); + +extern DataNode *DataArrayExecute(DataNode *ret, DataArray *thisArr); + +extern void *DataNodeGetObj(DataNode *node); + +// inlined on 360 +typedef DataNode *(*DTAFunction_t)(DataNode *node, DataArray *args); +extern void DataRegisterFunc(Symbol name, DTAFunction_t func); + #endif // _DATA_H diff --git a/include/rb3/File.h b/include/rb3/File.h index c3111f4..8a21686 100644 --- a/include/rb3/File.h +++ b/include/rb3/File.h @@ -1,15 +1,102 @@ #ifndef _FILE_H #define _FILE_H -typedef enum _FileType +#include "String.h" + +typedef struct _File File; + +typedef void *(*FileDestructor_t)(File *thisFile); +typedef String *(*FileFilename_t)(String *str, File *thisFile); +typedef int (*FileRead_t)(File *thisFile, void *iData, int iBytes); +typedef char (*FileReadAsync_t)(File *thisFile, void *iData, int iBytes); +typedef int (*FileWrite_t)(File *thisFile, void *iData, int iBytes); +typedef char (*FileWriteAsync_t)(File *thisFile, void *iData, int iBytes); +typedef int (*FileSeek_t)(File *thisFile, int iOffset, int iSeekType); +typedef int (*FileTell_t)(File *thisFile); +typedef void (*FileFlush_t)(File *thisFile); +typedef char (*FileEof_t)(File *thisFile); +typedef char (*FileFail_t)(File *thisFile); +typedef int (*FileSize_t)(File *thisFile); +typedef int (*FileUncompressedSize_t)(File *thisFile); +typedef char (*FileReadDone_t)(File *thisFile, int *oBytes); +typedef char (*FileWriteDone_t)(File *thisFile, int *oBytes); + +typedef struct _File_vtable +{ +#ifdef RB3E_WII + void *rtti; + void *null; +#endif + FileDestructor_t destructor; +#ifdef RB3E_PS3 + FileDestructor_t destructor2; +#endif + FileFilename_t Filename; + FileRead_t Read; + FileReadAsync_t ReadAsync; + FileWrite_t Write; + FileWriteAsync_t WriteAsync; + FileSeek_t Seek; + FileTell_t Tell; + FileFlush_t Flush; + FileEof_t Eof; + FileFail_t Fail; + FileSize_t Size; + FileUncompressedSize_t UncompressedSize; + FileReadDone_t ReadDone; + FileWriteDone_t WriteDone; +} File_vtable; + +struct File +{ + File_vtable *vtable; +}; + +typedef struct _AsyncFile AsyncFile; + +typedef void (*AsyncFile_OpenAsync_t)(AsyncFile *thisFile); +typedef char (*AsyncFile_OpenDone_t)(AsyncFile *thisFile); +typedef void (*AsyncFile_WriteAsync_t)(AsyncFile *thisFile, void *iData, int iBytes); +typedef char (*AsyncFile_WriteDone_t)(AsyncFile *thisFile); +typedef void (*AsyncFile_SeekToTell_t)(AsyncFile *thisFile); +typedef void (*AsyncFile_ReadAsync_t)(AsyncFile *thisFile, void *iData, int iBytes); +typedef char (*AsyncFile_ReadDone_t)(AsyncFile *thisFile); +typedef void (*AsyncFile_Close_t)(AsyncFile *thisFile); + +typedef struct _AsyncFile_vtable { - kRead = 0, - kWrite = 1, - kReadNoArk = 2, - kAppend = 3 -} FileType; + File_vtable file; + AsyncFile_OpenAsync_t _OpenAsync; + AsyncFile_OpenDone_t _OpenDone; + AsyncFile_WriteAsync_t _WriteAsync; + AsyncFile_WriteDone_t _WriteDone; + AsyncFile_SeekToTell_t _SeekToTell; + AsyncFile_ReadAsync_t _ReadAsync; + AsyncFile_ReadDone_t _ReadDone; + AsyncFile_Close_t _Close; +} AsyncFile_vtable; + +struct AsyncFile +{ + AsyncFile_vtable *vtable; + int mMode; + char mFail; + char unk9; + String mFilename; + unsigned int mTell; + int mOffset; + unsigned int mSize; + unsigned int mUCSize; + char *mBuffer; + char *mData; + int mBytesLeft; + int mBytesRead; +}; // flags can be used to check the existence of files both inside and outside the ARK, similar to how the NewFile flags work -char FileExists(char *iFilename, int iMode); +char FileExists(char *path, int flags); + +char FileIsDLC(char *path); +char FileIsLocal(char *path); -#endif // _FILE_H \ No newline at end of file +#endif // _FILE_H diff --git a/include/rb3/FileStream.h b/include/rb3/FileStream.h index ccdce2b..d159cc2 100644 --- a/include/rb3/FileStream.h +++ b/include/rb3/FileStream.h @@ -39,6 +39,6 @@ struct _FileStream char unk[0x256]; }; -FileStream *FileStreamConstructor(FileStream *thisFileStream, char *fileName, FileType fileType, char littleEndian); +FileStream *FileStreamConstructor(FileStream *thisFileStream, char *fileName, int fileType, char littleEndian); #endif // _FILESTREAM_H \ No newline at end of file diff --git a/include/rb3/Locale.h b/include/rb3/Locale.h index 4d67514..94991bc 100644 --- a/include/rb3/Locale.h +++ b/include/rb3/Locale.h @@ -2,7 +2,7 @@ #define _LOCALE_H // I believe "fail" being set to 1 means that if there is no locale, it will return the raw Symbol and not a blank string or vice versa -extern char *Localize(int thisLocale, Symbol token, int fail); +extern char *Localize(int thisLocale, Symbol sym, int fail); extern void SetSystemLanguage(Symbol lang, int cheat); #endif // _LOCALE_H \ No newline at end of file diff --git a/include/rb3/MetaPerformer.h b/include/rb3/MetaPerformer.h index 9745d41..2d07f56 100644 --- a/include/rb3/MetaPerformer.h +++ b/include/rb3/MetaPerformer.h @@ -11,10 +11,10 @@ typedef struct _MetaPerformer extern void SetVenue(int *thisMetaPerformer, Symbol venue); #ifdef RB3E_XBOX -extern void GetSongSymbol(Symbol *symOut, int metaPerformer); +extern void GetSongShortname(Symbol *symOut, int metaPerformer); #else // this calling convention makes more sense? wtf microsoft -extern Symbol GetSongSymbol(int metaPerformer); +extern Symbol GetSongShortname(int metaPerformer); #endif #endif // _METAPERFORMER_H \ No newline at end of file diff --git a/include/rb3/ModifierManager.h b/include/rb3/ModifierManager.h index 5d803f2..e7c41b2 100644 --- a/include/rb3/ModifierManager.h +++ b/include/rb3/ModifierManager.h @@ -11,7 +11,7 @@ typedef struct _Modifier bool enabled; } Modifier; -extern Modifier *ModifierIsActive(int thisModifierManager, Symbol symbol, bool defaultValue); +extern Modifier *ModifierActive(int thisModifierManager, Symbol symbol, bool defaultValue); extern void *ModifierManagerConstructor(int thisModifierManager, int unk); #endif // _MODIFIERMANAGER_H \ No newline at end of file diff --git a/include/rb3/MusicLibrary.h b/include/rb3/MusicLibrary.h index de9a9df..a58cfa7 100644 --- a/include/rb3/MusicLibrary.h +++ b/include/rb3/MusicLibrary.h @@ -16,7 +16,7 @@ typedef struct _MusicLibrary // Jumps to a given entry in the music library void MusicLibrarySelect(int theMusicLibrary, Symbol entryName, int sortType, int unk_r6); int *MusicLibraryConstructor(int *thisMusicLibrary, int *songPreview); -int *MusicLibraryConstructorHook(MusicLibrary *thisMusicLibrary, int *songPreview); +int *MusicLibraryConstructorHook(int *thisMusicLibrary, int *songPreview); void MusicLibraryOnEnter(void* thisMusicLibrary); void MusicLibraryOnUnload(void* thisMusicLibrary); void MusicLibraryOnEnterHook(void* thisMusicLibrary); diff --git a/include/rb3/Quazal/StepSequenceJobStep.h b/include/rb3/Quazal/StepSequenceJobStep.h new file mode 100644 index 0000000..3adb8c6 --- /dev/null +++ b/include/rb3/Quazal/StepSequenceJobStep.h @@ -0,0 +1,14 @@ +#ifndef _STEPSEQUENCEJOBSTEP_H +#define _STEPSEQUENCEJOBSTEP_H + +typedef struct _StepSequenceJobStep +{ + void *jobFunc; // the function that gets called when the job is ran (?) + int unk; +#ifdef RB3E_WII + int unk2; // field only appears to exist on Wii +#endif + char *jobName; +} StepSequenceJobStep; + +#endif // _STEPSEQUENCEJOBSTEP_H \ No newline at end of file diff --git a/include/rb3/SongMetadata.h b/include/rb3/SongMetadata.h index 0e7459a..d3ae181 100644 --- a/include/rb3/SongMetadata.h +++ b/include/rb3/SongMetadata.h @@ -4,6 +4,7 @@ #include "rb3/BinStream.h" #include "rb3/Data.h" #include "String.h" +#include "Symbol.h" // technically BandSongMetadata? typedef struct _SongMetadata @@ -14,10 +15,10 @@ typedef struct _SongMetadata #else char unknown[0x28]; #endif - Symbol mShortName; - int mSongID; + Symbol shortname; + int song_id; char unknown2[0x4]; - Symbol mGameOrigin; + Symbol gameOrigin; char unknown3[0x10]; String title; String artist; @@ -25,11 +26,11 @@ typedef struct _SongMetadata #ifdef RB3E_WII char unknown6[0x10]; #else - char unknown6[0x18]; + char unknown6[0x10]; // hotfixed from 0x18, then 0x14? what is happening #endif - char *genre; + Symbol genre; int animTempo; - char *vocalGender; + Symbol vocalGender; int lengthMs; } SongMetadata; diff --git a/include/rb3/SongParser.h b/include/rb3/SongParser.h new file mode 100644 index 0000000..e78cb9b --- /dev/null +++ b/include/rb3/SongParser.h @@ -0,0 +1,20 @@ +#ifndef _SONGPARSER_H +#define _SONGPARSER_H + +#include "Symbol.h" +#include "BandUser.h" + +// TODO: fill out the rest of this, there is a lot of useful stuff in this class that could be useful for controlling how songs load +typedef struct _SongParser +{ +#ifdef RB3E_WII + char pad[0xdc]; +#else + char pad[0xf8]; +#endif + TrackType mTrackType; +} SongParser; + +extern int SongParserPitchToSlot(SongParser *thisSongParser, int pitch, int *difficulty, int tick); + +#endif // _SONGPARSER_H diff --git a/include/rb3/SortNode.h b/include/rb3/SortNode.h index 1b14d71..73ed37f 100644 --- a/include/rb3/SortNode.h +++ b/include/rb3/SortNode.h @@ -5,6 +5,10 @@ #include "Symbol.h" #include "SongMetadata.h" +typedef struct _SortNode SortNode; +typedef struct _ShortcutNodeVtable ShortcutNodeVtable; +typedef struct _SongRecord SongRecord; + typedef enum _SongNodeType { kNodeNone = 0, @@ -17,7 +21,26 @@ typedef enum _SongNodeType kNodeStoreSong = 7 } SongNodeType; -typedef struct _SortNode SortNode; +typedef struct _Unknown2 +{ +#ifdef RB3E_WII + char unknown[0xfc]; +#else + char unknown[0x108]; +#endif + SongMetadata *metaData; +} Unknown2; + +struct _SortNode +{ + ShortcutNodeVtable *vtable; +#ifdef RB3E_WII + char something[0x30]; +#else + char something[0x3c]; +#endif + SongRecord *record; +}; typedef int (*ReturnsZero_t)(); typedef void (*OnlyReturns_t)(); @@ -54,15 +77,4 @@ typedef struct _SongRecord SongMetadata *metaData; } SongRecord; -struct _SortNode -{ - ShortcutNodeVtable *vtable; -#ifdef RB3E_WII - char something[0x30]; -#else - char something[0x3c]; -#endif - SongRecord *record; -}; - #endif // _SORTNODE_H \ No newline at end of file diff --git a/include/rb3/UI/UIPanel.h b/include/rb3/UI/UIPanel.h index 292876c..caee228 100644 --- a/include/rb3/UI/UIPanel.h +++ b/include/rb3/UI/UIPanel.h @@ -3,12 +3,6 @@ typedef struct _UIPanel UIPanel; -typedef enum _PanelState -{ - kPanelDown = 0, - kPanelUp = 1 -} PanelState; - struct _UIPanel { int *vtable; // 0x0 @@ -19,10 +13,10 @@ struct _UIPanel int unk5; // 0x14 int unk6; // 0x18 #ifdef RB3E_WII - PanelState mState; // 0x1c + int is_up; // 0x1c #else - int unk7; // 0x1c - PanelState mState; // 0x20 + int unk7; // 0x1c + int is_up; // 0x20 #endif }; diff --git a/include/rb3/UsbWii.h b/include/rb3/UsbWii.h index cf0b5c3..b82514a 100644 --- a/include/rb3/UsbWii.h +++ b/include/rb3/UsbWii.h @@ -3,12 +3,56 @@ #ifdef RB3E_WII typedef struct _HIDDevice { - int deviceID; - unsigned short vendorID; - unsigned short productID; - int token; + int fd; + unsigned short vid; + unsigned short pid; + unsigned char iInterface; + unsigned char endpoint_address_in; + unsigned char endpoint_address_out; + unsigned char inst; + void *p_hdd; + void *p_hcd; + void *p_hid; + void *p_hed0; + void *p_hed1; } HIDDevice; +typedef struct _UsbDevice { + HIDDevice *dd; // 0x0 + unsigned int type; // 0x4 + unsigned int state; // 0x8 + unsigned int flags; // 0xC + unsigned int inactivity; // 0x10 + unsigned char unk_0x14[0x2]; // 0x14 + short ledNum; // 0x16 + unsigned int unk6; // 0x18 + unsigned int buttonMask; // 0x1C + unsigned short lstickX; // 0x20 + unsigned short lstickY; // 0x22 + unsigned short rstickX; // 0x24 + unsigned short rstickY; // 0x26 + unsigned char pressures[4]; // 0x28 + unsigned char extended[0x10]; // 0x2C + unsigned char unk_0x3C[0x4]; // 0x3C + unsigned char packet[0x40]; // 0x40 + unsigned char unk_0x80[0x80]; // 0x80 +} UsbDevice; // 0x100 + +typedef enum _UsbType { + kUsbNone = 0, + kUsbGuitar = 1, + kUsbGuitarRb2 = 2, + kUsbDrums = 3, + kUsbDrumsRb2 = 4, + kUsbMidiGuitarMustang = 5, + kUsbMidiGuitarSquire = 6, + kUsbMidiKeyboard = 7, + kUsbUnused = 8, + kUsbMidiKeyboardMpa = 9, + kUsbMidiDrums = 10, + kUsbTypeMax = 11, +} UsbType; + extern int UsbWiiGetType(HIDDevice *device); #endif // RB3E_WII diff --git a/include/rb3/WiiMemMgr.h b/include/rb3/WiiMemMgr.h new file mode 100644 index 0000000..3a0d6dc --- /dev/null +++ b/include/rb3/WiiMemMgr.h @@ -0,0 +1,32 @@ +#ifndef _WIIMEMMGR_H +#define _WIIMEMMGR_H +#ifdef RB3E_WII + +typedef struct _FreeBlock FreeBlock; + +typedef struct _FreeBlock +{ + unsigned int mSizeWords; + unsigned int mTimeStamp; + FreeBlock *mNextBlock; +} FreeBlock; + +typedef struct _Heap +{ + FreeBlock *freeBlockChain; + int *mStart; + const char *mName; + int mSizeWords; + int mNum; + int mIsHandleHeap; + int mDebugLevel; + int mStrategy; + int mAllowTemp; + int unk_1; + int unk_2; + int unk_3; + int unk_4; +} Heap; + +#endif // RB3E_WII +#endif // _WIIMEMMGR_H diff --git a/include/rb3/XboxCache.h b/include/rb3/XboxCache.h new file mode 100644 index 0000000..4e5034f --- /dev/null +++ b/include/rb3/XboxCache.h @@ -0,0 +1,35 @@ + +#ifndef _XBOXCACHE_H +#define _XBOXCACHE_H +#ifdef RB3E_XBOX + +#include +#include "rb3/Cache.h" +#include "rb3/String.h" +#include "rb3/Symbol.h" + +typedef struct _CacheIDXbox +{ + void *vtable; + String mStrCacheName; + XCONTENT_DATA mContentData; +} CacheIDXbox; + +typedef struct _CacheMgrXbox +{ + CacheMgr root; + int mEnumerationHandle; + XOVERLAPPED mOverlapped; + CacheIDXbox **mOutCacheIDPtr; + void *mPendingCachePtr; + void *mPendingObject; + CacheIDXbox *mPendingCacheID; + XCONTENT_DATA mContentData; + String mCacheSearchName; + String unk; +} CacheMgrXbox; + +int CacheMgrXbox_MountAsync(CacheMgrXbox *thisCacheMgr, CacheIDXbox *cacheID, Cache **outCache, void *object); + +#endif // RB3E_XBOX +#endif // _XBOXCACHE_H diff --git a/include/rb3/XboxContent.h b/include/rb3/XboxContent.h new file mode 100644 index 0000000..c875da8 --- /dev/null +++ b/include/rb3/XboxContent.h @@ -0,0 +1,41 @@ + +#ifndef _XBOXCONTENT_H +#define _XBOXCONTENT_H +#ifdef RB3E_XBOX + +#include +#include "rb3/String.h" +#include "rb3/Symbol.h" + +typedef enum _ContentState +{ + kUnmounted = 0, + kNeedsMounting = 1, + kMounting = 2, + kUnmounting = 3, + kMounted = 4, + kDeleting = 6, + kDeleted = 7, + kFailed = 8, +} ContentState; + +typedef struct _XboxContent +{ + void *vtable; + XOVERLAPPED *mOverlapped; + XCONTENT_CROSS_TITLE_DATA mTitleData; + int mLicenseBits; + char mLicenseBitsValid; // bool + String mRoot; + String mMountPath; + int mState; + int mPadNum; + char mDeleted; // bool + Symbol mFileName; + int mLRM; // what? +} XboxContent; + +XboxContent *XboxContentConstruct(XboxContent *thisXboxContent, XCONTENT_CROSS_TITLE_DATA *titleData, int contentIndex, int padNum, char needsMount); + +#endif // RB3E_XBOX +#endif // _XBOXCONTENT_H diff --git a/include/rb3_include.h b/include/rb3_include.h index ee2a055..6e6662f 100644 --- a/include/rb3_include.h +++ b/include/rb3_include.h @@ -32,6 +32,7 @@ #include "rb3/Random.h" #include "rb3/RockCentralGateway.h" #include "rb3/SongMetadata.h" +#include "rb3/SongParser.h" #include "rb3/SongSortByRecentEntry.h" #include "rb3/SongSortMgr.h" #include "rb3/SortNode.h" diff --git a/include/rb3e_include.h b/include/rb3e_include.h index 2f63dd2..79e748c 100644 --- a/include/rb3e_include.h +++ b/include/rb3e_include.h @@ -4,7 +4,10 @@ #include "rb3_include.h" #include "config.h" #include "crc32.h" +#include "DataDebug.h" #include "DTAFunctions.h" +#include "FileSD.h" +#include "FileWiiNAND.h" #include "GameHooks.h" #include "GemHooks.h" #include "GlobalSymbols.h" @@ -25,6 +28,7 @@ #include "RndPropAnimHooks.h" #include "SetlistHooks.h" #include "SongHooks.h" +#include "SongParserHooks.h" #include "SongSort.h" #include "SpeedHooks.h" #include "utilities.h" diff --git a/include/rb3enhanced.h b/include/rb3enhanced.h index f68c383..1d57254 100644 --- a/include/rb3enhanced.h +++ b/include/rb3enhanced.h @@ -23,13 +23,27 @@ int RB3E_FileExists(char *filename); int RB3E_OpenFile(char *filename, char readWrite); int RB3E_FileSize(int file); int RB3E_ReadFile(int file, int offset, void *buffer, int size); +int RB3E_WriteFile(int file, int offset, void *buffer, int size); void RB3E_CloseFile(int file); int RB3E_CreateThread(void *address, void *arg, int stack_size); void RB3E_Sleep(int ms); int RB3E_RelaunchGame(); +void RB3E_FlushCache(void * address, unsigned int size); +int RB3E_DeleteSongCache(); + +// stub function at the start of the .text segment - doubles as the start of _functions.c +void RB3EBase(); + +// stub function at the end of _functions.c after all stubs are made +void RB3EStubEnd(); // loaded song count - done in SongHooks.c extern int RB3E_LoadedSongCount; // Emulator detection int RB3E_IsEmulator(); + +// StageKit set state, needed for fixing LED bug. +#ifdef RB3E_XBOX + void StagekitSetStateHook(int state1, int state2); +#endif diff --git a/include/rvl/cnt.h b/include/rvl/cnt.h new file mode 100644 index 0000000..545756f --- /dev/null +++ b/include/rvl/cnt.h @@ -0,0 +1,147 @@ +#ifndef _RVL_CNT_H +#define _RVL_CNT_H + +#include +#include +#include "emvolution/dvd.h" + +// -- ARC/U8, on-disk -- + +// 'U.8-' +#define ARC_MAGIC 0x55AA382D + +// https://wiki.tockdom.com/wiki/U8_(File_Format) +// header of ARC/U8 files +typedef struct _u8_header_t { + unsigned int magic; + int first_node; + int nodes_size; + int data_offset; + unsigned int reserved[4]; +} u8_header_t; // 0x20 + +// the types of nodes within an ARC +typedef enum _u8_node_type_t { + arcnode_file = 0, + arcnode_dir = 1 +} u8_node_type_t; + +// info about a file node +typedef struct _u8_node_file_t { + int data_offset; + int data_size; +} u8_node_file_t; +// info about a directory node +typedef struct _u8_node_dir_t { + int parent_node_index; + int next_node; +} u8_node_dir_t; + +// an ARC node itself +typedef struct _u8_node_t { + int node_type : 8; + int string_offset : 24; + union { + u8_node_file_t file; + u8_node_dir_t dir; + }; +} u8_node_t; // 0xC + +// -- ARC, internally -- + +typedef struct _arc_handle_t { + u8_header_t *header; + u8_node_t *nodes; + uint8_t *file; + unsigned int count; + const char *strings; + unsigned int fstSize; + int entrynum; +} arc_handle_t; + +typedef struct _arc_file_info_t { + arc_handle_t *handle; + unsigned int offset; + unsigned int size; +} arc_file_info_t; + +typedef struct _arc_entry_t { + arc_handle_t *handle; + unsigned int path; + int type; + const char *name; +} arc_entry_t; + +// -- CNT, internally -- + +typedef enum _cnt_handle_type { + cnthandle_nand = 1, + cnthandle_dvd = 2 +} cnt_handle_type; + +typedef struct _cnt_handle_nand { + arc_handle_t ArcHandle; + long FileDescriptor; + void *allocator; +} cnt_handle_nand; + +typedef struct _cnt_handle_dvd { + unsigned char backing[0x24]; +} cnt_handle_dvd; + +typedef struct _cnt_handle { + union { + cnt_handle_nand nand; + cnt_handle_dvd dvd; + }; + unsigned char type; +} cnt_handle; + +typedef struct _cnt_dir { + unsigned char backing[0x10]; + unsigned char type; +} cnt_dir; + +typedef struct _cnt_dir_entry { + union { + arc_entry_t arc; + }; + unsigned char type; +} cnt_dir_entry; + +typedef struct _cnt_file_info_nand { + cnt_handle_nand *CntHandle; + unsigned int startoffset; + unsigned int length; + long readOffset; +} cnt_file_info_nand; + +typedef struct _cnt_file_info_dvd { + DVDFileInfo fileInfo; + long readOffset; +} cnt_file_info_dvd; + +typedef struct _cnt_file_info { + union { + cnt_file_info_nand nand; + cnt_file_info_dvd dvd; + }; + unsigned char type; +} cnt_file_info; + +int contentInitHandleTitleNAND(unsigned long long title_id, unsigned int content_index, cnt_handle *handle, MEMAllocator *allocator); +int CNTReleaseHandle(cnt_handle *handle); + +int CNTOpen(cnt_handle *handle, const char *path, cnt_file_info *file_info); +int CNTGetLength(cnt_file_info *file_info); +int CNTClose(cnt_file_info *file_info); +int CNTRead(cnt_file_info *file_info, void *buffer, int length); +int CNTSeek(cnt_file_info *file_info, int position, int seek_from); + +int CNTOpenDir(cnt_handle *handle, const char *path, cnt_dir *dir); +int CNTReadDir(cnt_dir *dir, cnt_dir_entry *entry); +int CNTCloseDir(cnt_dir *dir); + +int ARCInitHandle(void *bin, arc_handle_t *handle); + +#endif // _RVL_CNT_H diff --git a/include/rvl/ec.h b/include/rvl/ec.h new file mode 100644 index 0000000..c7ae95d --- /dev/null +++ b/include/rvl/ec.h @@ -0,0 +1,26 @@ +#ifndef _RVL_EC_H +#define _RVL_EC_H + +typedef struct _ec_content_info_t { + unsigned int flags; + unsigned short index; + unsigned short type; + unsigned long long size; +} ec_content_info_t; + +typedef struct _ec_title_info_t { + unsigned long long titleId; + int isTmdPresent; + int isOnDevice; + unsigned int type; + unsigned int version; + unsigned int occupiedUserBlocks; + unsigned int occupiedUserInodes; + unsigned int occupiedSysBlocks; + unsigned int occupiedSysInodes; +} ec_title_info_t; + +int EC_GetContentInfos(unsigned long long titleId, ec_content_info_t *contentInfos, int *numContentInfos); +int EC_DeleteContents(unsigned long long titleId, unsigned short *contents, int numContents); + +#endif // _RVL_EC_H diff --git a/include/rvl/mem.h b/include/rvl/mem.h new file mode 100644 index 0000000..2cbf51f --- /dev/null +++ b/include/rvl/mem.h @@ -0,0 +1,20 @@ +#ifndef _RVL_MEM_H + +typedef struct MEMAllocator; + +typedef void* (*MEMAllocatorAllocFunc)(struct MEMAllocator* allocator, unsigned int size); +typedef void (*MEMAllocatorFreeFunc)(struct MEMAllocator* allocator, void* block); + +typedef struct _MEMAllocatorFuncs { + MEMAllocatorAllocFunc allocFunc; + MEMAllocatorFreeFunc freeFunc; +} MEMAllocatorFuncs; + +typedef struct _MEMAllocator { + MEMAllocatorFuncs *funcs; + void *heap; + unsigned int heapParam1; + unsigned int heapParam2; +} MEMAllocator; + +#endif // _RVL_MEM_H diff --git a/include/rvl/tmd.h b/include/rvl/tmd.h new file mode 100644 index 0000000..b883fab --- /dev/null +++ b/include/rvl/tmd.h @@ -0,0 +1,44 @@ +#ifndef _RVL_TMD_H +#define _RVL_TMD_H + +typedef struct _tmd_header_t { + unsigned int signature_type; // assumes RSA-2048 / 0x00010001 + unsigned char signature[0x100]; + unsigned char padding[0x3C]; + unsigned char issuer[0x40]; + unsigned char version; + unsigned char ca_crl_version; + unsigned char signer_crl_version; + unsigned char vwii; + unsigned long long ios_version; + union { + unsigned long long id; + struct { + unsigned int pad; + unsigned char code[4]; + } name; + } title_id; + unsigned int title_type; + unsigned short group_id; + unsigned short padding2; + unsigned short region; + unsigned char ratings[0x10]; + unsigned char padding3[0xC]; + unsigned char ipc_mask[0xC]; + unsigned char padding4[0x12]; + unsigned int access_rights; + unsigned short title_version; + unsigned short num_contents; + unsigned short boot_index; + unsigned short padding5; +} __attribute__((packed)) tmd_header_t; + +typedef struct _tmd_content_t { + unsigned int id; + unsigned short index; + unsigned short type; + unsigned long long size; + unsigned char SHA1[0x14]; +} __attribute__((packed)) tmd_content_t; + +#endif // _RVL_TMD_H diff --git a/include/rvl/wad.h b/include/rvl/wad.h new file mode 100644 index 0000000..1e6cfbc --- /dev/null +++ b/include/rvl/wad.h @@ -0,0 +1,45 @@ +#ifndef _RVL_WAD_H +#define _RVL_WAD_H + +// .bin backup files on the SD card + +// at offset 0x4 in the file, NOT 0x0 +#define BACKUP_WAD_MAGIC 0x426B0001 + +typedef struct _backup_wad_header_t { + unsigned int header_size; + unsigned short type; + unsigned short version; + unsigned int console_id; + unsigned int save_file_count; + unsigned int save_file_data_size; + unsigned int content_tmd_size; + unsigned int content_data_size; + unsigned int backup_area_size; + unsigned char included_contents[0x40]; //bitfield + union { + unsigned long long id; + struct { + unsigned int pad; + unsigned char code[4]; + } name; + } title_id; + unsigned char mac_address[0x06]; + unsigned char reserved[0x02]; + unsigned char pad[0x10]; +} backup_wad_header_t; + +/* + files are aligned to 0x40 byte boundaries + +-------------------------------+ + | backup_wad_header_t header | + +-------------------------------+ + | tmd_t tmd of content_tmd_size | + | padding to 0x40 bytes | + +-------------------------------+ + | binary of content_data_size | + | padding to 0x40 bytes | + +-------------------------------+ +*/ + +#endif // _RVL_WAD_H diff --git a/include/wii_cnt_crypt.h b/include/wii_cnt_crypt.h new file mode 100644 index 0000000..6812683 --- /dev/null +++ b/include/wii_cnt_crypt.h @@ -0,0 +1,29 @@ +/* + RB3Enhanced - wii_cnt_crypt.h +*/ + +#ifdef RB3E_WII + +#include + +typedef struct _RB3E_CNTFileSD { + int fd; // 0x0 + uint32_t titleId; // 0x4 + int contentLength; // 0x8 + int startOffset; // 0xC + uint16_t contentIndex; // 0x10 + uint16_t padding; // 0x12 - probably not needed + struct AES_ctx *aesCtx; // 0x14 + uint8_t *arcHeader; // 0x18 + int lastBlockIndex; // 0x1c + uint8_t aesKey[0x10]; // 0x20 + uint8_t lastBlock[0x10]; // 0x30 + uint8_t lastBlockEnc[0x10]; // 0x40 +} RB3E_CNTFileSD; + +RB3E_CNTFileSD *RB3E_OpenCNTFileSD(const char *filepath, unsigned long long titleid, unsigned int index); +void RB3E_CloseCNTFileSD(RB3E_CNTFileSD *file); +void RB3E_CNTFileRead(RB3E_CNTFileSD *file, int offset, uint8_t *buffer, int length); +void TryToLoadPRNGKeyFromFile(); + +#endif // RB3E_WII diff --git a/include/wii_cnt_hooks.h b/include/wii_cnt_hooks.h new file mode 100644 index 0000000..1f84958 --- /dev/null +++ b/include/wii_cnt_hooks.h @@ -0,0 +1,9 @@ +/* + RB3Enhanced - wii_cnt_hooks.h +*/ + +#ifdef RB3E_WII + +void InitCNTHooks(); + +#endif diff --git a/include/xbox360.h b/include/xbox360.h index d00e717..c2635e2 100644 --- a/include/xbox360.h +++ b/include/xbox360.h @@ -28,6 +28,8 @@ DWORD XexGetProcedureAddress(HANDLE ModuleHandle, DWORD Ordinal, PVOID OutAddres int XeCryptSha(void *input_1, int input_1_size, void *input_2, int input_2_size, void *input_3, int input_3_size, void *output, int output_size); int XeCryptHmacSha(void *key, int key_size, void *input_1, int input_1_size, void *input_2, int input_2_size, void *input_3, int input_3_size, void *output, int output_size); int XeKeysConsolePrivateKeySign(unsigned char hash[0x14], unsigned char output_cert_sig[0x228]); +// memory management +int MmIsAddressValid(DWORD address); // structure for xnet typedef struct _XnpRouteEntry_t @@ -51,4 +53,5 @@ void XNetLogonGetExtendedStatus(unsigned int *login_status, unsigned int *login_ void InitCryptoHooks(); void InitLivelessHooks(); void InitInputHooks(); +void InitContentHooks(); #endif // RB3E_XBOX diff --git a/make_xbox.bat b/make_xbox.bat deleted file mode 100644 index 7b2beab..0000000 --- a/make_xbox.bat +++ /dev/null @@ -1,41 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM Set compilation variables. -set OUTNAME=RB3Enhanced -set BUILD_DIR=build_xbox -set OUT_DIR=out -set SRC_DIR=source -set INC_DIR=include -set LIBS=xapilib.lib xboxkrnl.lib xnet.lib xonline.lib -set DEFINES=-D _XBOX -D NDEBUG -D RB3E_XBOX -D RB3E -D RB3EDEBUG -REM Set the SDK paths to use. -set INCLUDE=%XEDK%\include\xbox -set LIB=%XEDK%\lib\xbox -set BINARYPATH=%XEDK%\bin\win32 - -REM Warning, since this build script is just to tide X360-only people over without the full toolchain. -echo It is highly recommended that you install the build tools as noted in BUILDING.md, and use "make xbox" to build an Xbox 360 version. -timeout /t 3 - -REM Create folders. -@mkdir %BUILD_DIR% >nul 2>&1 -@mkdir %OUT_DIR% >nul 2>&1 - -REM Pre-build script -call scripts\version.bat > source\version.h - -REM Compile all .c source files. -for %%a in (%SRC_DIR%/*.c) do "%BINARYPATH%\cl.exe" -c -Zi -nologo -W3 -WX- -Ox -Os %DEFINES% -GF -Gm- -MT -GS- -Gy -fp:fast -fp:except- -Zc:wchar_t -Zc:forScope -GR- -openmp- -FI"%XEDK%/include/xbox/xbox_intellisense_platform.h" -Fd"%BUILD_DIR%/" -I "%INC_DIR%" -Fo"%BUILD_DIR%/%%a.obj" -TC %SRC_DIR%/%%a - -REM Fetch all created .obj files. -set OBJECTS= -for %%a in (%BUILD_DIR%/*.c.obj) do set OBJECTS=%BUILD_DIR%/%%a !OBJECTS! -echo Linking DLL... -"%BINARYPATH%\link.exe" -ERRORREPORT:PROMPT -INCREMENTAL:NO -NOLOGO %LIBS% -MANIFESTUAC:"level='asInvoker' uiAccess='false'" -DEBUG -STACK:"262144","262144" -OPT:REF -OPT:ICF -TLBID:1 -RELEASE -dll -entry:"_DllMainCRTStartup" -XEX:NO -OUT:"%BUILD_DIR%/%OUTNAME%.exe" -PDB:"%BUILD_DIR%/%OUTNAME%.pdb" -IMPLIB:"%BUILD_DIR%/%OUTNAME%" %OBJECTS% - -REM Create a final XEXDLL. -echo Creating XEXDLL... -"%BINARYPATH%\imagexex.exe" -nologo -config:"xex.xml" -out:"%OUT_DIR%/%OUTNAME%.dll" "%BUILD_DIR%/%OUTNAME%.exe" - -endlocal \ No newline at end of file diff --git a/source/DTAFunctions.c b/source/DTAFunctions.c index 78ebfd1..0558093 100644 --- a/source/DTAFunctions.c +++ b/source/DTAFunctions.c @@ -11,9 +11,43 @@ #include "config.h" #include "net_events.h" #include "rb3/Data.h" +#include "rb3/SongMetadata.h" +#include "rb3/BandSongMgr.h" #include "rb3enhanced.h" +#include "net.h" +#include "version.h" -DataNode *PrintToDebugger(DataNode *node, DataArray *args) +DataNode *DTAGetRB3EBuildTag(DataNode *node, DataArray *args) +{ + node->type = SYMBOL; + node->value.string = globalSymbols.buildtag.sym; + return node; +} + +DataNode *DTAGetRB3ECommit(DataNode *node, DataArray *args) +{ + node->type = SYMBOL; + node->value.string = globalSymbols.commit.sym; + return node; +} + +DataNode *DTAGetAPIVersion(DataNode *node, DataArray *args) +{ + /* + This API version should be incremented every time a DTA function gets + added, has functionality modified, or removed, as well as every time a + major feature is added/removed from RB3E or a new major version is + released. + + API version history: + 0 - 29/09/2025 - Initial API version. + */ + node->type = INT_VALUE; + node->value.intVal = 0; + return node; +} + +DataNode *DTAPrintToDebugger(DataNode *node, DataArray *args) { DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); switch (firstArg->type) @@ -36,21 +70,21 @@ DataNode *PrintToDebugger(DataNode *node, DataArray *args) return node; } -// Get configuration values -DataNode *GetMusicSpeed(DataNode *node, int *args) +DataNode *DTAGetMusicSpeed(DataNode *node, DataArray *args) { node->type = FLOAT_VALUE; node->value.floatVal = config.SongSpeedMultiplier; return node; } -DataNode *GetTrackSpeed(DataNode *node, int *args) + +DataNode *DTAGetTrackSpeed(DataNode *node, DataArray *args) { node->type = FLOAT_VALUE; node->value.floatVal = config.TrackSpeedMultiplier; return node; } -// Set configuration values -DataNode *ChangeMusicSpeed(DataNode *node, DataArray *args) + +DataNode *DTAChangeMusicSpeed(DataNode *node, DataArray *args) { DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); switch (firstArg->type) @@ -71,7 +105,8 @@ DataNode *ChangeMusicSpeed(DataNode *node, DataArray *args) node->value.intVal = 1; return node; } -DataNode *ChangeTrackSpeed(DataNode *node, DataArray *args) + +DataNode *DTAChangeTrackSpeed(DataNode *node, DataArray *args) { DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); switch (firstArg->type) @@ -92,6 +127,7 @@ DataNode *ChangeTrackSpeed(DataNode *node, DataArray *args) node->value.intVal = 1; return node; } + DataNode *DTASetVenue(DataNode *node, DataArray *args) { DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); @@ -118,21 +154,21 @@ DataNode *DTASetVenue(DataNode *node, DataArray *args) return node; } -DataNode *DTAIsEmulator(DataNode *node, int *args) +DataNode *DTAIsEmulator(DataNode *node, DataArray *args) { node->type = INT_VALUE; node->value.intVal = RB3E_IsEmulator(); return node; } -DataNode *DTARelaunchGame(DataNode *node, int *args) +DataNode *DTARelaunchGame(DataNode *node, DataArray *args) { node->type = INT_VALUE; node->value.intVal = RB3E_RelaunchGame(); return node; } -DataNode *DTAGetSongCount(DataNode *node, int *args) +DataNode *DTAGetSongCount(DataNode *node, DataArray *args) { node->type = INT_VALUE; node->value.intVal = RB3E_LoadedSongCount; @@ -168,17 +204,183 @@ DataNode *DTASendModData(DataNode *node, DataArray *args) return node; } +DataNode *DTAGetSongName(DataNode *node, DataArray *args) +{ + // return "Darude Sandstorm"; + DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); + Symbol rb3e_no_song_name; + SymbolConstruct(&rb3e_no_song_name, "rb3e_no_song_name"); + node->type = SYMBOL; + node->value.string = rb3e_no_song_name.sym; + if (firstArg->type != INT_VALUE) + RB3E_MSG("Invalid types for arguments to rb3e_get_song_name! 1st: %i", firstArg->type); + else + { + SongMetadata *songmet = GetMetadata((BandSongMgr *)PORT_THESONGMGR, firstArg->value.intVal); + RB3E_DEBUG("rb3e_get_song_name %i", firstArg->value.intVal); + if (songmet == NULL) { + RB3E_MSG("!! FAILED TO GET SONG METADATA FOR %i !!", firstArg->value.intVal); + } else { + Symbol titleSym; + SymbolConstruct(&titleSym, songmet->title.buf); + node->value.string = titleSym.sym; + } + } + return node; +} + +DataNode *DTAGetArtist(DataNode *node, DataArray *args) +{ + DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); + Symbol rb3e_no_artist; + SymbolConstruct(&rb3e_no_artist, "rb3e_no_artist"); + node->type = SYMBOL; + node->value.string = rb3e_no_artist.sym; + if (firstArg->type != INT_VALUE) + RB3E_MSG("Invalid types for arguments to rb3e_get_artist! 1st: %i", firstArg->type); + else + { + SongMetadata *songmet = GetMetadata((BandSongMgr *)PORT_THESONGMGR, firstArg->value.intVal); + RB3E_DEBUG("rb3e_get_artist %i", firstArg->value.intVal); + if (songmet == NULL) { + RB3E_MSG("!! FAILED TO GET SONG METADATA FOR %i !!", firstArg->value.intVal); + } else { + Symbol artistSym; + SymbolConstruct(&artistSym, songmet->artist.buf); + node->value.string = artistSym.sym; + } + } + return node; +} + +DataNode *DTAGetAlbum(DataNode *node, DataArray *args) +{ + DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); + Symbol rb3e_no_album; + SymbolConstruct(&rb3e_no_album, "rb3e_no_album"); + node->type = SYMBOL; + node->value.string = rb3e_no_album.sym; + if (firstArg->type != INT_VALUE) + RB3E_MSG("Invalid types for arguments to rb3e_get_album! 1st: %i", firstArg->type); + else + { + SongMetadata *songmet = GetMetadata((BandSongMgr *)PORT_THESONGMGR, firstArg->value.intVal); + RB3E_DEBUG("rb3e_get_album %i", firstArg->value.intVal); + if (songmet == NULL) { + RB3E_MSG("!! FAILED TO GET SONG METADATA FOR %i !!", firstArg->value.intVal); + } else { + Symbol albumSym; + SymbolConstruct(&albumSym, songmet->album.buf); + node->value.string = albumSym.sym; + } + } + return node; +} + +DataNode *DTAGetGenre(DataNode *node, DataArray *args) +{ + DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); + Symbol rb3e_no_genre; + SymbolConstruct(&rb3e_no_genre, "rb3e_no_genre"); + node->type = SYMBOL; + node->value.string = rb3e_no_genre.sym; + if (firstArg->type != INT_VALUE) + RB3E_MSG("Invalid types for arguments to rb3e_no_genre! 1st: %i", firstArg->type); + else + { + SongMetadata *songmet = GetMetadata((BandSongMgr *)PORT_THESONGMGR, firstArg->value.intVal); + RB3E_DEBUG("rb3e_get_genre %i", firstArg->value.intVal); + if (songmet == NULL) { + RB3E_MSG("!! FAILED TO GET SONG METADATA FOR %i !!", firstArg->value.intVal); + } else { + node->value.string = songmet->genre.sym; + } + } + return node; +} + +DataNode *DTAGetOrigin(DataNode *node, DataArray *args) +{ + DataNode *firstArg = DataNodeEvaluate(&args->mNodes->n[1]); + Symbol rb3e_no_origin; + SymbolConstruct(&rb3e_no_origin, "rb3e_no_origin"); + node->type = SYMBOL; + node->value.string = rb3e_no_origin.sym; + if (firstArg->type != INT_VALUE) + RB3E_MSG("Invalid types for arguments to rb3e_no_origin! 1st: %i", firstArg->type); + else + { + SongMetadata *songmet = GetMetadata((BandSongMgr *)PORT_THESONGMGR, firstArg->value.intVal); + RB3E_DEBUG("rb3e_no_origin %i", firstArg->value.intVal); + if (songmet == NULL) { + RB3E_MSG("!! FAILED TO GET SONG METADATA FOR %i !!", firstArg->value.intVal); + } else { + node->value.string = songmet->gameOrigin.sym; + } + } + return node; +} + +DataNode *DTADeleteSongCache(DataNode *node, DataArray *args) +{ + node->type = INT_VALUE; + node->value.intVal = RB3E_DeleteSongCache(); + return node; +} + +DataNode *DTALocalIP(DataNode *node, DataArray *args) +{ + Symbol noIpSym; + Symbol ipSym; + unsigned int localIP = RB3E_GetInternalIP(); + if (localIP == 0) + { + SymbolConstruct(&noIpSym, "(not connected)"); + node->type = SYMBOL; + node->value.string = noIpSym.sym; + return node; + } + else + { + char ipBuffer[24]; + unsigned char *ipParts = (unsigned char *)&localIP; + sprintf(ipBuffer, "%i.%i.%i.%i", ipParts[0], ipParts[1], ipParts[2], ipParts[3]); + SymbolConstruct(&ipSym, ipBuffer); + node->type = SYMBOL; + node->value.string = ipSym.sym; + return node; + } +} + +#ifdef RB3E_XBOX +// this function is inlined on the Xbox version, so we re-create it +void DataRegisterFunc(Symbol name, DTAFunction_t func) +{ + *(DTAFunction_t *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &name) = func; +} +#endif + void AddDTAFunctions() { - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.print_debug) = (int)PrintToDebugger; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_change_music_speed) = (int)ChangeMusicSpeed; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_change_track_speed) = (int)ChangeTrackSpeed; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_get_music_speed) = (int)GetMusicSpeed; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_get_track_speed) = (int)GetTrackSpeed; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_set_venue) = (int)DTASetVenue; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_is_emulator) = (int)DTAIsEmulator; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_relaunch_game) = (int)DTARelaunchGame; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_get_song_count) = (int)DTAGetSongCount; - *(int *)HmxFactoryFuncAt((int *)PORT_GDATAFUNCS, &globalSymbols.rb3e_send_event_string) = (int)DTASendModData; + DataRegisterFunc(globalSymbols.print_debug, DTAPrintToDebugger); + DataRegisterFunc(globalSymbols.rb3e_api_version, DTAGetAPIVersion); + DataRegisterFunc(globalSymbols.rb3e_build_tag, DTAGetRB3EBuildTag); + DataRegisterFunc(globalSymbols.rb3e_commit, DTAGetRB3ECommit); + DataRegisterFunc(globalSymbols.rb3e_change_music_speed, DTAChangeMusicSpeed); + DataRegisterFunc(globalSymbols.rb3e_change_track_speed, DTAChangeTrackSpeed); + DataRegisterFunc(globalSymbols.rb3e_get_music_speed, DTAGetMusicSpeed); + DataRegisterFunc(globalSymbols.rb3e_get_track_speed, DTAGetTrackSpeed); + DataRegisterFunc(globalSymbols.rb3e_set_venue, DTASetVenue); + DataRegisterFunc(globalSymbols.rb3e_is_emulator, DTAIsEmulator); + DataRegisterFunc(globalSymbols.rb3e_relaunch_game, DTARelaunchGame); + DataRegisterFunc(globalSymbols.rb3e_get_song_count, DTAGetSongCount); + DataRegisterFunc(globalSymbols.rb3e_send_event_string, DTASendModData); + DataRegisterFunc(globalSymbols.rb3e_get_song_name, DTAGetSongName); + DataRegisterFunc(globalSymbols.rb3e_get_artist, DTAGetArtist); + DataRegisterFunc(globalSymbols.rb3e_get_album, DTAGetAlbum); + DataRegisterFunc(globalSymbols.rb3e_get_origin, DTAGetOrigin); + DataRegisterFunc(globalSymbols.rb3e_get_genre, DTAGetGenre); + DataRegisterFunc(globalSymbols.rb3e_delete_songcache, DTADeleteSongCache); + DataRegisterFunc(globalSymbols.rb3e_local_ip, DTALocalIP); RB3E_MSG("Added DTA functions!", NULL); -} \ No newline at end of file +} diff --git a/source/DataDebug.c b/source/DataDebug.c new file mode 100644 index 0000000..aa74d4c --- /dev/null +++ b/source/DataDebug.c @@ -0,0 +1,200 @@ +/* + RB3Enhanced - DataDebug.h + Hooks for debugging scripting errors, similar to debug. +*/ + +#include +#include +#include +#include +#include "rb3/Data.h" +#include "ports.h" + +DataNode *DataSetHook(DataNode *ret, DataArray *array) +{ + if (array != NULL) + { + if (array->mNodes->n[1].type != VAR && array->mNodes->n[1].type != OBJECT_PROP_REF) + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Invalid set called of type %i in %s (line %d)", array->mNodes->n[1].type, array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Invalid set called of type %i", array->mNodes->n[1].type); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + } + return DataSet(ret, array); +} + +DataNode *DataSetElemHook(DataNode *ret, DataArray *array) +{ + if (array != NULL) + { + DataArray *setArray = array->mNodes->n[1].value.dataArray; + DataNode *evaluated = DataNodeEvaluate(&array->mNodes->n[1]); + if (evaluated->type == ARRAY) + { + setArray = evaluated->value.dataArray; + } + else + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Tried to set_elem with non-array in %s (line %d)", array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Tried to set_elem with non-array", NULL); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + if (setArray != NULL) + { + DataNode *idxNode = DataNodeEvaluate(&array->mNodes->n[2]); + if (idxNode != NULL) + { + int idx = idxNode->value.intVal; + if (idx < 0 || idx > setArray->mNodeCount) + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Tried to set_elem with an invalid index %d in %s (line %d)", idx, array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Tried to set_elem with an invalid index", NULL); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + } + else + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Tried to set_elem with a null index in %s (line %d)", array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Tried to set_elem with a null index", NULL); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + } + else + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Tried to set_elem with a null array in %s (line %d)", array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Tried to set_elem with a null array", NULL); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + } + return DataSetElem(ret, array); +} + +DataNode *DataOnElemHook(DataNode *ret, DataArray *array) +{ + if (array != NULL) + { + DataArray *elemArray = array->mNodes->n[1].value.dataArray; + DataNode *evaluated = DataNodeEvaluate(&array->mNodes->n[1]); + if (evaluated->type == ARRAY) + { + elemArray = evaluated->value.dataArray; + } + else + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Tried to elem with non-array in %s (line %d)", array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Tried to elem with non-array", NULL); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + if (elemArray != NULL) + { + DataNode *idxNode = DataNodeEvaluate(&array->mNodes->n[2]); + if (idxNode != NULL) + { + int idx = idxNode->value.intVal; + if (idx < 0 || idx >= elemArray->mNodeCount) + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Tried to elem with an invalid index %d in %s (line %d)", idx, array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Tried to elem with an invalid index %d", idx); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + } + else + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Tried to elem with a null index in %s (line %d)", array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Tried to elem with a null index", NULL); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + } + else + { + if (array->mFile.sym != NULL) + { + RB3E_DEBUG("Tried to elem with a null array in %s (line %d)", array->mFile.sym, array->mLine); + } + else + { + RB3E_DEBUG("Tried to elem with a null array", NULL); + } + ret->type = INT_VALUE; + ret->value.intVal = 0; + return ret; + } + } + return DataOnElem(ret, array); +} + +void *DataNodeGetObjHook(DataNode *node) +{ + DataNode *eval = DataNodeEvaluate(node); + if (eval->type != OBJECT && eval->type != STRING_VALUE && eval->type != SYMBOL) + { + RB3E_DEBUG("Tried to get object of non-object node type %d", node->type); + return NULL; + } + return DataNodeGetObj(node); +} diff --git a/source/FileSD.c b/source/FileSD.c new file mode 100644 index 0000000..596d34d --- /dev/null +++ b/source/FileSD.c @@ -0,0 +1,193 @@ +#ifdef RB3E_WII + +#include +#include +#include +#include +#include +#include +#include "FileSD.h" +#include "rb3/File.h" +#include "rb3/Mem.h" +#include "rb3enhanced.h" +#include "ports.h" + +#define LE16(i) ((((i) & 0xFF) << 8 | ((i) >> 8) & 0xFF) & 0xFFFF) +#define LE(i) (((i) & 0xff) << 24 | ((i) & 0xff00) << 8 | ((i) & 0xff0000) >> 8 | ((i) >> 24) & 0xff) + +File_vtable FileSD_vtable; + +FileSD *FileSD_Destroy(FileSD *file) +{ + RB3E_DEBUG("FileSD_Destroy(file=%p)", file); + if (file->mFd != -1) + SD_close(file->mFd); + if (file->mFilePath != NULL) + MemFree(file->mFilePath); + MemFree(file); + return NULL; +} + +FileSD *FileSD_New(const char *filepath, int flags) +{ + RB3E_DEBUG("FileSD_New(filepath='%s', flags=0x%x)", filepath, flags); + // create the new object + FileSD *sd = MemAlloc(sizeof(FileSD), 0); + if (sd == NULL) { + RB3E_DEBUG("Not enough memory to read file '%s' from SD!", filepath); + return NULL; + } + memset(sd, 0, sizeof(FileSD)); + FileSD_InitVtable(); // TODO(Emma): find a better place to put this + sd->vtable = &FileSD_vtable; + sd->mFd = -1; + + // set the file path + sd->mFilePath = MemAlloc(strlen(filepath) + 1, 0); + if (sd->mFilePath == NULL) { + RB3E_DEBUG("Not enough memory to read file '%s' from SD!", filepath); + FileSD_Destroy(sd); + return NULL; + } + strcpy(sd->mFilePath, filepath); + + // determine the flags to use + int fileMode = O_RDONLY; + if ((flags & 0x4)) { + fileMode |= (O_RDWR | O_CREAT); + sd->mWriteEnabled = true; + } + if ((flags & 0x100)) { + fileMode |= O_APPEND; + } + RB3E_DEBUG("fileMode = 0x%x", fileMode); + + // open the file + sd->mFd = SD_open(&sd->mFileStruct, filepath, fileMode); + if (sd->mFd == -1) { + RB3E_DEBUG("Failed to open file '%s'", filepath); + FileSD_Destroy(sd); + return NULL; + } + + sd->mFilesize = sd->mFileStruct.filesize; + + return sd; +} + +String *FileSD_Filename(String *str, FileSD *file) +{ + // really we should be creating a new SD card + RB3E_DEBUG("FileSD_Filename(str=%p, file=%p)", str, file); + str->length = strlen(file->mFilePath); + str->buf = file->mFilePath; + return str; +} + +int FileSD_Read(FileSD *file, void *iData, int iBytes) { + //RB3E_DEBUG("FileSD_Read(file=%p, iData=%p, iBytes=%i)", file, iData, iBytes); + int read = SD_read(file->mFd, iData, iBytes); + if (read >= 0) { + file->mCurrentPosition += read; + } + return read; +} + +char FileSD_ReadAsync(FileSD *file, void *iData, int iBytes) { + // TODO(Emma): async SD card reads + //RB3E_DEBUG("FileSD_ReadAsync(file=%p, iData=%p, iBytes=%i)", file, iData, iBytes); + int read = FileSD_Read(file, iData, iBytes); + file->mLastBytesRead = read; + return (read >= 0); +} + +int FileSD_Write(FileSD *file, void *iData, int iBytes) { + // RB3E_DEBUG("FileSD_Write(file=%p, iData=%p, iBytes=%i)", file, iData, iBytes); + int wrote = SD_write(file->mFd, iData, iBytes); + if (wrote >= 0) { + file->mCurrentPosition += wrote; + } + return wrote; +} + +char FileSD_WriteAsync(FileSD *file, void *iData, int iBytes) { + // TODO(Emma): async SD card writes + // RB3E_DEBUG("FileSD_WriteAsync(file=%p, iData=%p, iBytes=%i)", file, iData, iBytes); + int wrote = FileSD_Write(file, iData, iBytes); + file->mLastBytesWrote = wrote; + return (wrote >= 0); +} + +int FileSD_Seek(FileSD *file, int iOffset, int iSeekType) { + RB3E_DEBUG("FileSD_Seek(file=%p, iOffset=%i, iSeekType=%i)", file, iOffset, iSeekType); + switch (iSeekType) { + case SEEK_SET: + file->mCurrentPosition = iOffset; + break; + case SEEK_CUR: + file->mCurrentPosition += iOffset; + break; + case SEEK_END: + file->mCurrentPosition = file->mFilesize - iOffset; + break; + } + file->mCurrentPosition = SD_seek(file->mFd, file->mCurrentPosition, SEEK_SET); + return file->mCurrentPosition; +} + +int FileSD_Tell(FileSD *file) { + RB3E_DEBUG("FileSD_Tell(file=%p)", file); + return file->mCurrentPosition; +} + +void FileSD_Flush(FileSD *file) { + RB3E_DEBUG("FileSD_Flush(file=%p)", file); + return; +} + +char FileSD_Eof(FileSD *file) { + //RB3E_DEBUG("FileSD_Eof(file=%p)", file); + return (file->mCurrentPosition >= file->mFilesize); +} + +char FileSD_Fail(FileSD *file) { + //RB3E_DEBUG("FileSD_Fail(file=%p)", file); + return file->mFailed; +} + +int FileSD_Size(FileSD *file) { + RB3E_DEBUG("FileSD_Size(file=%p)", file); + return file->mFilesize; +} + +char FileSD_ReadDone(FileSD *file, int *oBytes) { + //RB3E_DEBUG("FileSD_ReadDone(file=%p)", file); + *oBytes = file->mLastBytesRead; + return 1; +} + +char FileSD_WriteDone(FileSD *file, int *oBytes) { + //RB3E_DEBUG("FileSD_WriteDone(file=%p)", file); + *oBytes = file->mLastBytesWrote; + return 0; +} + +void FileSD_InitVtable() { + FileSD_vtable.destructor = (FileDestructor_t)FileSD_Destroy; + FileSD_vtable.Filename = (FileFilename_t)FileSD_Filename; + FileSD_vtable.Read = (FileRead_t)FileSD_Read; + FileSD_vtable.ReadAsync = (FileReadAsync_t)FileSD_ReadAsync; + FileSD_vtable.Write = (FileWrite_t)FileSD_Write; + FileSD_vtable.WriteAsync = (FileWriteAsync_t)FileSD_WriteAsync; + FileSD_vtable.Seek = (FileSeek_t)FileSD_Seek; + FileSD_vtable.Tell = (FileTell_t)FileSD_Tell; + FileSD_vtable.Flush = (FileFlush_t)FileSD_Flush; + FileSD_vtable.Eof = (FileEof_t)FileSD_Eof; + FileSD_vtable.Fail = (FileFail_t)FileSD_Fail; + FileSD_vtable.Size = (FileSize_t)FileSD_Size; + FileSD_vtable.UncompressedSize = (FileUncompressedSize_t)FileSD_Size; + FileSD_vtable.ReadDone = (FileReadDone_t)FileSD_ReadDone; + FileSD_vtable.WriteDone = (FileWriteDone_t)FileSD_WriteDone; +} + +#endif // RB3E_WII diff --git a/source/FileWiiNAND.c b/source/FileWiiNAND.c new file mode 100644 index 0000000..dc4f39d --- /dev/null +++ b/source/FileWiiNAND.c @@ -0,0 +1,190 @@ +#ifdef RB3E_WII + +#include +#include +#include +#include +#include +#include +#include "FileWiiNAND.h" +#include "rb3/File.h" +#include "rb3/Mem.h" +#include "rb3enhanced.h" +#include "ports.h" + +#define LE16(i) ((((i) & 0xFF) << 8 | ((i) >> 8) & 0xFF) & 0xFFFF) +#define LE(i) (((i) & 0xff) << 24 | ((i) & 0xff00) << 8 | ((i) & 0xff0000) >> 8 | ((i) >> 24) & 0xff) + +File_vtable FileWiiNAND_vtable; + +FileWiiNAND *FileWiiNAND_Destroy(FileWiiNAND *file) +{ + RB3E_DEBUG("FileWiiNAND_Destroy(file=%p)", file); + if (file->mFd != -1) + IOS_Close(file->mFd); + if (file->mFilePath != NULL) + MemFree(file->mFilePath); + MemFree(file); + return NULL; +} + +typedef struct _nand_file_stats_t { + uint32_t length; + uint32_t pos; +} nand_file_stats_t; + +FileWiiNAND *FileWiiNAND_New(const char *filepath) +{ + // never ever allow this on real hardware + if (!RB3E_IsEmulator()) + return NULL; + + RB3E_DEBUG("FileWiiNAND_New(filepath='%s')", filepath); + // create the new object + FileWiiNAND *nd = MemAlloc(sizeof(FileWiiNAND), 0); + if (nd == NULL) { + RB3E_DEBUG("Not enough memory to read file '%s' from NAND!", filepath); + return NULL; + } + memset(nd, 0, sizeof(FileWiiNAND)); + FileWiiNAND_InitVtable(); // TODO(Emma): find a better place to put this + nd->vtable = &FileWiiNAND_vtable; + nd->mFd = -1; + + // set the file path + nd->mFilePath = MemAlloc(strlen(filepath) + 1, 0); + if (nd->mFilePath == NULL) { + RB3E_DEBUG("Not enough memory to read file '%s' from NAND!", filepath); + FileWiiNAND_Destroy(nd); + return NULL; + } + strcpy(nd->mFilePath, filepath); + + // open the file + nd->mFd = IOS_Open(filepath, IPC_OPEN_READ); + if (nd->mFd == -1) { + RB3E_DEBUG("Failed to open file '%s'", filepath); + FileWiiNAND_Destroy(nd); + return NULL; + } + + // get the filesize + nand_file_stats_t stats __attribute__((aligned(32))); + ios_ret_t r = IOS_Ioctl(nd->mFd, 11, NULL, 0, &stats, sizeof(nand_file_stats_t)); + if (r != IPC_OK) { + RB3E_DEBUG("IOS returned %i trying to get stats", r); + FileWiiNAND_Destroy(nd); + return NULL; + } + + nd->mFilesize = stats.length; + + return nd; +} + +String *FileWiiNAND_Filename(String *str, FileWiiNAND *file) +{ + RB3E_DEBUG("FileWiiNAND_Filename(str=%p, file=%p)", str, file); + str->length = strlen(file->mFilePath); + str->buf = file->mFilePath; + return str; +} + +int FileWiiNAND_Read(FileWiiNAND *file, void *iData, int iBytes) { + //RB3E_DEBUG("FileWiiNAND_Read(file=%p, iData=%p, iBytes=%i)", file, iData, iBytes); + int read = IOS_Read(file->mFd, iData, iBytes); + if (read >= 0) { + file->mCurrentPosition += read; + } + return read; +} + +char FileWiiNAND_ReadAsync(FileWiiNAND *file, void *iData, int iBytes) { + // TODO(Emma): async SD card reads + //RB3E_DEBUG("FileWiiNAND_ReadAsync(file=%p, iData=%p, iBytes=%i)", file, iData, iBytes); + int read = FileWiiNAND_Read(file, iData, iBytes); + file->mLastBytesRead = read; + return (read >= 0); +} + +int FileWiiNAND_Write(FileWiiNAND *file, void *iData, int iBytes) { + return 0; +} + +char FileWiiNAND_WriteAsync(FileWiiNAND *file, void *iData, int iBytes) { + return 0; +} + +int FileWiiNAND_Seek(FileWiiNAND *file, int iOffset, int iSeekType) { + RB3E_DEBUG("FileWiiNAND_Seek(file=%p, iOffset=%i, iSeekType=%i)", file, iOffset, iSeekType); + switch (iSeekType) { + case SEEK_SET: + file->mCurrentPosition = iOffset; + break; + case SEEK_CUR: + file->mCurrentPosition += iOffset; + break; + case SEEK_END: + file->mCurrentPosition = file->mFilesize - iOffset; + break; + } + IOS_Seek(file->mFd, file->mCurrentPosition, SEEK_SET); + return file->mCurrentPosition; +} + +int FileWiiNAND_Tell(FileWiiNAND *file) { + RB3E_DEBUG("FileWiiNAND_Tell(file=%p)", file); + return file->mCurrentPosition; +} + +void FileWiiNAND_Flush(FileWiiNAND *file) { + RB3E_DEBUG("FileWiiNAND_Flush(file=%p)", file); + return; +} + +char FileWiiNAND_Eof(FileWiiNAND *file) { + //RB3E_DEBUG("FileWiiNAND_Eof(file=%p)", file); + return (file->mCurrentPosition >= file->mFilesize); +} + +char FileWiiNAND_Fail(FileWiiNAND *file) { + //RB3E_DEBUG("FileWiiNAND_Fail(file=%p)", file); + return file->mFailed; +} + +int FileWiiNAND_Size(FileWiiNAND *file) { + RB3E_DEBUG("FileWiiNAND_Size(file=%p)", file); + return file->mFilesize; +} + +char FileWiiNAND_ReadDone(FileWiiNAND *file, int *oBytes) { + //RB3E_DEBUG("FileWiiNAND_ReadDone(file=%p)", file); + *oBytes = file->mLastBytesRead; + return 1; +} + +char FileWiiNAND_WriteDone(FileWiiNAND *file, int *oBytes) { + //RB3E_DEBUG("FileWiiNAND_WriteDone(file=%p)", file); + *oBytes = file->mLastBytesWrote; + return 0; +} + +void FileWiiNAND_InitVtable() { + FileWiiNAND_vtable.destructor = (FileDestructor_t)FileWiiNAND_Destroy; + FileWiiNAND_vtable.Filename = (FileFilename_t)FileWiiNAND_Filename; + FileWiiNAND_vtable.Read = (FileRead_t)FileWiiNAND_Read; + FileWiiNAND_vtable.ReadAsync = (FileReadAsync_t)FileWiiNAND_ReadAsync; + FileWiiNAND_vtable.Write = (FileWrite_t)FileWiiNAND_Write; + FileWiiNAND_vtable.WriteAsync = (FileWriteAsync_t)FileWiiNAND_WriteAsync; + FileWiiNAND_vtable.Seek = (FileSeek_t)FileWiiNAND_Seek; + FileWiiNAND_vtable.Tell = (FileTell_t)FileWiiNAND_Tell; + FileWiiNAND_vtable.Flush = (FileFlush_t)FileWiiNAND_Flush; + FileWiiNAND_vtable.Eof = (FileEof_t)FileWiiNAND_Eof; + FileWiiNAND_vtable.Fail = (FileFail_t)FileWiiNAND_Fail; + FileWiiNAND_vtable.Size = (FileSize_t)FileWiiNAND_Size; + FileWiiNAND_vtable.UncompressedSize = (FileUncompressedSize_t)FileWiiNAND_Size; + FileWiiNAND_vtable.ReadDone = (FileReadDone_t)FileWiiNAND_ReadDone; + FileWiiNAND_vtable.WriteDone = (FileWriteDone_t)FileWiiNAND_WriteDone; +} + +#endif // RB3E_WII diff --git a/source/GameHooks.c b/source/GameHooks.c index b9d9057..798e94f 100644 --- a/source/GameHooks.c +++ b/source/GameHooks.c @@ -26,9 +26,9 @@ void *GameConstructHook(void *theGame) // You just lost RB3E_EventBandInfo bandevent = {0}; #ifdef RB3E_XBOX Symbol song; - GetSongSymbol(&song, *(int *)PORT_THEMETAPERFORMER); + GetSongShortname(&song, *(int *)PORT_THEMETAPERFORMER); #else - Symbol song = GetSongSymbol(*(int *)PORT_THEMETAPERFORMER); + Symbol song = GetSongShortname(*(int *)PORT_THEMETAPERFORMER); #endif if (song.sym != NULL) { @@ -39,7 +39,7 @@ void *GameConstructHook(void *theGame) // You just lost if (metadata != NULL) { RB3E_DEBUG("Metadata: %p", metadata); - RB3E_DEBUG("Started song: '%s' - %s (ID: %i, %s)", metadata->title.buf, metadata->artist.buf, metadata->mSongID, metadata->mShortName); + RB3E_DEBUG("Started song: '%s' - %s (ID: %i, %s)", metadata->title.buf, metadata->artist.buf, metadata->song_id, metadata->shortname); RB3E_SendEvent(RB3E_EVENT_SONG_NAME, metadata->title.buf, metadata->title.length); RB3E_SendEvent(RB3E_EVENT_SONG_ARTIST, metadata->artist.buf, metadata->artist.length); } @@ -49,10 +49,10 @@ void *GameConstructHook(void *theGame) // You just lost bandUser = GetBandUserFromSlot(*(int *)PORT_THEBANDUSERMGR, i); if (bandUser != NULL) { - RB3E_DEBUG("BandUser %i: %p - Track: %i, Controller: %i, Difficulty: %i", i, bandUser, bandUser->mTrackType, bandUser->mControllerType, bandUser->mDifficulty); + RB3E_DEBUG("BandUser %i: %p - Track: %i, Controller: %i, Difficulty: %i", i, bandUser, bandUser->trackType, bandUser->controllerType, bandUser->difficulty); bandevent.MemberExists[i] = 1; - bandevent.Difficulty[i] = bandUser->mDifficulty; - bandevent.TrackType[i] = bandUser->mTrackType; + bandevent.Difficulty[i] = bandUser->difficulty; + bandevent.TrackType[i] = bandUser->trackType; } else { @@ -68,5 +68,12 @@ void *GameDestructHook(void *theGame, int r4) { char in_game = 0x00; RB3E_SendEvent(RB3E_EVENT_STATE, &in_game, sizeof(in_game)); + +#ifdef RB3E_XBOX + // When not in-game, turn off the stage-kit lights. + // Fixes the RB3 bug(?) that leaves 2 red leds on when exiting the score view screen. + StagekitSetStateHook(0x00, 0xFF); +#endif + return GameDestruct(theGame, r4); } diff --git a/source/GemHooks.c b/source/GemHooks.c index 0e0370a..f8f08ba 100644 --- a/source/GemHooks.c +++ b/source/GemHooks.c @@ -1,17 +1,15 @@ #include "GlobalSymbols.h" #include "ports.h" -#include "rb3/Mem.h" #include "rb3/ModifierManager.h" #include "rb3/GameGem.h" #include "rb3/Random.h" #include "rb3/Symbol.h" -#include "rb3/Vector.h" int WillBeNoStrumHook(GameGemList *thisGameGemList, int *gem) { Modifier *forceHoposModifier; - forceHoposModifier = ModifierIsActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.forceHopos, 0); + forceHoposModifier = ModifierActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.forceHopos, 0); if (forceHoposModifier->enabled) { return 1; @@ -31,7 +29,7 @@ int *GetWidgetByNameHook(int *gemManager, Symbol sym) Symbol *drumCymbalColors[4] = {&globalSymbols.redCymbalGem, &globalSymbols.yellowCymbalGem, &globalSymbols.blueCymbalGem, &globalSymbols.greenCymbalGem}; int i = 0; - colorShuffleModifier = ModifierIsActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.colorShuffle, 0); + colorShuffleModifier = ModifierActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.colorShuffle, 0); if (colorShuffleModifier->enabled) { @@ -71,7 +69,7 @@ Symbol GetSlotColorHook(int *bandUser, int slot) Symbol slotColor = GetSlotColor(bandUser, slot); - colorShuffleModifier = ModifierIsActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.colorShuffle, 0); + colorShuffleModifier = ModifierActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.colorShuffle, 0); if (colorShuffleModifier->enabled) { @@ -131,8 +129,8 @@ int AddGameGemHook(GameGemList *gameGemList, GameGem *gem, NoStrumState gemType) char origBlue = gem->blue; char origOrange = gem->orange; - mirrorModeModifier = ModifierIsActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.mirrorMode, 0); - gemShuffleModifier = ModifierIsActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.gemShuffle, 0); + mirrorModeModifier = ModifierActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.mirrorMode, 0); + gemShuffleModifier = ModifierActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.gemShuffle, 0); if (gemShuffleModifier->enabled) { diff --git a/source/GlobalSymbols.c b/source/GlobalSymbols.c index ee01767..af6f8fb 100644 --- a/source/GlobalSymbols.c +++ b/source/GlobalSymbols.c @@ -9,6 +9,7 @@ #include "rb3/Symbol.h" #include "ports.h" #include "GlobalSymbols.h" +#include "version.h" GlobalSymbols globalSymbols; static char globalSymbolsInitialised = 0; @@ -26,7 +27,13 @@ void InitGlobalSymbols() memset(&globalSymbols, 0, sizeof(globalSymbols)); + SymbolConstruct(&globalSymbols.buildtag, RB3E_BUILDTAG); + SymbolConstruct(&globalSymbols.commit, RB3E_BUILDCOMMIT); + SymbolConstruct(&globalSymbols.print_debug, "print_debug"); + SymbolConstruct(&globalSymbols.rb3e_api_version, "rb3e_api_version"); + SymbolConstruct(&globalSymbols.rb3e_build_tag, "rb3e_build_tag"); + SymbolConstruct(&globalSymbols.rb3e_commit, "rb3e_commit"); SymbolConstruct(&globalSymbols.rb3e_change_music_speed, "rb3e_change_music_speed"); SymbolConstruct(&globalSymbols.rb3e_change_track_speed, "rb3e_change_track_speed"); SymbolConstruct(&globalSymbols.rb3e_get_music_speed, "rb3e_get_music_speed"); @@ -36,12 +43,20 @@ void InitGlobalSymbols() SymbolConstruct(&globalSymbols.rb3e_relaunch_game, "rb3e_relaunch_game"); SymbolConstruct(&globalSymbols.rb3e_get_song_count, "rb3e_get_song_count"); SymbolConstruct(&globalSymbols.rb3e_send_event_string, "rb3e_send_event_string"); + SymbolConstruct(&globalSymbols.rb3e_get_song_name, "rb3e_get_song_name"); + SymbolConstruct(&globalSymbols.rb3e_get_artist, "rb3e_get_artist"); + SymbolConstruct(&globalSymbols.rb3e_get_album, "rb3e_get_album"); + SymbolConstruct(&globalSymbols.rb3e_get_origin, "rb3e_get_origin"); + SymbolConstruct(&globalSymbols.rb3e_get_genre, "rb3e_get_genre"); + SymbolConstruct(&globalSymbols.rb3e_delete_songcache, "rb3e_delete_songcache"); + SymbolConstruct(&globalSymbols.rb3e_local_ip, "rb3e_local_ip"); SymbolConstruct(&globalSymbols.blackBackground, "mod_black_background"); SymbolConstruct(&globalSymbols.colorShuffle, "mod_color_shuffle"); SymbolConstruct(&globalSymbols.forceHopos, "mod_force_hopos"); SymbolConstruct(&globalSymbols.mirrorMode, "mod_mirror_mode"); SymbolConstruct(&globalSymbols.gemShuffle, "mod_gem_shuffle"); + SymbolConstruct(&globalSymbols.doubleBass, "mod_double_bass"); SymbolConstruct(&globalSymbols.greenGem, "gem_green.wid"); SymbolConstruct(&globalSymbols.redGem, "gem_red.wid"); diff --git a/source/LocaleHooks.c b/source/LocaleHooks.c index c065d4b..e997ccf 100644 --- a/source/LocaleHooks.c +++ b/source/LocaleHooks.c @@ -39,10 +39,10 @@ static int numOverrideLocales = sizeof(overrideLocales) / sizeof(overrideLocales static char *newLocales[][2] = { {"mod_black_background", "Black Background"}, {"mod_force_hopos", "Force HOPOs"}, - {"mod_mirror_mode", "Mirror Mode (Guitar Only)"}, + {"mod_mirror_mode", "Mirror Mode"}, {"mod_color_shuffle", "Gem Color Shuffle"}, - {"mod_gem_shuffle", "Gem Shuffle"}, -}; + {"mod_gem_shuffle", "Note Shuffle"}, + {"mod_double_bass", "Double Bass Pedal"}}; static int numNewLocales = sizeof(newLocales) / sizeof(newLocales[0]); char RB3E_ActiveLocale[RB3E_LANG_LEN + 1] = {0}; @@ -67,17 +67,20 @@ int IsSupportedLanguageHook(Symbol lang, int r4) // this has to be accessible at all times, so can't put it on the stack static char mod_locale_string[0x80]; -char *LocalizeHook(int thisLocale, Symbol token, int fail) +char *LocalizeHook(int thisLocale, Symbol sym, int fail) { int i = 0; char newLocaleName[0x50]; Symbol newLocale; char *original; + + // No longer needed with origin rewrite // game origin icons relies on using different formatting for the song/artist name - if (config.GameOriginIcons == 1 && token.sym != NULL && strcmp(token.sym, "song_artist_fmt") == 0) - return "%s %s"; + // if (config.GameOriginIcons == 1 && sym.sym != NULL && strcmp(sym.sym, "song_artist_fmt") == 0) + // return "%s %s"; + // if the string starts with message_motd_ (but isn't message_motd), it's our motd - if (memcmp(token.sym, "message_motd_", 13) == 0) + if (memcmp(sym.sym, "message_motd_", 13) == 0) { // check for "rb3e_mod_string" SymbolConstruct(&newLocale, "rb3e_mod_string"); @@ -92,19 +95,19 @@ char *LocalizeHook(int thisLocale, Symbol token, int fail) } #ifdef RB3E_XBOX // replace the "invite friends" label with "Manage Liveless" - if (config.LivelessAddress[0] != 0 && strcmp(token.sym, "overshell_invite") == 0) + if (config.LivelessAddress[0] != 0 && strcmp(sym.sym, "overshell_invite") == 0) return "Manage Liveless"; #endif // if our string is in the override list, use that string entirely for (i = 0; i < numOverrideLocales; i++) { - if (strcmp(token.sym, overrideLocales[i][0]) == 0) + if (strcmp(sym.sym, overrideLocales[i][0]) == 0) { // length sanity check before sprintf - if (strlen(token.sym) > 0x40) - return Localize(thisLocale, token, fail); + if (strlen(sym.sym) > 0x40) + return Localize(thisLocale, sym, fail); // check if rb3e_localename exists - sprintf(newLocaleName, "rb3e_%s", token.sym); + sprintf(newLocaleName, "rb3e_%s", sym.sym); SymbolConstruct(&newLocale, newLocaleName); original = Localize(thisLocale, newLocale, fail); if (original == NULL) @@ -113,12 +116,12 @@ char *LocalizeHook(int thisLocale, Symbol token, int fail) } } // try to localize through to the original string - original = Localize(thisLocale, token, fail); + original = Localize(thisLocale, sym, fail); if (original == NULL) { // check to see if it's one of our new localisations for (i = 0; i < numNewLocales; i++) - if (strcmp(token.sym, newLocales[i][0]) == 0) + if (strcmp(sym.sym, newLocales[i][0]) == 0) return newLocales[i][1]; } return original; diff --git a/source/MiloSceneHooks.c b/source/MiloSceneHooks.c index 593d090..518f686 100644 --- a/source/MiloSceneHooks.c +++ b/source/MiloSceneHooks.c @@ -34,16 +34,17 @@ void LoadObj(Object *object, BinStream *stream) if (strlen(origPath) + strlen(object->name) - 20 > sizeof(newPath)) goto object_pre_load; strncpy(newPath, origPath, strlen(origPath) - 10); - strncat(newPath, "/", 1); - strncat(newPath, object->name, strlen(object->name)); + // HACK(Emma): we don't have strncat on Wii so just strcat. THIS IS NOT SAFE! + strcat(newPath, "/"); //, 1); + strcat(newPath, object->name); //, strlen(object->name)); replacementPath = RB3E_GetRawfilePath(newPath, 0); if (replacementPath != NULL) { RB3E_DEBUG("Replacing Milo scene asset %s with file at %s", object->name, replacementPath); - // create FileStream with kReadNoArk so it looks outside of the ARK for the file - fileStream = *FileStreamConstructor(&fileStream, replacementPath, kReadNoArk, 0); + // create FileStream with filetype 2 so it looks outside of the ARK for the file + fileStream = *FileStreamConstructor(&fileStream, replacementPath, 2, 0); // attempt to detect endianness of replacement asset // this allows assets from GH2 and earlier to work @@ -56,7 +57,7 @@ void LoadObj(Object *object, BinStream *stream) // not sure if this is 100% necessary, but cannot hurt fileStream.vtable->seekImpl(&fileStream, 0, 0); - object->table->preLoad(object, &fileStream); + object->table->preLoad(object, (BinStream *)&fileStream); // destroy the FileStream so we do not leak memory fileStream.vtable->destructor(&fileStream, 0); @@ -66,4 +67,28 @@ void LoadObj(Object *object, BinStream *stream) object_pre_load: object->table->preLoad(object, stream); return; +} + +// This hook allows for GH2-360/RB1/RB2 meshes to load correctly in RB3 +void VertexReadHook(BinStream *thisBinStream, Vector3 *vec3) +{ +#ifdef RB3E_XBOX + // the gRev of the current mesh + int gRev = *(int *)PORT_MESH_GREV; + char empty[4] = {0}; + + BinstreamReadEndian(thisBinStream, (void *)&vec3->x, 4); + BinstreamReadEndian(thisBinStream, (void *)&vec3->y, 4); + BinstreamReadEndian(thisBinStream, (void *)&vec3->z, 4); + + // if the current RndMesh being read is the GH2-360/RB1/RB2 format, we need to skip over the W component + // RB3 (for whatever reason) expects vertices in these versions of meshes to not include W + // even though they *do* in actual GH2-360/RB1/RB2 meshes + if (gRev == 34) + { + BinstreamReadEndian(thisBinStream, (void *)&empty, 4); + } + + return; +#endif } \ No newline at end of file diff --git a/source/MusicLibrary.c b/source/MusicLibrary.c index 4c9fa39..d595943 100644 --- a/source/MusicLibrary.c +++ b/source/MusicLibrary.c @@ -7,12 +7,16 @@ void CheckForPanelAndJump(Symbol entryName, int sortType) { +#ifndef RB3E_WII_BANK8 // check if the song select panel is "up" (displayed on screen) before attempting a jump - UIPanel *songSelectPanel = ObjectFindUIPanel(*(int *)PORT_OBJECTDIRMAINDIR, "song_select_panel", 1); - if (songSelectPanel != NULL && songSelectPanel->mState == kPanelUp) + UIPanel *songSelectPanel = ObjectFindUIPanel(*(int **)PORT_OBJECTDIRMAINDIR, "song_select_panel", 1); + if (songSelectPanel != NULL && songSelectPanel->is_up == 1) { MusicLibrarySelect(*(int *)PORT_THEMUSICLIBRARY, entryName, sortType, 1); } +#else + RB3E_MSG("CheckForPanelAndJump not implemented on Bank 8!", NULL); +#endif } // selects any entry based on the full artist name or the game origin diff --git a/source/OvershellHooks.c b/source/OvershellHooks.c index 8afdc79..e2363b4 100644 --- a/source/OvershellHooks.c +++ b/source/OvershellHooks.c @@ -61,4 +61,4 @@ void OvershellPartSelectProviderReload(OvershellSlot *thisOvershellSlot, Control OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_bass, kTrackBass, "B"); break; } -} +} \ No newline at end of file diff --git a/source/PassiveMessages.c b/source/PassiveMessages.c index 4da7a06..005de59 100644 --- a/source/PassiveMessages.c +++ b/source/PassiveMessages.c @@ -18,7 +18,7 @@ void DisplayMessage(char *message) messageNode.type = SYMBOL; messageNode.value.string = message; messageArray.mNodeCount = 1; - messageArray.mNodes = &messageNode; // set this right for what it actually is + messageArray.mNodes = (DataNodes *)&messageNode; // set this right for what it actually is messageArray.mRefCount = 1; messageArray.mFile = *(Symbol *)PORT_NULLSYMBOL; QueueMessage((*(BandUI *)PORT_THEBANDUI).passiveMessagesPanel, &messageArray, 0, (*(Symbol *)PORT_NULLSYMBOL).sym, 0xFFFFFFFF); diff --git a/source/QuazalHooks.c b/source/QuazalHooks.c index 306c67c..af70ade 100644 --- a/source/QuazalHooks.c +++ b/source/QuazalHooks.c @@ -15,12 +15,12 @@ void OperatorEqualsFmtHook(char *r3, char *r4) return; } -int StepSequenceJobSetStepHook(int *unk, Step *step) +int StepSequenceJobSetStepHook(int *unk, StepSequenceJobStep *step) { // steps can have no name it seems; make sure we are not trying to print a null pointer - if (step != NULL && step->m_szStepDescription != NULL) + if (step != NULL && step->jobName != NULL) { - RB3E_DEBUG("Quazal Job: %s", step->m_szStepDescription); + RB3E_DEBUG("Quazal Job: %s", step->jobName); } return StepSequenceJobSetStep(unk, step); } \ No newline at end of file diff --git a/source/SetlistHooks.c b/source/SetlistHooks.c index 4e03c56..f154833 100644 --- a/source/SetlistHooks.c +++ b/source/SetlistHooks.c @@ -5,6 +5,7 @@ #include #include +#include #include #include "config.h" #include "ports.h" @@ -12,6 +13,7 @@ #include "rb3/BandLabel.h" #include "rb3/File.h" #include "rb3/FilePath.h" +#include "rb3/Mem.h" #include "rb3/MusicLibrary.h" #include "rb3/NodeSort.h" #include "rb3/UI/UIListSlot.h" @@ -76,7 +78,7 @@ void CreateMaterial(GameOriginInfo *info) } -int *MusicLibraryConstructorHook(MusicLibrary *thisMusicLibrary, int *songPreview) +int *MusicLibraryConstructorHook(int *thisMusicLibrary, int *songPreview) { return MusicLibraryConstructor(thisMusicLibrary, songPreview); } @@ -87,7 +89,7 @@ RndMat *MusicLibraryMatHook(MusicLibrary *thisMusicLibrary, int data, int idx, U { if (strcmp(listSlot->mMatchName.buf, "game_origin_icon") == 0) { - int *ret = 0; + NodeSort *ret = 0; SortNode *node = 0; SongNodeType nodeType = kNodeNone; int curInfo = 0; @@ -106,12 +108,12 @@ RndMat *MusicLibraryMatHook(MusicLibrary *thisMusicLibrary, int data, int idx, U // do a basic null check here, sometimes it can be null if (node != NULL && node->record != NULL && node->record->metaData != NULL && - node->record->metaData->mGameOrigin.sym != NULL) + node->record->metaData->gameOrigin.sym != NULL) { // this shit fucking sucks lol for (curInfo = 0; curInfo < numGameOrigins; curInfo++) { - if (strcmp(node->record->metaData->mGameOrigin.sym, originInfo[curInfo].gameOrigin) == 0) + if (strcmp(node->record->metaData->gameOrigin.sym, originInfo[curInfo].gameOrigin) == 0) { if (textures[originInfo[curInfo].num] != NULL && textures[originInfo[curInfo].num]->mMat != NULL) { @@ -133,7 +135,7 @@ RndMat *MusicLibraryMatHook(MusicLibrary *thisMusicLibrary, int data, int idx, U } } - return MusicLibraryMat(thisMusicLibrary, data, idx, listSlot); + return (RndMat*)(intptr_t)MusicLibraryMat((void*)thisMusicLibrary, data, idx, (int*)listSlot); } // called when entering the music library @@ -236,9 +238,9 @@ SongMetadata *SongMetadataConstructorHook(SongMetadata *thisSongMetadata, DataAr thisSongMetadata = SongMetadataConstructor(thisSongMetadata, data, backupData, isOnDisc); // make sure the game origin isn't null - if (thisSongMetadata->mGameOrigin.sym != 0) + if (thisSongMetadata->gameOrigin.sym != 0) { - AddGameOriginToIconList(thisSongMetadata->mGameOrigin.sym); + AddGameOriginToIconList(thisSongMetadata->gameOrigin.sym); return thisSongMetadata; } @@ -251,9 +253,9 @@ char SongMetadataLoadHook(SongMetadata *thisSongMetadata, BinStream *stream) char ret = SongMetadataLoad(thisSongMetadata, stream); // make sure the game origin isn't null - if (thisSongMetadata->mGameOrigin.sym != 0) + if (thisSongMetadata->gameOrigin.sym != 0) { - AddGameOriginToIconList(thisSongMetadata->mGameOrigin.sym); + AddGameOriginToIconList(thisSongMetadata->gameOrigin.sym); return ret; } diff --git a/source/SongHooks.c b/source/SongHooks.c index 594c257..28e19ae 100644 --- a/source/SongHooks.c +++ b/source/SongHooks.c @@ -7,8 +7,6 @@ #include #include "ports.h" #include "crc32.h" -#include "GameOriginInfo.h" -#include "rb3/File.h" #include "rb3/PassiveMessagesPanel.h" #include "rb3/SongMetadata.h" #include "rb3/Data.h" @@ -53,21 +51,21 @@ int MetadataSongIDHook(DataNode *song_id) int GetSongIDHook(DataArray *song, DataArray *missing_data_maybe) { Symbol song_id; - DataNode *found; - DataArray *array; + DataNode *found = NULL; + DataArray *array = NULL; SymbolConstruct(&song_id, "song_id"); if (song == NULL) return 0; - array = DataFindArray(song, song_id); + // check missing song data first if we have it + if (missing_data_maybe != NULL) + array = DataFindArray(missing_data_maybe, song_id); + // if there's nothing in missing song data, check song array if (array == NULL) - { - if (missing_data_maybe == NULL) - return 0; - else - array = DataFindArray(missing_data_maybe, song_id); - } + array = DataFindArray(song, song_id); + // no song_id? idk man if (array == NULL) return 0; + // get the value from song_id found = DataNodeEvaluate(&array->mNodes->n[1]); if (found == NULL) return 0; @@ -81,4 +79,4 @@ SongMetadata *InitSongMetadataHook(SongMetadata *songMetadata) // increment the loaded song count based on how many songmetadata objects there are RB3E_LoadedSongCount++; return InitSongMetadata(songMetadata); -} +} \ No newline at end of file diff --git a/source/SongParserHooks.c b/source/SongParserHooks.c new file mode 100644 index 0000000..fbc9d59 --- /dev/null +++ b/source/SongParserHooks.c @@ -0,0 +1,41 @@ +/* + RB3Enhanced - SongParserHooks.c + Hooks related to the parsing of song MIDIs. +*/ + +#include +#include +#include "SongParserHooks.h" +#include "rb3/ModifierManager.h" +#include "GlobalSymbols.h" + +int SongParserPitchToSlotHook(SongParser *thisSongParser, int pitch, int *difficulty, int tick) +{ + Modifier *doubleBassModifier; + int ret; + + ret = SongParserPitchToSlot(thisSongParser, pitch, difficulty, tick); + doubleBassModifier = ModifierActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.doubleBass, 0); + + // only read the notes when double bass pedal modifier is on + if (doubleBassModifier->enabled) + { + // make sure the current track type is drums + if (thisSongParser->mTrackType == kTrackDrum) + { + // check for MIDI note pitch 95 (which is the 2x bass pedal note) + if (pitch == 95) + { + // the original function is expected to modify the difficulty value depending on the midi note pitch (which is why it is a ptr), so do that here + // we don't need to worry about the other difficulties since this is purely expert only. although adding support for those in the future would be easy + if (*difficulty == 4) + { + *difficulty = 3; + return 0; + } + } + } + } + + return ret; +} \ No newline at end of file diff --git a/source/_functions.c b/source/_functions.c index 9f3d012..e7f6e73 100644 --- a/source/_functions.c +++ b/source/_functions.c @@ -15,7 +15,7 @@ #define RB3E_STUB(x) \ __declspec(naked) void x() \ { \ - __asm { li r3, __LINE__ } \ + __asm { li r3, __LINE__ } \ } #else // other platforms should use GCC notation #define RB3E_STUB(x) \ @@ -25,13 +25,15 @@ } #endif +RB3E_STUB(RB3EBase); // this will always be the start of .text + // function stub definitions #ifndef RB3E_WII RB3E_STUB(AppConstructor) // AppConstructor is handled by the BrainSlug engine #endif RB3E_STUB(ExecuteDTA) RB3E_STUB(SymbolConstruct) -RB3E_STUB(ModifierIsActive) +RB3E_STUB(ModifierActive) RB3E_STUB(HmxFactoryFuncAt) RB3E_STUB(BandLabelSetDisplayText) RB3E_STUB(RandomInt) @@ -42,7 +44,7 @@ RB3E_STUB(FileExists) RB3E_STUB(QueueMessage) RB3E_STUB(GetMetadata) RB3E_STUB(GetSongIDFromShortname) -RB3E_STUB(GetSongSymbol) +RB3E_STUB(GetSongShortname) RB3E_STUB(GetBandUsers) RB3E_STUB(GetBandUserFromSlot) RB3E_STUB(ChunkStreamConstructor) @@ -59,6 +61,11 @@ RB3E_STUB(MemPrint) RB3E_STUB(MemNumHeaps) RB3E_STUB(MemAlloc) RB3E_STUB(MemFree) +RB3E_STUB(FileIsDLC) +RB3E_STUB(DxRndSuspend) +#ifndef RB3E_XBOX +RB3E_STUB(DataRegisterFunc) // DataRegisterFunc is inlined on 360 +#endif // hooked function stubs RB3E_STUB(Localize) RB3E_STUB(SetVenue) @@ -116,4 +123,35 @@ RB3E_STUB(NodeSortGetNode) RB3E_STUB(DynamicTexConstructor) RB3E_STUB(DynamicTexDestructor) RB3E_STUB(RndMatSetDiffuseTex) -RB3E_STUB(RndTexSetBitmap3) \ No newline at end of file +RB3E_STUB(RndTexSetBitmap3) +RB3E_STUB(BinstreamWrite) +RB3E_STUB(BinstreamRead) +RB3E_STUB(BinstreamReadEndian) +RB3E_STUB(BinstreamWriteEndian) +RB3E_STUB(SongParserPitchToSlot) +RB3E_STUB(DataSet) +RB3E_STUB(DataSetElem) +RB3E_STUB(DataOnElem) +RB3E_STUB(DataNodeGetObj) +RB3E_STUB(HeapInit) +RB3E_STUB(ResolvedModuleKeyboard) +RB3E_STUB(XboxContentConstruct) +RB3E_STUB(CacheMgrXbox_MountAsync) +RB3E_STUB(DataArrayExecute) + +#ifdef RB3E_WII +// Wii-specific functions +// FUTURE(Emma): these really ought to be provided by the BrainSlug loader +RB3E_STUB(OSFatal) +RB3E_STUB(OSSetErrorHandler) +RB3E_STUB(PPCHalt) +RB3E_STUB(OSReturnToMenu) +RB3E_STUB(ARCInitHandle) +RB3E_STUB(contentInitHandleTitleNAND) +RB3E_STUB(CNTReleaseHandle) +RB3E_STUB(CNTRead) +RB3E_STUB(EC_GetContentInfos) +RB3E_STUB(CNTOpen) +#endif + +RB3E_STUB(RB3EStubEnd); diff --git a/source/aes.c b/source/aes.c new file mode 100644 index 0000000..9ab5131 --- /dev/null +++ b/source/aes.c @@ -0,0 +1,575 @@ +#ifdef RB3E_WII + +/* + +This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. +Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. + +The implementation is verified against the test vectors in: + National Institute of Standards and Technology Special Publication 800-38A 2001 ED + +ECB-AES128 +---------- + + plain-text: + 6bc1bee22e409f96e93d7e117393172a + ae2d8a571e03ac9c9eb76fac45af8e51 + 30c81c46a35ce411e5fbc1191a0a52ef + f69f2445df4f9b17ad2b417be66c3710 + + key: + 2b7e151628aed2a6abf7158809cf4f3c + + resulting cipher + 3ad77bb40d7a3660a89ecaf32466ef97 + f5d3d58503b9699de785895a96fdbaaf + 43b1cd7f598ece23881b00e3ed030688 + 7b0c785e27e8ad3f8223207104725dd4 + + +NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) + You should pad the end of the string with zeros if this is not the case. + For AES192/256 the key size is proportionally larger. + +*/ + + +/*****************************************************************************/ +/* Includes: */ +/*****************************************************************************/ +#include // CBC mode, for memset +#include "aes.h" + +/*****************************************************************************/ +/* Defines: */ +/*****************************************************************************/ +// The number of columns comprising a state in AES. This is a constant in AES. Value=4 +#define Nb 4 + +#if defined(AES256) && (AES256 == 1) + #define Nk 8 + #define Nr 14 +#elif defined(AES192) && (AES192 == 1) + #define Nk 6 + #define Nr 12 +#else + #define Nk 4 // The number of 32 bit words in a key. + #define Nr 10 // The number of rounds in AES Cipher. +#endif + +// jcallan@github points out that declaring Multiply as a function +// reduces code size considerably with the Keil ARM compiler. +// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 +#ifndef MULTIPLY_AS_A_FUNCTION + #define MULTIPLY_AS_A_FUNCTION 0 +#endif + + + + +/*****************************************************************************/ +/* Private variables: */ +/*****************************************************************************/ +// state - array holding the intermediate results during decryption. +typedef uint8_t state_t[4][4]; + + + +// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM +// The numbers below can be computed dynamically trading ROM for RAM - +// This can be useful in (embedded) bootloader applications, where ROM is often limited. +static const uint8_t sbox[256] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +static const uint8_t rsbox[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; +#endif + +// The round constant word array, Rcon[i], contains the values given by +// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) +static const uint8_t Rcon[11] = { + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; + +/* + * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), + * that you can remove most of the elements in the Rcon array, because they are unused. + * + * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon + * + * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), + * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." + */ + + +/*****************************************************************************/ +/* Private functions: */ +/*****************************************************************************/ +/* +static uint8_t getSBoxValue(uint8_t num) +{ + return sbox[num]; +} +*/ +#define getSBoxValue(num) (sbox[(num)]) + +// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. +static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) +{ + unsigned i, j, k; + uint8_t tempa[4]; // Used for the column/row operations + + // The first round key is the key itself. + for (i = 0; i < Nk; ++i) + { + RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; + RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; + RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; + RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; + } + + // All other round keys are found from the previous round keys. + for (i = Nk; i < Nb * (Nr + 1); ++i) + { + { + k = (i - 1) * 4; + tempa[0]=RoundKey[k + 0]; + tempa[1]=RoundKey[k + 1]; + tempa[2]=RoundKey[k + 2]; + tempa[3]=RoundKey[k + 3]; + + } + + if (i % Nk == 0) + { + // This function shifts the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + + // Function RotWord() + { + const uint8_t u8tmp = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = u8tmp; + } + + // SubWord() is a function that takes a four-byte input word and + // applies the S-box to each of the four bytes to produce an output word. + + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + + tempa[0] = tempa[0] ^ Rcon[i/Nk]; + } +#if defined(AES256) && (AES256 == 1) + if (i % Nk == 4) + { + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + } +#endif + j = i * 4; k=(i - Nk) * 4; + RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; + RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; + RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; + RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; + } +} + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) +{ + KeyExpansion(ctx->RoundKey, key); +} +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) +{ + KeyExpansion(ctx->RoundKey, key); + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) +{ + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +#endif + +// This function adds the round key to state. +// The round key is added to the state by an XOR function. +static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) +{ + uint8_t i,j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; + } + } +} + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void SubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxValue((*state)[j][i]); + } + } +} + +// The ShiftRows() function shifts the rows in the state to the left. +// Each row is shifted with different offset. +// Offset = Row number. So the first row is not shifted. +static void ShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to left + temp = (*state)[0][1]; + (*state)[0][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[3][1]; + (*state)[3][1] = temp; + + // Rotate second row 2 columns to left + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to left + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[3][3]; + (*state)[3][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[1][3]; + (*state)[1][3] = temp; +} + +static uint8_t xtime(uint8_t x) +{ + return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); +} + +// MixColumns function mixes the columns of the state matrix +static void MixColumns(state_t* state) +{ + uint8_t i; + uint8_t Tmp, Tm, t; + for (i = 0; i < 4; ++i) + { + t = (*state)[i][0]; + Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; + Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; + Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; + Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; + Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; + } +} + +// Multiply is used to multiply numbers in the field GF(2^8) +// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary +// The compiler seems to be able to vectorize the operation better this way. +// See https://github.com/kokke/tiny-AES-c/pull/34 +#if MULTIPLY_AS_A_FUNCTION +static uint8_t Multiply(uint8_t x, uint8_t y) +{ + return (((y & 1) * x) ^ + ((y>>1 & 1) * xtime(x)) ^ + ((y>>2 & 1) * xtime(xtime(x))) ^ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ + } +#else +#define Multiply(x, y) \ + ( ((y & 1) * x) ^ \ + ((y>>1 & 1) * xtime(x)) ^ \ + ((y>>2 & 1) * xtime(xtime(x))) ^ \ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ + +#endif + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +/* +static uint8_t getSBoxInvert(uint8_t num) +{ + return rsbox[num]; +} +*/ +#define getSBoxInvert(num) (rsbox[(num)]) + +// MixColumns function mixes the columns of the state matrix. +// The method used to multiply may be difficult to understand for the inexperienced. +// Please use the references to gain more information. +static void InvMixColumns(state_t* state) +{ + int i; + uint8_t a, b, c, d; + for (i = 0; i < 4; ++i) + { + a = (*state)[i][0]; + b = (*state)[i][1]; + c = (*state)[i][2]; + d = (*state)[i][3]; + + (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); + (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); + (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); + (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); + } +} + + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void InvSubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxInvert((*state)[j][i]); + } + } +} + +static void InvShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to right + temp = (*state)[3][1]; + (*state)[3][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[0][1]; + (*state)[0][1] = temp; + + // Rotate second row 2 columns to right + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to right + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[1][3]; + (*state)[1][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[3][3]; + (*state)[3][3] = temp; +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +// Cipher is the main function that encrypts the PlainText. +static void Cipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(0, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without MixColumns() + for (round = 1; ; ++round) + { + SubBytes(state); + ShiftRows(state); + if (round == Nr) { + break; + } + MixColumns(state); + AddRoundKey(round, state, RoundKey); + } + // Add round key to last round + AddRoundKey(Nr, state, RoundKey); +} + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +static void InvCipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(Nr, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without InvMixColumn() + for (round = (Nr - 1); ; --round) + { + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(round, state, RoundKey); + if (round == 0) { + break; + } + InvMixColumns(state); + } + +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +/*****************************************************************************/ +/* Public functions: */ +/*****************************************************************************/ +#if defined(ECB) && (ECB == 1) + + +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call encrypts the PlainText with the Key using AES algorithm. + Cipher((state_t*)buf, ctx->RoundKey); +} + +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call decrypts the PlainText with the Key using AES algorithm. + InvCipher((state_t*)buf, ctx->RoundKey); +} + + +#endif // #if defined(ECB) && (ECB == 1) + + + + + +#if defined(CBC) && (CBC == 1) + + +static void XorWithIv(uint8_t* buf, const uint8_t* Iv) +{ + uint8_t i; + for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size + { + buf[i] ^= Iv[i]; + } +} + +void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length) +{ + size_t i; + uint8_t *Iv = ctx->Iv; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + XorWithIv(buf, Iv); + Cipher((state_t*)buf, ctx->RoundKey); + Iv = buf; + buf += AES_BLOCKLEN; + } + /* store Iv in ctx for next call */ + memcpy(ctx->Iv, Iv, AES_BLOCKLEN); +} + +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) +{ + size_t i; + uint8_t storeNextIv[AES_BLOCKLEN]; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + memcpy(storeNextIv, buf, AES_BLOCKLEN); + InvCipher((state_t*)buf, ctx->RoundKey); + XorWithIv(buf, ctx->Iv); + memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); + buf += AES_BLOCKLEN; + } + +} + +#endif // #if defined(CBC) && (CBC == 1) + + + +#if defined(CTR) && (CTR == 1) + +/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) +{ + uint8_t buffer[AES_BLOCKLEN]; + + size_t i; + int bi; + for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) + { + if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ + { + + memcpy(buffer, ctx->Iv, AES_BLOCKLEN); + Cipher((state_t*)buffer,ctx->RoundKey); + + /* Increment Iv and handle overflow */ + for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) + { + /* inc will overflow */ + if (ctx->Iv[bi] == 255) + { + ctx->Iv[bi] = 0; + continue; + } + ctx->Iv[bi] += 1; + break; + } + bi = 0; + } + + buf[i] = (buf[i] ^ buffer[bi]); + } +} + +#endif // #if defined(CTR) && (CTR == 1) + +#endif // RB3E_WII diff --git a/source/config.c b/source/config.c index 8628622..540c607 100644 --- a/source/config.c +++ b/source/config.c @@ -27,8 +27,8 @@ void InitDefaultConfig() memset(&config, 0, sizeof(config)); strcpy(config.RawfilesDir, "rb3"); #ifdef RB3E_WII - // uncomment when GoCentral has a default instance that uses naswii auth - // strcpy(config.NASServer, "naswii.rbenhanced.rocks"); + strcpy(config.NASServer, "naswii.rbenhanced.rocks"); + config.ModernSDMode = 1; #endif config.SongSpeedMultiplier = 1.0; config.TrackSpeedMultiplier = 1.0; @@ -59,6 +59,8 @@ static int INIHandler(void *user, const char *section, const char *name, const c config.LogFileAccess = RB3E_CONFIG_BOOL(value); if (strcmp(name, "UnlockClothing") == 0) config.UnlockClothing = RB3E_CONFIG_BOOL(value); + if (strcmp(name, "DisableMenuMusic") == 0) + config.DisableMenuMusic = RB3E_CONFIG_BOOL(value); if (strcmp(name, "LanguageOverride") == 0 && strlen(value) == RB3E_LANG_LEN) strncpy(config.LanguageOverride, value, RB3E_LANG_LEN); if (strcmp(name, "RawfilesDir") == 0 && !RB3E_CONFIG_FALSE(value)) @@ -67,6 +69,8 @@ static int INIHandler(void *user, const char *section, const char *name, const c config.DisableRawfiles = RB3E_CONFIG_BOOL(value); if (strcmp(name, "QuazalLogging") == 0) config.QuazalLogging = RB3E_CONFIG_BOOL(value); + if (strcmp(name, "ContentLogging") == 0) + config.ContentLogging = RB3E_CONFIG_BOOL(value); } if (strcmp(section, "Events") == 0) { @@ -80,7 +84,7 @@ static int INIHandler(void *user, const char *section, const char *name, const c if (strcmp(name, "EnableGoCentral") == 0) config.EnableGoCentral = RB3E_CONFIG_BOOL(value); if (strcmp(name, "GoCentralAddress") == 0) - strncpy(config.GoCentralAddress, value, RB3E_MAX_CONFIG_LEN); + strncpy(config.GoCentralAddress, value, RB3E_MAX_DOMAIN); } if (strcmp(section, "HTTP") == 0) { @@ -88,6 +92,8 @@ static int INIHandler(void *user, const char *section, const char *name, const c config.EnableHTTPServer = RB3E_CONFIG_BOOL(value); if (strcmp(name, "AllowCORS") == 0) config.AllowCORS = RB3E_CONFIG_BOOL(value); + if (strcmp(name, "AllowScripts") == 0) + config.AllowScripts = RB3E_CONFIG_BOOL(value); } if (strcmp(section, "Network") == 0) { @@ -103,6 +109,8 @@ static int INIHandler(void *user, const char *section, const char *name, const c strncpy(config.NASServer, value, RB3E_MAX_DOMAIN); if (strcmp(name, "LegacySDMode") == 0) config.LegacySDMode = RB3E_CONFIG_BOOL(value); + if (strcmp(name, "ModernSDMode") == 0) + config.ModernSDMode = RB3E_CONFIG_BOOL(value); } #elif RB3E_XBOX if (strcmp(section, "Xbox360") == 0) diff --git a/source/net_http_server.c b/source/net_http_server.c index 0e9b410..27f4e02 100644 --- a/source/net_http_server.c +++ b/source/net_http_server.c @@ -8,9 +8,11 @@ #include #include #include +#include #include "quazal/InetAddress.h" #include "rb3/SongMetadata.h" #include "rb3/BandSongMgr.h" +#include "rb3/RockCentralGateway.h" #include "rb3enhanced.h" #include "version.h" #include "ports.h" @@ -24,6 +26,7 @@ int RB3E_HTTPSocket = -1; static int InitFailed = 0; static char *PendingShortname = NULL; +static char *PendingScript = NULL; typedef struct _song_list_vector { @@ -59,6 +62,39 @@ typedef enum _HTTP_Request_Status HTTP_REQUEST_DONE } HTTP_Request_Status; +// function from https://stackoverflow.com/a/14530993 +void urldecode2(char *dst, const char *src) +{ + char a, b; + while (*src) + { + if ((*src == '%') && + ((a = src[1]) && (b = src[2])) && + (isxdigit(a) && isxdigit(b))) + { + if (a >= 'a') + a -= 'a' - 'A'; + if (a >= 'A') + a -= ('A' - 10); + else + a -= '0'; + if (b >= 'a') + b -= 'a' - 'A'; + if (b >= 'A') + b -= ('A' - 10); + else + b -= '0'; + *dst++ = 16 * a + b; + src += 3; + } + else + { + *dst++ = *src++; + } + } + *dst++ = '\0'; +} + void HTTP_Server_Accept(void *connection) { int s = (int)connection; @@ -69,6 +105,7 @@ void HTTP_Server_Accept(void *connection) int state_read_bytes = 0; int read_bytes = 0; char request_path[250] = {0}; + char decoded_path[0x400]; int request_valid = 0; char last_byte = '\0'; const char ok_response[] = "HTTP/1.1 200 OK\r\nServer: RB3Enhanced " RB3E_BUILDTAG "\r\nContent-Type: text/html\r\nX-Clacks-Overhead: GNU maxton\r\nContent-Length: 2\r\n\r\nOK"; @@ -76,6 +113,7 @@ void HTTP_Server_Accept(void *connection) char response_buffer[2000] = {0}; int song_id = 0; char *shortname = NULL; + char *script = NULL; SongMetadata *song_metadata = NULL; int list_count = 0; int i = 0; @@ -159,10 +197,11 @@ void HTTP_Server_Accept(void *connection) state_read_bytes = 0; } } - RB3E_DEBUG("Got a request for '%s' over HTTP", request_path); - if (strstr(request_path, "/song_") == request_path) + urldecode2(decoded_path, request_path); + RB3E_DEBUG("Got a request for '%s' over HTTP", decoded_path); + if (strstr(decoded_path, "/song_") == decoded_path) { - if (sscanf(request_path + sizeof("/song"), "%i", &song_id) == 1) + if (sscanf(decoded_path + sizeof("/song"), "%i", &song_id) == 1) { RB3E_DEBUG("Fetching song metadata for %i over HTTP", song_id); song_metadata = GetMetadata((BandSongMgr *)PORT_THESONGMGR, song_id); @@ -179,7 +218,7 @@ void HTTP_Server_Accept(void *connection) strcat(response_buffer, "Content-Type: text/plain\r\n"); strcat(response_buffer, "\r\n"); strcat(response_buffer, "shortname="); - strcat(response_buffer, song_metadata->mShortName.sym); + strcat(response_buffer, song_metadata->shortname.sym); strcat(response_buffer, "\r\n"); strcat(response_buffer, "title="); strcat(response_buffer, song_metadata->title.buf); @@ -191,17 +230,17 @@ void HTTP_Server_Accept(void *connection) strcat(response_buffer, song_metadata->album.buf); strcat(response_buffer, "\r\n"); strcat(response_buffer, "origin="); - strcat(response_buffer, song_metadata->mGameOrigin.sym); + strcat(response_buffer, song_metadata->gameOrigin.sym); strcat(response_buffer, "\r\n"); strcat(response_buffer, "\r\n"); RB3E_TCP_Send(s, (void *)response_buffer, strlen(response_buffer)); goto close_connection; } } - else if (strstr(request_path, "/jump?shortname=") == request_path) + else if (strstr(decoded_path, "/jump?shortname=") == decoded_path) { // TODO(Emma): This will crash if you're not in the music library. Figure out if we can detect what screen we're on - shortname = request_path + 16; + shortname = decoded_path + 16; RB3E_DEBUG("Jumping to song %s in Music Library", shortname); // hacky solution to send the shortname to the main thread PendingShortname = shortname; @@ -210,7 +249,16 @@ void HTTP_Server_Accept(void *connection) RB3E_TCP_Send(s, (void *)ok_response, strlen(ok_response)); goto close_connection; } - else if (strcmp(request_path, "/list_songs") == 0) + else if (strstr(decoded_path, "/execute?script=") == decoded_path && config.AllowScripts) + { + script = decoded_path + 16; + PendingScript = script; + while (PendingScript != NULL) + RB3E_Sleep(1); + RB3E_TCP_Send(s, (void *)ok_response, strlen(ok_response)); + goto close_connection; + } + else if (strcmp(decoded_path, "/list_songs") == 0) { if (song_list.start == NULL) SongMgrGetRankedSongs((BandSongMgr *)PORT_THESONGMGR, &song_list, 0, 0); @@ -232,10 +280,10 @@ void HTTP_Server_Accept(void *connection) if (song_metadata != NULL) { strcat(response_buffer, "["); - strcat(response_buffer, song_metadata->mShortName.sym); + strcat(response_buffer, song_metadata->shortname.sym); strcat(response_buffer, "]\r\n"); strcat(response_buffer, "shortname="); - strcat(response_buffer, song_metadata->mShortName.sym); + strcat(response_buffer, song_metadata->shortname.sym); strcat(response_buffer, "\r\n"); strcat(response_buffer, "title="); strcat(response_buffer, song_metadata->title.buf); @@ -247,7 +295,7 @@ void HTTP_Server_Accept(void *connection) strcat(response_buffer, song_metadata->album.buf); strcat(response_buffer, "\r\n"); strcat(response_buffer, "origin="); - strcat(response_buffer, song_metadata->mGameOrigin.sym); + strcat(response_buffer, song_metadata->gameOrigin.sym); strcat(response_buffer, "\r\n"); strcat(response_buffer, "\r\n"); if (strlen(response_buffer) > 1500) @@ -265,7 +313,7 @@ void HTTP_Server_Accept(void *connection) } goto close_connection; } - else if (strcmp(request_path, "/") == 0) + else if (strcmp(decoded_path, "/") == 0) { file_path = RB3E_GetRawfilePath("rb3e_index.html", 1); if (file_path == NULL) @@ -298,7 +346,7 @@ void HTTP_Server_Accept(void *connection) } goto close_connection; } - else if (strcmp(request_path, "/jsonrpc") == 0) + else if (strcmp(decoded_path, "/jsonrpc") == 0) { file_path = RB3E_GetRawfilePath("discordrp.json", 1); if (file_path == NULL) @@ -359,6 +407,11 @@ void HTTP_Server_RunLoop() MusicLibrarySelectSong(PendingShortname); PendingShortname = NULL; } + if (PendingScript != NULL) + { + ExecuteDTA(PORT_ROCKCENTRALGATEWAY, PendingScript); + PendingScript = NULL; + } // accept incoming connections // TODO(Emma): add some sort of subnet restriction and ratelimit r = RB3E_TCP_Accept(RB3E_HTTPSocket, &connected_ip, &connected_port); diff --git a/source/net_natpmp.c b/source/net_natpmp.c index d277747..cf74fba 100644 --- a/source/net_natpmp.c +++ b/source/net_natpmp.c @@ -91,7 +91,7 @@ void PCP_RequestOpenPort(unsigned short port) memcpy(req.nonce, mapping_nonce, sizeof(mapping_nonce)); fill_ipv4_mapped_ipv6(&req.suggested_address, 0); // fire it off to the gateway - RB3E_UDP_SendTo(natpmp_socket, gateway_ipv4, PCP_COMMAND_PORT, &req, sizeof(NATPMP_MappingRequest)); + RB3E_UDP_SendTo(natpmp_socket, gateway_ipv4, PCP_COMMAND_PORT, &req, sizeof(PCP_MAPRequest)); RB3E_DEBUG("Requested port %i from PCP", port); } diff --git a/source/rb3enhanced.c b/source/rb3enhanced.c index 2e521bf..27a26a0 100644 --- a/source/rb3enhanced.c +++ b/source/rb3enhanced.c @@ -18,7 +18,8 @@ char *DefinesHook(char *string_define, int always_null) case 0: return "RB3E"; case 1: - // might not be working? + return "RB3E_HAS_VERSION"; + case 2: if (RB3E_IsEmulator()) return "RB3E_EMULATOR"; return NULL; @@ -33,7 +34,7 @@ void SetVenueHook(int *thisMetaPerformer, Symbol venue) Modifier *blackBackgroundModifier; SymbolConstruct(&blackBackground, "mod_black_background"); - blackBackgroundModifier = ModifierIsActive(*(int *)PORT_MODIFIERMGR_POINTER, blackBackground, 0); + blackBackgroundModifier = ModifierActive(*(int *)PORT_MODIFIERMGR_POINTER, blackBackground, 0); if (blackBackgroundModifier->enabled) venue.sym = "none"; @@ -57,22 +58,37 @@ void UpdatePresenceHook(void *thisPresenceMgr) } // New file hook, for ARKless file loading -void *NewFileHook(char *iFilename, int iMode) +void *NewFileHook(char *fileName, int flags) { char *new_path = NULL; + if (strlen(fileName) > 250) + return NULL; if (config.DisableRawfiles) goto LOAD_ORIGINAL; // checks the platform-specific APIs for the file - new_path = RB3E_GetRawfilePath(iFilename, 0); + new_path = RB3E_GetRawfilePath(fileName, 0); if (new_path != NULL) { - iFilename = new_path; - iMode = 0x10002; // tell the game to load this file raw + fileName = new_path; + flags = 0x10002; // tell the game to load this file raw + } +#ifdef RB3E_WII + // load the FileSD + if (RB3E_Mounted > 0 && + (memcmp(fileName, "sd:/", 3) == 0 || new_path != NULL)) { + return FileSD_New(fileName, flags); } +#ifdef RB3EDEBUG + // only on debug builds and only on Dolphin, we can read from NAND + if (RB3E_IsEmulator() && memcmp(fileName, "nand/", 5) == 0) { + return FileWiiNAND_New(fileName + 4); + } +#endif +#endif LOAD_ORIGINAL: if (config.LogFileAccess) - RB3E_MSG("File: %s (%s)", iFilename, (iMode & 0x10000) ? "Raw" : "ARK"); - return NewFile(iFilename, iMode); + RB3E_MSG("File: %s (%s)", fileName, (flags & 0x10000) ? "Raw" : "ARK"); + return NewFile(fileName, flags); } DataArray *DataReadFileHook(char *path, int dtb) @@ -112,15 +128,16 @@ void *ModifierManagerConstructorHook(int thisModifierManager, int unk) ExecuteDTA(PORT_ROCKCENTRALGATEWAY, "{do{push_back {find $syscfg modifiers modifiers} (mod_mirror_mode)}}"); ExecuteDTA(PORT_ROCKCENTRALGATEWAY, "{do{push_back {find $syscfg modifiers modifiers} (mod_color_shuffle)}}"); ExecuteDTA(PORT_ROCKCENTRALGATEWAY, "{do{push_back {find $syscfg modifiers modifiers} (mod_gem_shuffle)}}"); + ExecuteDTA(PORT_ROCKCENTRALGATEWAY, "{do{push_back {find $syscfg modifiers modifiers} (mod_double_bass)}}"); return ModifierManagerConstructor(thisModifierManager, unk); } // a TextStream object that prints straight to RB3E_DEBUG -void DebugTextStreamDestructor(void *thisTextStream) +void DebugTextStreamDestructor(TextStream *thisTextStream) { // not dynamically allocating anything, can't be a destructor } -void DebugTextStreamPrint(void *thisTextStream, char *text) +void DebugTextStreamPrint(TextStream *thisTextStream, char *text) { #ifdef RB3E_XBOX // HACK(Emma): retail xbdm doesn't like newlines. @@ -155,6 +172,7 @@ static unsigned int framecount = 0; // STUFF YOU DO HERE WILL **DIRECTLY** IMPACT THE GAME'S FRAMERATE! void HTTP_Server_RunLoop(); void Liveless_Poll(); +void KeyboardPoll(); void RB3E_RunLoop() { if (config.EnableNATPMP) @@ -166,6 +184,7 @@ void RB3E_RunLoop() Liveless_Poll(); if (config.EnableUPnP) UPNP_Poll(); + KeyboardPoll(); #endif #ifdef RB3EDEBUG // print out memory every 5 seconds @@ -206,16 +225,32 @@ void ApplyPatches() POKE_32(PORT_STRAPSCREEN_2, NOP); // Patch out erroneous second host header POKE_32(PORT_NASWII_HOST, NOP); - // always take the branch to 0x8024a628 so vocals can be selected without a mic plugged in - POKE_32(PORT_MICCHECK, 0x42800140); // always fire the UpdatePresence function. TODO(Emma): look into it, still not firing when screen is changed :/ POKE_32(PORT_UPDATEPRESENCEBLOCK_B, NOP); +#ifdef RB3E_WII_BANK8 + // nop debug crash enumerating content with legacysdmode disabled + POKE_32(0x80419158, NOP); +#endif +#ifndef RB3E_WII_BANK8 + // always take the branch to 0x8024a628 so vocals can be selected without a mic plugged in + // bank 8 does not have the mic check + POKE_32(PORT_MICCHECK, 0x42800140); +#endif #elif RB3E_XBOX if (RB3E_IsEmulator()) POKE_32(PORT_SONGMGR_ISDEMO_CHECK, NOP); // skips check for stagekit to allow for fog commands to be issued without a stagekit plugged in POKE_32(PORT_STAGEKIT_EXISTS, NOP); + + // just blr instead of actually triggering the breakpoint + POKE_32(PORT_QUAZAL_BREAKPOINT, BLR); +#endif + // fixes a crash in online multiplayer +#ifdef RB3E_WII + POKE_B(PORT_MULTIPLAYER_CRASH, PORT_MULTIPLAYER_FIX); +#else + POKE_BL(PORT_MULTIPLAYER_CRASH, PORT_MULTIPLAYER_FIX); #endif RB3E_MSG("Patches applied!", NULL); } @@ -236,16 +271,28 @@ void ApplyConfigurablePatches() POKE_32(PORT_RENDER_RES_Y_PATCH1, LI(11, config.RenderResY)); } #endif + if (config.UnlockClothing == 1) { // Unlocks all clothing, tattoos, face paint, and video venues - // TODO: Figure out what marks items as locked in the UI and patch that as well, right now it still shows them as locked - POKE_32(PORT_CHARACTER_CLOTHES_CHECK, NOP); + POKE_32(PORT_CHARACTER_CLOTHES_CHECK, LI(3, 1)); + POKE_32(PORT_CHARACTER_CLOTHES_CHECK2, BLR); POKE_32(PORT_TATTOO_CHECK, LI(3, 1)); POKE_32(PORT_FACE_PAINT_CHECK, LI(3, 1)); POKE_32(PORT_VIDEO_VENUE_CHECK, LI(3, 1)); } + if (config.DisableMenuMusic == 1) + { + // Disables MetaMusic from loading, saves space on the heap + POKE_32(PORT_METAMUSICLOAD, BLR); + POKE_32(PORT_METAMUSICSTART, BLR); + POKE_32(PORT_METAMUSICPOLL, BLR); + // Always return 1 from MetaMusic::Loaded, in MetaPanel::IsLoaded + POKE_32(PORT_METAMUSICISLOADED, LI(3, 1)); + POKE_32(PORT_METAMUSICISLOADED + 4, BLR); + } + #ifdef RB3EDEBUG if (config.QuazalLogging == 1) { @@ -284,6 +331,14 @@ void SymbolPreInitHook(int stringTableSize, int hashTableSize) InitGlobalSymbols(); } +#ifdef RB3E_WII +char FileIsLocalHook(char *filepath) { + if (memcmp(filepath, "sd:/", 4) == 0) + return 1; + return FileIsDLC(filepath); +} +#endif + void InitialiseFunctions() { #ifndef RB3E_WII @@ -298,20 +353,28 @@ void InitialiseFunctions() POKE_B(&DataFindData, PORT_DATAARRAYFINDDATA); #endif POKE_B(&SongMgrGetRankedSongs, PORT_SONGMGRGETRANKEDSONGS); +#ifndef RB3E_WII_BANK8 POKE_B(&PrepareSomeVectorMaybe, PORT_PREPARESOMEVECTORMAYBE); POKE_B(&SomeVectorPushBackMaybe, PORT_SOMEVECTORPUSHBACKMAYBE); +#endif POKE_B(&ExecuteDTA, PORT_EXECUTEDTA); POKE_B(&BandLabelSetDisplayText, PORT_BANDLABELSETDISPLAYTEXT); POKE_B(&SymbolConstruct, PORT_SYMBOL_CT); - POKE_B(&ModifierIsActive, PORT_MODIFIERMGR_ACTIVE); + POKE_B(&ModifierActive, PORT_MODIFIERMGR_ACTIVE); + POKE_B(&GetBandUserFromSlot, PORT_GETBANDUSERFROMSLOT); +#ifndef RB3E_WII_BANK8 POKE_B(&HmxFactoryFuncAt, PORT_HMXFACTORYFUNCAT); + // TODO(Emma): port to bank8 + POKE_B(&ObjectFindUIPanel, PORT_OBJECTFINDUIPANEL); + POKE_B(&vector_push_back, PORT_VECTORPUSHBACK); // otherwise unused it seems? +#endif POKE_B(&RandomInt, PORT_RANDOMINT); POKE_B(&DataNodeEvaluate, PORT_DATANODEEVALUATE); POKE_B(&FileExists, PORT_FILE_EXISTS); POKE_B(&SetAddress, PORT_SETADDRESS); POKE_B(&QueueMessage, PORT_QUEUEMESSAGE); POKE_B(&MusicLibrarySelect, PORT_MUSICLIBRARYSELECTMAYBE); - POKE_B(&GetSongSymbol, PORT_GETSONGSYMBOL); + POKE_B(&GetSongShortname, PORT_GETSONGSHORTNAME); POKE_B(&GetMetadata, PORT_GETMETADATA); POKE_B(&GetSongIDFromShortname, PORT_GETSONGIDFROMSHORTNAME); POKE_B(&GetBandUsers, PORT_GETBANDUSERS); @@ -320,7 +383,6 @@ void InitialiseFunctions() POKE_B(&ChunkStreamConstructor, PORT_CHUNKSTREAM_CT); POKE_B(&Dynamic_Cast, PORT_DYNAMICCAST); POKE_B(&GameGetActivePlayer, PORT_GAMEGETACTIVEPLAYER); - POKE_B(&ObjectFindUIPanel, PORT_OBJECTFINDUIPANEL); POKE_B(&JoypadGetPadData, PORT_JOYPADGETPADDATA); POKE_B(&MemAlloc, PORT_MEMALLOC); POKE_B(&MemFree, PORT_MEMFREE); @@ -330,13 +392,26 @@ void InitialiseFunctions() POKE_B(&RndTexSetBitmap2, PORT_RNDTEXSETBITMAP2); POKE_B(&FilePathConstructor, PORT_FILEPATHCONSTRUCTOR); POKE_B(&NodeSortGetNode, PORT_NODESORTGETNODE); - POKE_B(&vector_push_back, PORT_VECTORPUSHBACK); POKE_B(&GameGemDBConstructor, PORT_GAMEGEMDB_CT); POKE_B(&SongSortMgrGetSort, PORT_SONGSORTMGRGETSORT); POKE_B(&DynamicTexConstructor, PORT_DYNAMICTEX_CT); POKE_B(&DynamicTexDestructor, PORT_DYNAMICTEX_DT); POKE_B(&RndMatSetDiffuseTex, PORT_RNDMATSETDIFFUSETEX); POKE_B(&RndTexSetBitmap3, PORT_RNDTEXSETBITMAP3); + POKE_B(&BinstreamWrite, PORT_BINSTREAMWRITE); + POKE_B(&BinstreamRead, PORT_BINSTREAMREAD); + POKE_B(&BinstreamWriteEndian, PORT_BINSTREAMWRITEENDIAN); + POKE_B(&BinstreamReadEndian, PORT_BINSTREAMREADENDIAN); +#ifdef RB3E_XBOX + POKE_B(&DataArrayExecute, PORT_DATAARRAYEXECUTE); +#endif +#ifndef RB3E_XBOX + POKE_B(&DataRegisterFunc, PORT_DATAREGISTERFUNC); +#endif +#ifdef RB3E_WII + POKE_B(&FileIsDLC, PORT_FILEISDLC); +#endif + RB3E_FlushCache((void *)RB3EBase, (unsigned int)RB3EStubEnd - (unsigned int)RB3EBase); RB3E_MSG("Functions initialized!", NULL); } @@ -345,6 +420,9 @@ void ApplyHooks() POKE_B(PORT_DATAINITFUNCS_TAIL, &AddDTAFunctions); POKE_B(PORT_ISSUPPORTEDLANGUAGE, &IsSupportedLanguageHook); POKE_B(PORT_OVERSHELLPARTSELECTPROVIDERRELOAD, &OvershellPartSelectProviderReload); +//#ifndef RB3E_WII_BANK8 +// POKE_B(PORT_BUILDINSTRUMENTSELECTION, &BuildInstrumentSelectionList); +//#endif POKE_BL(PORT_OPTIONSTR_DEFINE, &DefinesHook); POKE_BL(PORT_RUNLOOP_SPARE, &RB3E_RunLoop); HookFunction(PORT_LOCALIZE, &Localize, &LocalizeHook); @@ -372,22 +450,32 @@ void ApplyHooks() HookFunction(PORT_UPDATEPRESENCE, &UpdatePresence, &UpdatePresenceHook); HookFunction(PORT_MUSICLIBRARY_CT, &MusicLibraryConstructor, &MusicLibraryConstructorHook); HookFunction(PORT_MUSICLIBRARYMAT, &MusicLibraryMat, &MusicLibraryMatHook); + HookFunction(PORT_SONGPARSERPITCHTOSLOT, &SongParserPitchToSlot, &SongParserPitchToSlotHook); + HookFunction(PORT_DATASET, &DataSet, &DataSetHook); + HookFunction(PORT_DATASETELEM, &DataSetElem, &DataSetElemHook); + HookFunction(PORT_DATAONELEM, &DataOnElem, &DataOnElemHook); HookFunction(PORT_MUSICLIBRARYONENTER, &MusicLibraryOnEnter, &MusicLibraryOnEnterHook); HookFunction(PORT_MUSICLIBRARYONUNLOAD, &MusicLibraryOnUnload, &MusicLibraryOnUnloadHook); #ifdef RB3E_WII // wii exclusive hooks - HookFunction(PORT_USBWIIGETTYPE, &UsbWiiGetType, &UsbWiiGetTypeHook); + // HookFunction(PORT_USBWIIGETTYPE, &UsbWiiGetType, &UsbWiiGetTypeHook); HookFunction(PORT_WIINETINIT_DNSLOOKUP, &StartDNSLookup, &StartDNSLookupHook); + POKE_B(PORT_FILEISLOCAL, &FileIsLocalHook); #elif RB3E_XBOX // 360 exclusive hooks HookFunction(PORT_STAGEKIT_SET_STATE, &StagekitSetState, &StagekitSetStateHook); - - // TODO: port these to Wii + // TODO: port these to Wii + HookFunction(PORT_DATANODEGETOBJ, &DataNodeGetObj, &DataNodeGetObjHook); POKE_B(PORT_GETSONGID, &GetSongIDHook); POKE_BL(PORT_SONG_ID_EVALUATE, &MetadataSongIDHook); POKE_BL(PORT_LOADOBJS_BCTRL, &LoadObj); + POKE_BL(PORT_VERTEX_READ_1, &VertexReadHook); + POKE_BL(PORT_VERTEX_READ_2, &VertexReadHook); #endif RB3E_MSG("Hooks applied!", NULL); } +void InitCNTHooks(); +void TryToLoadPRNGKeyFromFile(); + void StartupHook(void *ThisApp, int argc, char **argv) { RB3E_MSG("Loaded! Version " RB3E_BUILDTAG " (" RB3E_BUILDCOMMIT ")", NULL); @@ -406,6 +494,14 @@ void StartupHook(void *ThisApp, int argc, char **argv) // apply any patches that are only added after config loads ApplyConfigurablePatches(); +#ifdef RB3E_WII + if (RB3E_Mounted && config.LegacySDMode == false && config.ModernSDMode == true) + { + TryToLoadPRNGKeyFromFile(); + InitCNTHooks(); + } +#endif + // start the game by calling the proper app constructor RB3E_MSG("Starting Rock Band 3...", NULL); AppConstructor(ThisApp, argc, argv); diff --git a/source/wii.c b/source/wii.c index e761122..7d113dc 100644 --- a/source/wii.c +++ b/source/wii.c @@ -20,10 +20,19 @@ #include "rb3enhanced.h" #include "GlobalSymbols.h" #include "config.h" +#include "rb3/WiiMemMgr.h" +#include "utilities.h" + +char _has256MBMem2 = 0; +int RB3E_Launcher_ExpandedRAM = 0; BSLUG_MODULE_GAME("SZB?"); BSLUG_MODULE_NAME("RB3Enhanced"); +#ifndef RB3E_WII_BANK8 BSLUG_MODULE_VERSION(RB3E_BUILDTAG); +#else +BSLUG_MODULE_VERSION(RB3E_BUILDTAG "-BANK8"); +#endif BSLUG_MODULE_AUTHOR("github.com/RBEnhanced"); BSLUG_MODULE_LICENSE("GPLv2"); @@ -54,6 +63,7 @@ int RB3E_IsEmulator() if (HasRunDetection) return DetectionResult; DetectionResult = DetectDolphin(); + HasRunDetection = 1; return DetectionResult; } @@ -73,6 +83,20 @@ int RB3E_CreateThread(void *address, void *arg, int stack_size) return -1; } +void RB3E_FlushCache(void * address, unsigned int size) { + unsigned int alignedAddress = ((unsigned int)address & ~0x1F); + unsigned int alignedSize = ((unsigned int)address & ~0x1F) + 0x20; + DCFlushRange((void *)alignedAddress, alignedSize); + ICInvalidateRange((void *)alignedAddress, alignedSize); +} + +int RB3E_DeleteSongCache() +{ + // TODO(Emma): delete song cache on Wii + RB3E_MSG("Tried to delete song cache on Wii, unsupported.", NULL); + return 0; +} + int RB3E_RelaunchGame() { /* @@ -97,8 +121,61 @@ int RB3E_RelaunchGame() return -1; } +void HeapInit(Heap *heap, const char *name, int num, int *mem, int sizeWords, int isHandle, int strategy, int debug_level, int allow_temp); +void HeapInitHook(Heap *heap, const char *name, int num, int *mem, int sizeWords, int isHandle, int strategy, int debug_level, int allow_temp) +{ + if (strcmp(name, "main") == 0) + { + // TODO(Emma): somehow reclaim original "mem" buffer here, as right now it's a very large (~38MiB) memory leak + // ideally we would add it as a free block in the resulting heap + mem = (int *)(0x94000000); + sizeWords = 0x3000000; + } + RB3E_DEBUG("Heap %i (\"%s\") = %p, words: 0x%x (size: 0x%x)", num, name, mem, sizeWords, sizeWords * 4); + HeapInit(heap, name, num, mem, sizeWords, isHandle, strategy, debug_level, allow_temp); +} + +#ifdef RB3E_WII_BANK8 +void ResolvedModuleKeyboard(void *keyboardRso); +void ResolvedModuleKeyboardHook(void *keyboardRso) +{ + // The keyboard RSO in bank 8 has asserts that detect if the address is outside of 128MB worth of MEM2 + // so we try to patch out the asserts that crash as well as all the address checks. + // Does this suck? Yes. Does it work? Debatable. + uint32_t *rsoAsUint32 = (uint32_t *)keyboardRso; + for (int i = 0; i < (0x100000 / 4); i++) + { + // check for (address & 0xF8000000) == 0x90000000 + if ((rsoAsUint32[i] & 0xF00FFFFF) == 0x50030008 && // rlwinm rD, rA, 0, 0, 0x4 + (rsoAsUint32[i + 1] & 0xFF00FFFF) == 0x3c007000 && // addis r0, rA, 0x7000 + rsoAsUint32[i + 2] == 0x28000000) // cmplwi r0, 0 + { + RB3E_DEBUG("Patching keyboard address check at %p", &rsoAsUint32[i]); + rsoAsUint32[i + 1] = LI(0, 0); // make the check always 0 (always succeed) + } + // check for assert function start + if (rsoAsUint32[i] == 0x90010070 && + rsoAsUint32[i + 1] == 0x41820158 && + rsoAsUint32[i + 2] == 0x7f83e378) + { + RB3E_DEBUG("Patching assert function at %p", &rsoAsUint32[i]); + rsoAsUint32[i] = BLR; + } + } + ResolvedModuleKeyboard(keyboardRso); +} +#endif + +extern void RB3E_InstallWiiExceptionHandler(); // wii_exceptions.c static void CTHook(void *ThisApp, int argc, char **argv) { + // install an exception handler to show a graphical error when things go wrong + RB3E_InstallWiiExceptionHandler(); + // print out if we're using a 256MB MEM2 + if (_has256MBMem2) + { + RB3E_MSG("Running with 256MB MEM2!", NULL); + } // launch game StartupHook(ThisApp, argc, argv); // apply wfc url patches if gocentral is enabled and configured server isn't Ninty @@ -137,6 +214,79 @@ static void _startHook() { POKE_B(&SymbolConstruct, PORT_SYMBOL_CT); POKE_B(PORT_BIGSYMBOLFUNC_TAIL, InitGlobalSymbols); + + // Extrems's 480p fix + POKE_B(PORT_VISETMODE_LI_28, PORT_VISETMODE_PATCH_CODE); + POKE_32(PORT_VISETMODE_PATCH_CODE + 0, LI(3, 3)); + POKE_32(PORT_VISETMODE_PATCH_CODE + 4, LI(28, 1)); + POKE_B(PORT_VISETMODE_PATCH_CODE + 8, PORT_VISETMODE_LI_28 + 4); + POKE_32(PORT_VISETMODE_STB_28, 0x98610021); // replace stb r28,0x21(r1) with stb r3,0x21(r1) + +#if 0 + // TODO(Emma): can we somehow make this an optional toggle in the launcher? + // Remove the deflicker filter + POKE_32(PORT_GXSETCOPYFILTER_BEQ, 0x48000040); // replace beq with b +#endif + +#ifdef RB3EDEBUG + // for now we limit the ability to use more than intentional RAM to debug builds + // just in case shit hits the fan + // Cool Facts: on a devkit Wii, the retail build of RB3 will have a 128MB heap by default. so we don't want to handle anything extra! + + // detect 256MB being enabled in Dolphin + if (*(uint32_t *)0x80003118 >= 0x10000000) + { + _has256MBMem2 = 1; + // change OS1 globals to act like MEM2 is 64MB + // this is because our Heap expansion code relies on the game thinking it's running on 64MB + POKE_32(0x80003118, 0x04000000); // mem2 size physical + POKE_32(0x8000311C, 0x04000000); // mem2 size simulated + POKE_32(0x80003120, 0x93400000); // end of mem2 + POKE_32(0x80003128, 0x933E0000); // usable mem2 end + POKE_32(0x80003130, 0x933E0000); // ipc buffer start + POKE_32(0x80003134, 0x93400000); // ipc buffer end + } + // otherwise, detect the launcher enabling 256MB on vWii + else if (RB3E_Launcher_ExpandedRAM == 0xCAFE1337) + { + _has256MBMem2 = 1; + } + + if (_has256MBMem2) + { + // change the IBAT and DBAT initialisation functions + + // change ConfigMEM2_52MB to allocate 256MB DBAT + POKE_32(PORT_CONFIGMEM2_52MB_INST + 0x0, ADDI(3, 3, 0x1FFF)); // 0x90000000 cached DBAT + POKE_32(PORT_CONFIGMEM2_52MB_INST + 0x10, ADDI(5, 5, 0x1FFF)); // 0xd0000000 uncached DBAT + // change ConfigMEM2_56MB to allocate 256MB DBAT + POKE_32(PORT_CONFIGMEM2_56MB_INST + 0x0, ADDI(3, 3, 0x1FFF)); // 0x90000000 cached DBAT + POKE_32(PORT_CONFIGMEM2_56MB_INST + 0x10, ADDI(5, 5, 0x1FFF)); // 0xd0000000 uncached DBAT + // change ConfigMEM2_64MB to allocate 256MB DBAT + POKE_32(PORT_CONFIGMEM2_64MB_INST + 0x0, ADDI(3, 3, 0x1FFF)); // 0x90000000 cached DBAT + POKE_32(PORT_CONFIGMEM2_64MB_INST + 0x10, ADDI(5, 5, 0x1FFF)); // 0xd0000000 uncached DBAT + // change EnableInstsOnMEM2Lo16MB to enable insts on all 256MB + // RsoInit calls this in RealMode to allow RSOs to run from MEM2 + POKE_32(PORT_ENABLEINSTSONMEM2LO16MB_INST + 0x0, ADDI(6, 6, 0x1FFF)); // 0x90000000 cached IBAT + // clear icache just in case + ICInvalidateRange((void *)PORT_CONFIGMEM2_52MB_INST, 0x800); + + // hook Heap::Init to change size and location of main heap + HookFunction(PORT_HEAPINIT, HeapInit, HeapInitHook); + +#ifdef RB3E_WII_BANK8 + // RSO loader code gets real mad at you + POKE_32(PORT_BANK8_MEM2_RSO_ASSERT1, LIS(0, 0xa000)); + POKE_32(PORT_BANK8_MEM2_RSO_ASSERT2, LIS(0, 0xa000)); + POKE_32(PORT_BANK8_MEM2_RSO_ASSERT3, LIS(0, 0xa000)); + POKE_32(PORT_BANK8_MEM2_RSO_ASSERT4, LIS(0, 0xa000)); + // hook the keyboard RSO function to accept pointers past 0x98000000 + HookFunction(PORT_BANK8_KEYBOARD_RESOLVED, ResolvedModuleKeyboard, ResolvedModuleKeyboardHook); +#endif + } +#endif + + // launch the game _start(); } diff --git a/source/wii_cnt_crypt.c b/source/wii_cnt_crypt.c new file mode 100644 index 0000000..fa1d73e --- /dev/null +++ b/source/wii_cnt_crypt.c @@ -0,0 +1,428 @@ +/* + RB3Enhanced - wii_cnt_crypt.c + Handling for encrypted and unencrypted Wii CNT(, CNTSD, ARC) files +*/ + +#ifdef RB3E_WII + +#include +#include +#include +#include +#include +#include "rvl/cnt.h" +#include "rvl/wad.h" +#include "rvl/tmd.h" +#include "rb3/Mem.h" +#include "aes.h" +#include "ports.h" +#include "rb3enhanced.h" +#include "wii_cnt_crypt.h" + +#define PRINT_BLOCK(name, x) RB3E_DEBUG(name ": %08x%08x%08x%08x", *(uint32_t *)&x[0], *(uint32_t *)&x[4], *(uint32_t *)&x[8], *(uint32_t *)&x[12]) + +// to be filled in by the brainslug launcher +uint8_t RB3E_ConsolePRNGKey[0x10] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +static uint8_t nullKey[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +// This is really slow apparently +void IOS_AES_Decrypt(uint8_t *input, uint8_t *key, uint8_t *iv, uint8_t *output) +{ + static ios_fd_t aesFd = -1; + if (aesFd == -1) + aesFd = IOS_Open("/dev/aes", IPC_OPEN_NONE); + + ioctlv vec[4] __attribute__((align(32))); + // input vector + vec[0].data = input; + vec[0].len = 0x10; + vec[1].data = key; + vec[1].len = 0x10; + // output vector + vec[2].data = output; + vec[2].len = 0x10; + vec[3].data = iv; + vec[3].len = 0x10; + // decrypt + IOS_Ioctlv(aesFd, 0x3, 2, 2, vec); +} + +uint64_t OSGetTime(); +uint64_t timeSpentInAES = 0; +uint64_t timeSpentInSD = 0; +uint64_t timeSpentInSeek = 0; +void RB3E_CNTFileReadBlock(RB3E_CNTFileSD *file, int blockIndex) +{ + // if we're trying to read the same block we're on, don't do anything + if (blockIndex == file->lastBlockIndex) + return; + + uint64_t time = 0; + if (file->aesCtx == NULL) // unencrypted reads + { + if (blockIndex == 0) + { + SD_seek(file->fd, file->startOffset, SEEK_SET); + } + else if (blockIndex != file->lastBlockIndex + 1) + { + RB3E_DEBUG("WARNING! CNTFileReadBlock had to seek back, this is gonna hurt", NULL); + SD_seek(file->fd, file->startOffset + (blockIndex * 0x10), SEEK_SET); + } + SD_read(file->fd, file->lastBlock, 0x10); + file->lastBlockIndex = blockIndex; + } + else // encrypted reads + { + if (blockIndex == 0) + { + time = OSGetTime(); + // block index 0 uses the IV derived from the content index + memset(file->aesCtx->Iv, 0, 0x10); + *(uint16_t *)file->aesCtx->Iv = file->contentIndex; + SD_seek(file->fd, file->startOffset, SEEK_SET); + timeSpentInSeek += OSGetTime() - time; + } + else if (blockIndex == file->lastBlockIndex + 1) + { + time = OSGetTime(); + // if we're reading in the next block, we don't need to seek backwards + //memcpy(file->aesCtx->Iv, file->lastBlockEnc, 0x10); + timeSpentInSeek += OSGetTime() - time; + } + else + { + time = OSGetTime(); + RB3E_DEBUG("WARNING! CNTFileReadBlock had to seek back, this is gonna hurt", NULL); + // all other blocks use the IV with the previous block's ciphertext + SD_seek(file->fd, file->startOffset + ((blockIndex - 1) * 0x10), SEEK_SET); + SD_read(file->fd, file->aesCtx->Iv, 0x10); + timeSpentInSeek += OSGetTime() - time; + } + time = OSGetTime(); + // read and decrypt the current block + SD_read(file->fd, file->lastBlock, 0x10); + timeSpentInSD += OSGetTime() - time; + + time = OSGetTime(); + //memcpy(file->lastBlockEnc, file->lastBlock, 0x10); // keep a copy of the encrypted last block to use as the next IV + //memcpy(file->lastBlock, file->lastBlockEnc, 0x10); + AES_CBC_decrypt_buffer(file->aesCtx, file->lastBlock, 0x10); + timeSpentInAES += OSGetTime() - time; + file->lastBlockIndex = blockIndex; + } +} + +void RB3E_CNTFileRead(RB3E_CNTFileSD *file, int offset, uint8_t *buffer, int length) +{ + int blockOffset = offset & 0xF; + int curBlock = (offset & ~0xF) >> 4; + int readBytes = 0; + uint64_t timeStart = OSGetTime(); + timeSpentInAES = 0; + timeSpentInSD = 0; + timeSpentInSeek = 0; + while (readBytes < length) + { + int toRead = 0x10 - blockOffset; + if (length - readBytes < 0x10) + toRead -= (toRead - (length - readBytes)); + if (toRead < 1 || toRead > 0x10) + { + RB3E_MSG("TRIED TO READ 0x%x BYTE BLOCK!", toRead); + break; + } + RB3E_CNTFileReadBlock(file, curBlock); + memcpy(buffer + readBytes, file->lastBlock + blockOffset, toRead); + //RB3E_DEBUG("Wrote 0x%x to %p", toRead, buffer + readBytes); + curBlock++; + blockOffset = 0; + readBytes += toRead; + } + RB3E_DEBUG("%p:0x%x:0x%x - %lli tb", buffer, offset, length, OSGetTime() - timeStart); + RB3E_DEBUG("seek %lli sd %lli aes %lli", timeSpentInSeek, timeSpentInSD, timeSpentInAES); +} + +void RB3E_CloseCNTFileSD(RB3E_CNTFileSD *file) +{ + if (file->fd != -1) + { + RB3E_CloseFile(file->fd); + file->fd = -1; + } + if (file->aesCtx != NULL) + { + MemFree(file->aesCtx); + file->aesCtx = NULL; + } + if (file->arcHeader != NULL) + { + MemFree(file->arcHeader); + file->arcHeader = NULL; + } + MemFree(file); +} + +static inline int alignoffset(int off, int align) +{ + return ((off + align - 1) & ~(align - 1)); +} + +static inline int getFlippedContent(uint8_t *contentFlags) +{ + int index = 0, i = 0, j = 0; + for (i = 0; i < 0x40; i++) + { + for (j = 0; j < 8; j++) + { + unsigned char mask = (1 << j); + if (contentFlags[i] == mask) goto found; + index++; + } + } +found: + return index; +} + +RB3E_CNTFileSD *RB3E_OpenCNTFileSD(const char *filepath, unsigned long long titleid, unsigned int index) +{ + // open a handle to the file + int fd = RB3E_OpenFile(filepath, 0); + if (fd == -1) + { + RB3E_MSG("Failed to open CNT archive '%s'", filepath); + return NULL; + } + // allocate a buffer for our file metadata + RB3E_CNTFileSD *file = (RB3E_CNTFileSD *)MemAlloc(sizeof(RB3E_CNTFileSD), 0); + if (file == NULL) + { + RB3E_MSG("Could not allocate enough memory for CNTSD file", NULL); + RB3E_CloseFile(fd); + return NULL; + } + memset(file, 0, sizeof(RB3E_CNTFileSD)); + file->fd = fd; + file->lastBlockIndex = -1; + // read the header of the file + uint32_t magic[2]; + RB3E_ReadFile(fd, 0, magic, 8); + if (magic[1] == BACKUP_WAD_MAGIC) + { + // we're an encrypted file, allocate an AES context + file->aesCtx = MemAlloc(sizeof(struct AES_ctx), 0); + if (file->aesCtx == NULL) + { + RB3E_MSG("Failed to allocate space for the AES context", NULL); + RB3E_CloseCNTFileSD(file); + return NULL; + } + // read the WAD header and get the current content index from it + backup_wad_header_t wad; + RB3E_ReadFile(fd, 0, &wad, sizeof(backup_wad_header_t)); + int index = getFlippedContent(wad.included_contents); + RB3E_MSG("Host %.4s, TMD size 0x%x, Content size 0x%x", wad.title_id.name.code, wad.content_tmd_size, wad.content_data_size); + if (index == 0 || index > 511) + { + RB3E_MSG("! Invalid index %i!", index); + RB3E_CloseCNTFileSD(file); + return NULL; + } + RB3E_DEBUG("Content index %i", index); + file->contentIndex = index; + // read the TMD to get the true content length + tmd_header_t tmd; + RB3E_ReadFile(fd, sizeof(backup_wad_header_t), &tmd, sizeof(tmd_header_t)); + if (tmd.num_contents < index) + { + RB3E_MSG("Invalid number of contents!", NULL); + RB3E_CloseCNTFileSD(file); + return NULL; + } + // i don't know if this is *always* true but i'm assuming it is true that content indices are always sequential + tmd_content_t tmd_cnt; + RB3E_ReadFile(fd, sizeof(backup_wad_header_t) + sizeof(tmd_header_t) + (index * sizeof(tmd_content_t)), &tmd_cnt, sizeof(tmd_content_t)); + if (tmd_cnt.index != index) + { + RB3E_MSG("Wrong TMD content index (tmd:%i != flipped:%i)", tmd_cnt.index, index); + RB3E_CloseCNTFileSD(file); + return NULL; + } + RB3E_MSG("%.4s/%03i - content: %08x, size: 0x%llx", tmd.title_id.name.code, index, tmd_cnt.id, tmd_cnt.size); + file->titleId = *(uint32_t *)tmd.title_id.name.code; + file->contentLength = tmd_cnt.size; + // get the start of the encrypted data blob + int startOfData = alignoffset(sizeof(backup_wad_header_t) + wad.content_tmd_size, 0x40); + RB3E_DEBUG("Content starts at 0x%x", startOfData); + file->startOffset = startOfData; + // try to decrypt it with the NULL PRNG key (sZA-sZG) + RB3E_DEBUG("Attempting to read with NULLed key", NULL); + PRINT_BLOCK("Key", nullKey); + AES_init_ctx(file->aesCtx, nullKey); + memcpy(file->aesKey, nullKey, 0x10); + RB3E_CNTFileReadBlock(file, 0); + if (*(uint32_t *)file->lastBlock != ARC_MAGIC) + { + // that failed - try to decrypt it with the console-specific PRNG key (sZH+) + RB3E_DEBUG("Trying console specific PRNG key", NULL); + PRINT_BLOCK("Key", RB3E_ConsolePRNGKey); + AES_init_ctx(file->aesCtx, RB3E_ConsolePRNGKey); + memcpy(file->aesKey, RB3E_ConsolePRNGKey, 0x10); + file->lastBlockIndex = -1; + RB3E_CNTFileReadBlock(file, 0); + if (*(uint32_t *)file->lastBlock != ARC_MAGIC) + { + // couldn't decrypt + RB3E_MSG("Failed to decrypt the BIN file", NULL); + PRINT_BLOCK("Last Block", file->lastBlock); + RB3E_CloseCNTFileSD(file); + return NULL; + } + } + RB3E_MSG("Opened BIN successfully!", NULL); + // read the ARC file header into RAM + u8_header_t archeader; + RB3E_CNTFileRead(file, 0, (uint8_t *)&archeader, sizeof(u8_header_t)); + // allocate the size of the header - data_offset doubles as the length of the full "header" + void *fullarcheader = MemAlloc(archeader.data_offset, 0); + if (fullarcheader == NULL) + { + RB3E_MSG("Failed to allocate space for the header", NULL); + RB3E_CloseCNTFileSD(file); + return NULL; + } + // read the header, including node and string tables + RB3E_CNTFileRead(file, 0, fullarcheader, archeader.data_offset); + file->arcHeader = fullarcheader; + RB3E_DEBUG("Created file %p", file); + return file; + } + else if (magic[0] == ARC_MAGIC) + { + // untested, just try to handle the ARC file directly + file->aesCtx = NULL; + file->contentIndex = index; + file->contentLength = RB3E_FileSize(file->fd); + file->startOffset = 0; + file->titleId = (uint32_t)titleid; + // read the ARC file header into RAM + u8_header_t archeader; + RB3E_CNTFileRead(file, 0, (uint8_t *)&archeader, sizeof(u8_header_t)); + // allocate the size of the header - data_offset doubles as the length of the full "header" + void *fullarcheader = MemAlloc(archeader.data_offset, 0); + if (fullarcheader == NULL) { + RB3E_MSG("Failed to allocate space for the header", NULL); + RB3E_CloseCNTFileSD(file); + return NULL; + } + // read the header, including node and string tables + RB3E_CNTFileRead(file, 0, fullarcheader, archeader.data_offset); + file->arcHeader = fullarcheader; + return file; + } + else + { + RB3E_MSG("'%s' was not valid CNT file.", filepath); + RB3E_CloseCNTFileSD(file); + return NULL; + } +} + +void ParseKeysFile(uint8_t *keys_data, size_t keys_len) +{ + RB3E_DEBUG("0x%x bytes key file", keys_len); + // check if its a BootMii-style keys.bin + if (keys_len >= 0x400 && *(uint32_t *)&keys_data[0x114] == 0xEBE42A22) + { + memcpy(RB3E_ConsolePRNGKey, keys_data + 0x168, 0x10); + } + // check if its an OTP file dump + else if (keys_len >= 0x100 && *(uint32_t *)&keys_data[0x14] == 0xEBE42A22) + { + memcpy(RB3E_ConsolePRNGKey, keys_data + 0x68, 0x10); + } + // check if its a straight up key + else if (keys_len == 0x10) + { + memcpy(RB3E_ConsolePRNGKey, keys_data, 0x10); + } + else + { + RB3E_DEBUG("FAILED TO LOAD KEY!", NULL); + return; + } + PRINT_BLOCK("Loaded Key", RB3E_ConsolePRNGKey); +} + +void TryToLoadPRNGKeyFromFile() +{ + uint8_t keys_buffer[0x400]; // size of keys.bin file + + // check if the loader already loaded a prng key + if (memcmp(RB3E_ConsolePRNGKey, nullKey, 0x10) != 0) + { + RB3E_DEBUG("PRNG key loaded, not scanning for file", NULL); + return; + } + + // DOLPHIN ONLY: try to load keys.bin from the NAND + if (RB3E_IsEmulator()) + { + ios_fd_t nandfd = IOS_Open("/keys.bin", IPC_OPEN_READ); + if (nandfd >= 0) + { + RB3E_DEBUG("Loading keys from NAND", NULL); + ios_ret_t readret = IOS_Read(nandfd, keys_buffer, sizeof(keys_buffer)); + IOS_Close(nandfd); + if (readret >= IPC_OK) + { + ParseKeysFile(keys_buffer, readret); + } + return; + } + } + + // try a few files on SD + // PRNG key as binary + if (RB3E_FileExists("sd:/prng_key.bin")) + { + int keysfd = RB3E_OpenFile("sd:/prng_key.bin", 0); + if (keysfd != -1) + { + RB3E_DEBUG("Loading keys from prng_key.bin", NULL); + int r = RB3E_ReadFile(keysfd, 0, keys_buffer, 0x10); + RB3E_CloseFile(keysfd); + ParseKeysFile(keys_buffer, r == 0x10 ? 0x10 : 0); + return; + } + } + // vWii/xyzzy OTP backup + if (RB3E_FileExists("sd:/otp.bin")) + { + int otpfd = RB3E_OpenFile("sd:/otp.bin", 0); + if (otpfd != -1) + { + RB3E_DEBUG("Loading keys from otp.bin", NULL); + int r = RB3E_ReadFile(otpfd, 0, keys_buffer, sizeof(keys_buffer)); + RB3E_CloseFile(otpfd); + ParseKeysFile(keys_buffer, r); + return; + } + } + // BootMii/xyzzy keys.bin backup + if (RB3E_FileExists("sd:/keys.bin")) + { + int keysfd = RB3E_OpenFile("sd:/keys.bin", 0); + if (keysfd != -1) + { + RB3E_DEBUG("Loading keys from keys.bin", NULL); + int r = RB3E_ReadFile(keysfd, 0, keys_buffer, sizeof(keys_buffer)); + RB3E_CloseFile(keysfd); + ParseKeysFile(keys_buffer, r); + return; + } + } +} + +#endif // RB3E_WII diff --git a/source/wii_cnt_hooks.c b/source/wii_cnt_hooks.c new file mode 100644 index 0000000..cf39ecf --- /dev/null +++ b/source/wii_cnt_hooks.c @@ -0,0 +1,173 @@ +/* + RB3Enhanced - wii_cnt_hooks.c + Hooks to CNT and EC functions to allow for custom content loading. +*/ + +#ifdef RB3E_WII + +#include +#include +#include +#include +#include "rvl/cnt.h" +#include "rvl/ec.h" +#include "rb3enhanced.h" +#include "utilities.h" +#include "ports.h" +#include "ppcasm.h" +#include "wii_cnt_crypt.h" + +int contentInitHandleTitleNAND_hook(unsigned long long title_id, unsigned int content_index, cnt_handle *handle, MEMAllocator *allocator) +{ + // make a file path of the SD DLC file + char titleid[8]; + char sdfilepath[256]; + memcpy(titleid, &title_id, 8); + sprintf(sdfilepath, "sd:/private/wii/data/%.4s/%03i.bin", &titleid[4], content_index); + // if the file exists then we're going to be opening that + if (RB3E_FileExists(sdfilepath)) + { + // open the bin file on the SD + RB3E_CNTFileSD *cntfilesd = RB3E_OpenCNTFileSD(sdfilepath, title_id, content_index); + if (cntfilesd == NULL) { + return -1; // TODO: what's the proper CNT error for "file not found"? + } + // open an ARC handle + arc_handle_t archandle; + ARCInitHandle(cntfilesd->arcHeader, &archandle); + // fill our output struct + memcpy(&handle->nand.ArcHandle, &archandle, sizeof(arc_handle_t)); + handle->nand.FileDescriptor = -0xBEEF; // dude is hungry + handle->nand.allocator = allocator; + handle->type = cnthandle_nand; + // sneak the handle to our RB3E_CNTFileSD in there + handle->nand.ArcHandle.header->reserved[0] = (unsigned int)cntfilesd; + // return success + return 0; + } + // fall back to reading from NAND + return contentInitHandleTitleNAND(title_id, content_index, handle, allocator); +} + +int CNTReleaseHandle_hook(cnt_handle *handle) +{ + // check if the file is reading from NAND and has our fake beef fd + if (handle->type == cnthandle_nand && handle->nand.FileDescriptor == -0xBEEF) + { + // get out the handle to our RB3E_CNTFileSD + RB3E_CNTFileSD *cntfilesd = (RB3E_CNTFileSD *)handle->nand.ArcHandle.header->reserved[0]; + RB3E_DEBUG("Freeing %p", cntfilesd); + RB3E_CloseCNTFileSD(cntfilesd); + // we don't call the original release function as we manage our own memory + return 0; + } + // fall back to the original release function + return CNTReleaseHandle(handle); +} + +int CNTRead_hook(cnt_file_info *file_info, void *buffer, int size) +{ + // check if the file is reading from NAND and has our fake beef fd + if (file_info->type == cnthandle_nand && file_info->nand.CntHandle->FileDescriptor == -0xBEEF) + { + //RB3E_DEBUG("File read %p 0x%x", buffer, size); + RB3E_CNTFileSD *cntfilesd = (RB3E_CNTFileSD *)file_info->nand.CntHandle->ArcHandle.header->reserved[0]; + RB3E_CNTFileRead(cntfilesd, file_info->nand.startoffset + file_info->nand.readOffset, buffer, size); + file_info->nand.readOffset += size; + return size; + } + // fall back to the original read function + return CNTRead(file_info, buffer, size); +} + +int EC_GetContentInfos_hook(unsigned long long titleId, ec_content_info_t *contentInfos, int *numContentInfos) +{ + // make a file path of the SD DLC folder + char titleid[8]; + char sddirpath[256]; + memcpy(titleid, &titleId, 8); + sprintf(sddirpath, "sd:/private/wii/data/%.4s/", &titleid[4]); + if (numContentInfos != NULL) + { + int originalNumContentInfos = *numContentInfos; + int r = 0; + DIR_STATE_STRUCT ds; + if (SD_diropen(&ds, sddirpath) != NULL) + { + RB3E_DEBUG("Enumerating %s", sddirpath); + // call the original so information about files on the NAND are populated + EC_GetContentInfos(titleId, contentInfos, numContentInfos); + *numContentInfos = originalNumContentInfos; + // we *do* have a directory, check if we need to fill the contentInfos buffer + if (contentInfos != NULL) + { + char filename[MAX_FILENAME_LENGTH]; + struct stat fs; + while (true) + { + if (SD_dirnext(&ds, filename, &fs) == 0) + { + // hacky check to see if the filename is some form of "000.BIN" or "000.APP" + if ((filename[0] >= '0' && filename[0] <= '9') && + (filename[1] >= '0' && filename[1] <= '9') && + (filename[2] >= '0' && filename[2] <= '9') && + (filename[3] == '.') && + (((filename[4] & ~0x20) == 'B' && (filename[5] & ~0x20) == 'I' && (filename[6] & ~0x20) == 'N') || + ((filename[4] & ~0x20) == 'A' && (filename[5] & ~0x20) == 'P' && (filename[6] & ~0x20) == 'P'))) + { + // make a content index out of the filename + int contentIndex = + ((filename[0] - '0') * 100) + ((filename[1] - '0') * 10) + (filename[2] - '0'); + if (contentIndex < originalNumContentInfos) + { + // mark that DLC as installed + contentInfos[contentIndex].flags = 3; + contentInfos[contentIndex].index = contentIndex; + contentInfos[contentIndex].type = 0x4001; // DLC + // the filesize of the DLC is checked to determine if something is a song or not + contentInfos[contentIndex].size = fs.st_size; + } + else + { + RB3E_DEBUG("%i is out of range of %i", contentIndex, originalNumContentInfos); + } + } + } + else break; + } + return r; + } + else + { + *numContentInfos = 512; // act like we have every possible content index + RB3E_DEBUG("Returning %i", *numContentInfos); + SD_dirclose(&ds); + } + return r; + } + else + { + RB3E_DEBUG("No SD DLC for %.4s", &titleid[4]); + // call the original function, we don't have our own directory + return EC_GetContentInfos(titleId, contentInfos, numContentInfos); + } + return r; + } + else + { + return EC_GetContentInfos(titleId, contentInfos, numContentInfos); + } +} + +void InitCNTHooks() +{ + POKE_32(PORT_SDMODECHECK, LI(3, 3)); + POKE_32(PORT_SDMODECHECK + 4, BLR); + POKE_B(&ARCInitHandle, PORT_ARCINITHANDLE); + HookFunction(PORT_CONTENTINITHANDLETITLENAND, contentInitHandleTitleNAND, contentInitHandleTitleNAND_hook); + HookFunction(PORT_CNTRELEASEHANDLE, CNTReleaseHandle, CNTReleaseHandle_hook); + HookFunction(PORT_CNTREAD, CNTRead, CNTRead_hook); + HookFunction(PORT_ECGETCONTENTINFOS, EC_GetContentInfos, EC_GetContentInfos_hook); +} + +#endif // RB3E_WII diff --git a/source/wii_exceptions.c b/source/wii_exceptions.c new file mode 100644 index 0000000..2757396 --- /dev/null +++ b/source/wii_exceptions.c @@ -0,0 +1,615 @@ +/* + RB3Enhanced - wii_exceptions.c + Low-level exception handling for RB3Enhanced on Wii. +*/ +#ifdef RB3E_WII + +#include +#include +#include +#include +#include +#include +#include +#include "exceptions.h" +#include "version.h" +#include "ppcasm.h" +#include "ports.h" + +// Font for OSFatal as used by Dolphin Emulator +static unsigned char dolphin_osfatal_font[] = { + 0x59, 0x61, 0x79, 0x30, 0x00, 0x01, 0x01, 0x10, 0x00, 0x00, 0x01, 0xF3, 0x00, 0x00, 0x0D, 0x71, + 0xFD, 0x77, 0x7E, 0xFB, 0xFF, 0xFF, 0xDB, 0xFF, 0xFF, 0xFE, 0xBF, 0xFF, 0xBF, 0xFF, 0xFD, 0xF7, + 0xFF, 0x4F, 0xFF, 0xFF, 0xFA, 0xBE, 0xC7, 0x53, 0xE4, 0x9F, 0x73, 0xEE, 0xF7, 0xDF, 0xA8, 0x7D, + 0x4C, 0x23, 0xF4, 0xFB, 0xE7, 0x79, 0xFA, 0x4F, 0x7F, 0xFF, 0xFE, 0x9F, 0xFE, 0x8B, 0xFF, 0xBF, + 0xE7, 0xFF, 0xF3, 0xF0, 0xFB, 0xA7, 0xFC, 0x89, 0xDC, 0xFF, 0x9E, 0xF1, 0x2A, 0xE3, 0xE5, 0xFF, + 0xFC, 0xEE, 0x7C, 0x7C, 0x97, 0xEF, 0xBE, 0x5F, 0x5C, 0x51, 0x4F, 0x8F, 0xAE, 0x07, 0xA7, 0x3F, + 0x3E, 0x30, 0xBF, 0x77, 0xE9, 0xE3, 0xDF, 0x24, 0xC8, 0x73, 0xF0, 0xBF, 0x22, 0x4D, 0xD8, 0x1F, + 0xA0, 0xFF, 0xFC, 0x68, 0x8F, 0x0F, 0x3F, 0xEF, 0xFE, 0xF7, 0xF6, 0x91, 0xD0, 0xA3, 0xA0, 0x1D, + 0xC4, 0x1D, 0x60, 0x8F, 0x08, 0x6E, 0x9A, 0x4F, 0x3F, 0x5A, 0x07, 0xA1, 0x81, 0xF7, 0xA0, 0x8E, + 0x07, 0x8F, 0x09, 0xF7, 0xEF, 0x8C, 0x23, 0xDC, 0x29, 0xFF, 0xE8, 0x7F, 0xFF, 0xFF, 0xA3, 0xFF, + 0xBE, 0x8D, 0x0F, 0x07, 0x07, 0xD0, 0x17, 0xFF, 0x8F, 0xFF, 0xFB, 0xED, 0xFF, 0xFE, 0x6F, 0x85, + 0x8B, 0xF3, 0xFE, 0x27, 0x28, 0x87, 0x95, 0xBC, 0x74, 0x34, 0x23, 0x12, 0x34, 0x02, 0x4A, 0x0E, + 0x01, 0xEF, 0xE1, 0xF5, 0x5F, 0x1F, 0xE3, 0xFF, 0xE3, 0xDF, 0xFF, 0xC5, 0x0B, 0xFF, 0x95, 0xFC, + 0x1F, 0x90, 0x6B, 0xF3, 0xBF, 0x7B, 0xF3, 0xD7, 0xEF, 0x90, 0x7D, 0xE0, 0x13, 0x00, 0xC3, 0x07, + 0x39, 0xE0, 0x75, 0x29, 0x7C, 0xF0, 0xDF, 0xCF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF7, 0x97, 0x1D, 0xFD, + 0xBF, 0x87, 0xF9, 0xF4, 0xFF, 0xFC, 0x7A, 0x70, 0x5C, 0x9E, 0x7D, 0x9F, 0x67, 0xD7, 0xFF, 0xE7, + 0x00, 0xFC, 0x4E, 0x7F, 0xBC, 0x91, 0xEF, 0xBE, 0xF4, 0xF7, 0x8F, 0x1D, 0xDE, 0x67, 0xD7, 0xDF, + 0x7C, 0xDF, 0xFD, 0xF9, 0xD5, 0xFF, 0xE7, 0xAE, 0xA0, 0xA4, 0xAE, 0xEF, 0x58, 0x49, 0x3F, 0xBD, + 0xF9, 0x67, 0xFB, 0xFF, 0x71, 0xEF, 0xBE, 0xF4, 0xDF, 0x79, 0x83, 0xDD, 0xFB, 0xE3, 0xBE, 0x81, + 0xEA, 0x05, 0x79, 0xDF, 0x78, 0xF5, 0xF2, 0xE7, 0x6D, 0xBE, 0x29, 0xD3, 0x9B, 0xCF, 0xFF, 0xF8, + 0x5F, 0xFF, 0x0F, 0xFF, 0xCF, 0xF7, 0xFE, 0xFE, 0xFD, 0xFF, 0xD7, 0x0B, 0xFF, 0xFF, 0xFF, 0xFB, + 0xF9, 0xEE, 0xFF, 0xCC, 0x04, 0xE8, 0x03, 0x9F, 0xBB, 0xD5, 0x98, 0xE8, 0xEB, 0xBE, 0xF9, 0xC8, + 0xFB, 0x58, 0x1F, 0x2A, 0x83, 0xE0, 0xF7, 0xBE, 0xF7, 0x10, 0xFF, 0x84, 0x9F, 0xBF, 0xF8, 0xFE, + 0xDF, 0xFE, 0x7F, 0xF7, 0x27, 0xD7, 0xEF, 0x2F, 0xEF, 0x2E, 0x05, 0xFF, 0x1C, 0x9F, 0x90, 0xAB, + 0x93, 0x9C, 0xF2, 0x4F, 0xA4, 0xBF, 0x7D, 0x62, 0x72, 0x3C, 0xE4, 0xE4, 0xDF, 0x38, 0xD1, 0xC2, + 0x60, 0x24, 0x43, 0x4A, 0x00, 0x3E, 0x80, 0x43, 0xC0, 0x01, 0xE9, 0x1F, 0xFF, 0xDF, 0x17, 0x50, + 0x7F, 0xFF, 0x23, 0x7F, 0xFF, 0x80, 0xF0, 0xCF, 0x47, 0x38, 0x9F, 0xCD, 0x0B, 0xE4, 0xDE, 0x4F, + 0x8E, 0x7C, 0x1E, 0x00, 0x27, 0x7F, 0xD0, 0xFF, 0x8C, 0x7C, 0x6A, 0x27, 0xBF, 0xC3, 0x27, 0xAF, + 0xFF, 0xBE, 0x3F, 0xFE, 0x9A, 0x07, 0xEE, 0x3F, 0x40, 0xF5, 0x7C, 0x7B, 0xF7, 0xFB, 0xD0, 0x3C, + 0x85, 0x06, 0x33, 0x80, 0xF8, 0x01, 0xBC, 0xAF, 0xFE, 0x0F, 0xFF, 0xF2, 0x20, 0x8E, 0x8B, 0xBE, + 0x3F, 0xFF, 0x3F, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x10, 0x03, 0x10, 0x09, 0x10, 0x03, 0x10, 0x0B, 0x20, 0x0A, 0x30, 0x13, 0x70, + 0x00, 0x10, 0x04, 0x10, 0x1B, 0x10, 0x12, 0x10, 0x0D, 0x10, 0x0D, 0x10, 0x0D, 0x10, 0x0F, 0x40, + 0x65, 0x10, 0x67, 0x30, 0x00, 0x10, 0x74, 0x10, 0x19, 0x20, 0x00, 0x20, 0x16, 0x10, 0x6D, 0x40, + 0xAB, 0x10, 0x00, 0x10, 0x00, 0x50, 0x11, 0x10, 0x1A, 0x30, 0xF9, 0x00, 0x00, 0x30, 0x01, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x80, 0x0E, 0x10, 0x01, 0x00, 0x2D, 0xA0, 0x12, 0x00, 0x5E, 0xA0, + 0x8F, 0x00, 0x2D, 0x80, 0x1F, 0x10, 0x5B, 0x10, 0x01, 0x00, 0xEF, 0x30, 0x01, 0x00, 0x00, 0x00, + 0x2E, 0x10, 0x7D, 0x00, 0x5D, 0x20, 0x01, 0xC0, 0x14, 0x01, 0x1F, 0x51, 0x55, 0x00, 0x00, 0x11, + 0x1E, 0x01, 0xA0, 0xD0, 0x00, 0xA0, 0x11, 0x11, 0x63, 0x00, 0x00, 0x00, 0x5F, 0x13, 0xE0, 0x80, + 0x00, 0x00, 0x2F, 0xC0, 0x8F, 0x00, 0x2F, 0x80, 0x0E, 0x30, 0x01, 0x00, 0x00, 0x52, 0xD7, 0x02, + 0xDB, 0x00, 0x60, 0x12, 0xA9, 0x15, 0xB6, 0x00, 0xCF, 0x14, 0x1E, 0x80, 0xFD, 0x32, 0xF0, 0x00, + 0x8F, 0x20, 0x01, 0x10, 0xC7, 0x10, 0x01, 0xE0, 0x2F, 0x10, 0x51, 0x40, 0x83, 0xB4, 0x6E, 0x40, + 0x5F, 0x14, 0xA7, 0x00, 0x00, 0x01, 0x53, 0x11, 0xE6, 0x70, 0x01, 0x00, 0x00, 0x55, 0xE9, 0x43, + 0xF1, 0x01, 0xB0, 0x20, 0x01, 0x11, 0x83, 0x40, 0x09, 0x40, 0x14, 0x02, 0x49, 0x02, 0x9E, 0x12, + 0x3F, 0x31, 0x57, 0x00, 0x00, 0x61, 0x7B, 0x45, 0xD5, 0x80, 0x01, 0xE0, 0x00, 0x81, 0xAA, 0x01, + 0xB0, 0x10, 0x01, 0x11, 0xA8, 0x14, 0x2F, 0x43, 0x95, 0x01, 0x7F, 0x20, 0x2D, 0x48, 0x95, 0x60, + 0x2D, 0x06, 0x6F, 0x40, 0x5F, 0x40, 0x01, 0x01, 0x5E, 0x07, 0xF5, 0x00, 0x00, 0x94, 0xF5, 0x20, + 0xFD, 0x01, 0x2D, 0x12, 0xA5, 0x76, 0xA7, 0x00, 0x2D, 0x33, 0x40, 0xA1, 0x46, 0x00, 0x2E, 0x93, + 0xE6, 0x07, 0x36, 0x05, 0x4F, 0x12, 0x1C, 0x06, 0xAB, 0x12, 0x48, 0x10, 0xC2, 0x06, 0x9B, 0x00, + 0x00, 0x07, 0x66, 0x31, 0x52, 0x07, 0x6A, 0x00, 0x00, 0x09, 0x45, 0xC1, 0xDE, 0x01, 0x4F, 0xA9, + 0x55, 0x04, 0x94, 0x09, 0xD6, 0xB6, 0x26, 0x09, 0x75, 0x12, 0xD1, 0x99, 0xB6, 0x01, 0xE7, 0x24, + 0x5F, 0x98, 0xB5, 0x07, 0xF5, 0xA0, 0xD8, 0x14, 0x73, 0x06, 0x5A, 0x90, 0x22, 0x00, 0x77, 0xCA, + 0x90, 0x01, 0x09, 0x4D, 0x99, 0x70, 0x61, 0x00, 0x2F, 0x09, 0xBF, 0x00, 0x00, 0x1E, 0x5A, 0x00, + 0x00, 0x00, 0x2B, 0x05, 0x8A, 0x91, 0x70, 0x03, 0x19, 0x19, 0xA2, 0x07, 0xFF, 0x92, 0x0F, 0x4D, + 0x51, 0x00, 0x00, 0x92, 0x62, 0x00, 0x5F, 0xBB, 0x6D, 0x07, 0x0F, 0x10, 0x2F, 0x02, 0xFF, 0x00, + 0x2F, 0x1C, 0x61, 0x90, 0xBF, 0x16, 0x4B, 0x00, 0xBF, 0xE4, 0x68, 0x04, 0x78, 0x18, 0xF3, 0x05, + 0xBD, 0x14, 0x43, 0x91, 0x8B, 0x2F, 0x39, 0x08, 0xFF, 0x18, 0x01, 0x2C, 0x09, 0x48, 0x09, 0x3E, + 0x8B, 0x00, 0x2F, 0x34, 0x9D, 0x38, 0xFF, 0x26, 0x28, 0x25, 0x5F, 0x07, 0xA1, 0x15, 0x91, 0x1D, + 0x8B, 0x19, 0x99, 0x24, 0xFF, 0x21, 0x34, 0x23, 0xCB, 0x00, 0x5F, 0x49, 0xBD, 0x10, 0xC1, 0xC9, + 0xBD, 0x03, 0xFF, 0x06, 0x4F, 0x00, 0x2F, 0x10, 0xED, 0x90, 0xED, 0x22, 0x81, 0x02, 0x47, 0xA0, + 0x07, 0x5B, 0x6B, 0x60, 0x07, 0xD0, 0x2D, 0x10, 0x52, 0x10, 0x57, 0x10, 0x01, 0x50, 0x5C, 0x20, + 0xEB, 0x02, 0x7D, 0x2C, 0x24, 0x1B, 0x15, 0x03, 0xF4, 0x20, 0x01, 0x10, 0x00, 0xF0, 0x30, 0x10, + 0x66, 0x13, 0x0F, 0x3E, 0xA5, 0x22, 0x3D, 0x14, 0x2C, 0x01, 0x39, 0x11, 0x4D, 0x33, 0x9B, 0x20, + 0x0B, 0x41, 0xA5, 0x2F, 0x09, 0x02, 0x6F, 0x5E, 0x73, 0x29, 0x8B, 0x02, 0xCF, 0xA0, 0x01, 0x5D, + 0x29, 0x10, 0x09, 0x0A, 0xB9, 0x40, 0x25, 0x43, 0xCB, 0x81, 0xE9, 0x03, 0xCB, 0x00, 0x2F, 0x70, + 0xBF, 0xA0, 0xBF, 0x0D, 0xBF, 0xE0, 0x5F, 0x40, 0x01, 0x14, 0xEB, 0x03, 0xFF, 0x8E, 0x09, 0x0E, + 0x0F, 0x02, 0xAF, 0x98, 0xFF, 0x07, 0x33, 0x19, 0x2F, 0xB8, 0x9F, 0x03, 0xCF, 0x4D, 0x8F, 0x0B, + 0x0F, 0xCB, 0x6F, 0x42, 0x1D, 0x00, 0x00, 0x9B, 0xBF, 0x00, 0x8D, 0x03, 0xCF, 0x0A, 0xDF, 0x07, + 0xF1, 0x7C, 0xE8, 0x00, 0x00, 0x11, 0x7D, 0x09, 0x06, 0x2C, 0x8D, 0x14, 0x63, 0x30, 0x00, 0x36, + 0xBA, 0x92, 0x22, 0x0B, 0x6F, 0x12, 0xE3, 0x02, 0x85, 0xAB, 0x0F, 0x01, 0xDF, 0xA0, 0x11, 0x07, + 0xC5, 0xC0, 0x5F, 0x10, 0x61, 0x00, 0x5F, 0x16, 0x3B, 0xDB, 0x9F, 0x24, 0xBF, 0x0B, 0xFE, 0xB0, + 0xBF, 0x13, 0xF7, 0x01, 0x4B, 0x07, 0xF5, 0x14, 0xD7, 0x0D, 0x7A, 0x04, 0x3F, 0x30, 0x99, 0x9B, + 0x6F, 0x0E, 0x93, 0x01, 0x39, 0xA1, 0x8A, 0x01, 0xFA, 0x17, 0x1B, 0xA0, 0x2F, 0x00, 0xCF, 0x2C, + 0xBF, 0x90, 0x2F, 0x11, 0xA6, 0xA9, 0x9F, 0xF0, 0x8F, 0xA0, 0xEF, 0x29, 0x25, 0x01, 0x78, 0x00, + 0x5F, 0x17, 0x13, 0x00, 0x5F, 0xA6, 0xCF, 0x27, 0x79, 0x00, 0x00, 0xA0, 0x50, 0x08, 0x5F, 0x02, + 0x89, 0x27, 0xF1, 0xD0, 0x2F, 0x0C, 0x2E, 0x91, 0x4F, 0x0E, 0xC9, 0x90, 0x2F, 0x6A, 0x19, 0x00, + 0x8F, 0xA1, 0xAF, 0x2E, 0x6D, 0x02, 0x9F, 0x19, 0x7F, 0x1A, 0xA9, 0xB0, 0x7F, 0x01, 0x51, 0x02, + 0xCF, 0xB1, 0x1F, 0x00, 0x00, 0x48, 0x21, 0x00, 0x00, 0x13, 0x21, 0x03, 0xF5, 0x08, 0x3F, 0x10, + 0x0B, 0xA5, 0x5B, 0x29, 0xC5, 0x09, 0x8F, 0x03, 0xF5, 0x10, 0x0E, 0x10, 0x18, 0xF3, 0xF5, 0x20, + 0x2D, 0x80, 0xC9, 0x09, 0x8F, 0x5A, 0x17, 0x15, 0xF9, 0x10, 0x0D, 0x6C, 0x32, 0x10, 0x01, 0x07, + 0x29, 0x3C, 0x5F, 0x12, 0x47, 0x20, 0x37, 0x40, 0x2C, 0x02, 0x49, 0x00, 0x5F, 0x27, 0x13, 0x21, + 0x4F, 0x40, 0x2C, 0x41, 0x47, 0x06, 0x89, 0x3A, 0xD1, 0x10, 0x5C, 0x1C, 0x6D, 0x03, 0x2F, 0x72, + 0x76, 0xC0, 0x01, 0x0B, 0x3F, 0x4B, 0x79, 0x80, 0x01, 0x00, 0x8F, 0x41, 0x1D, 0x20, 0x89, 0x07, + 0x75, 0x10, 0x0F, 0x30, 0x0F, 0xE0, 0x35, 0xC0, 0x1F, 0x00, 0x63, 0x10, 0x31, 0x40, 0xEF, 0x0F, + 0x93, 0x4B, 0xA0, 0x1E, 0x97, 0x11, 0x9B, 0xD0, 0x5E, 0x0C, 0x5F, 0x00, 0x2F, 0x22, 0x8D, 0x06, + 0x30, 0x03, 0x8F, 0x10, 0xC2, 0x62, 0x07, 0x20, 0x66, 0x07, 0x6B, 0x60, 0x67, 0x70, 0x41, 0x00, + 0xCF, 0xC0, 0xF5, 0x10, 0x35, 0x02, 0x15, 0x09, 0x5F, 0x10, 0x33, 0x04, 0x5F, 0x13, 0xC7, 0x92, + 0xBE, 0x00, 0xCA, 0xE1, 0x85, 0x1A, 0xB8, 0x09, 0x8F, 0xA9, 0x8F, 0x9C, 0x3E, 0x09, 0xEF, 0x00, + 0x5F, 0x13, 0xB3, 0x04, 0x32, 0x3F, 0x05, 0xD7, 0x45, 0x01, 0x4F, 0x14, 0x2B, 0x0C, 0xEF, 0x03, + 0xF5, 0x80, 0x00, 0x1F, 0x2D, 0xAD, 0xDF, 0x23, 0x46, 0x03, 0x43, 0x1B, 0x62, 0x0B, 0x90, 0xE2, + 0xAF, 0x10, 0x01, 0x92, 0x00, 0x10, 0x0F, 0x15, 0x21, 0x02, 0x6F, 0xA0, 0x1E, 0x12, 0x3D, 0x00, + 0xCE, 0x00, 0xEF, 0x15, 0xAF, 0x0B, 0xFF, 0x12, 0xD5, 0xCA, 0x9B, 0x00, 0x8F, 0x02, 0x40, 0x63, + 0x8F, 0x12, 0xC9, 0x00, 0x00, 0x15, 0xF8, 0x00, 0x5E, 0x05, 0x5F, 0x00, 0x00, 0x07, 0x1E, 0x0F, + 0x77, 0x0D, 0x4F, 0x01, 0x1F, 0x01, 0x4B, 0x00, 0x2F, 0x00, 0x8F, 0x01, 0xDF, 0x00, 0x00, 0x00, + 0x00, 0x2A, 0xA7, 0x1D, 0xDF, 0x25, 0x5F, 0x80, 0x01, 0xE0, 0x00, 0x7A, 0x77, 0x10, 0x2D, 0x16, + 0x83, 0x0A, 0x7F, 0x29, 0xBE, 0x30, 0x01, 0x08, 0xC1, 0x50, 0x2F, 0x50, 0x07, 0x0B, 0x1E, 0x30, + 0x5F, 0x2F, 0x67, 0x0E, 0x1B, 0x09, 0xBF, 0x5E, 0xD5, 0x00, 0x5F, 0x31, 0x1F, 0x8B, 0x97, 0x37, + 0x0F, 0x00, 0xBF, 0x0A, 0xAF, 0x00, 0x2F, 0x19, 0xAF, 0x2B, 0xFB, 0x00, 0x5F, 0x20, 0xEF, 0x80, + 0xEF, 0x38, 0x46, 0x20, 0x01, 0xE2, 0x4F, 0x30, 0x2F, 0x01, 0x1F, 0x42, 0x07, 0x11, 0xDF, 0x20, + 0x4F, 0x2B, 0x3F, 0x00, 0x2F, 0x30, 0x5F, 0x02, 0x9F, 0xC2, 0x6F, 0x02, 0x6F, 0x80, 0xBF, 0x08, + 0xBF, 0x22, 0x97, 0x07, 0x91, 0x03, 0xFF, 0x0E, 0xA5, 0x8F, 0xFF, 0xDA, 0x7F, 0x00, 0x2F, 0x00, + 0x8F, 0x9A, 0xAF, 0x07, 0xF5, 0x20, 0x5F, 0xDA, 0x1F, 0x0B, 0x71, 0x03, 0xF5, 0x93, 0xCF, 0x34, + 0xBF, 0x10, 0x69, 0x22, 0x51, 0x40, 0x01, 0x0C, 0x30, 0xE2, 0x15, 0x00, 0xBF, 0x07, 0x65, 0xA3, + 0x9B, 0x09, 0x0F, 0x1D, 0xAF, 0x13, 0x35, 0x8F, 0x91, 0x0E, 0xE4, 0x00, 0x8F, 0x90, 0xEF, 0x53, + 0xF5, 0x02, 0x4F, 0x01, 0x1F, 0x25, 0x77, 0xAE, 0xCF, 0x04, 0xBF, 0x22, 0x9F, 0x81, 0x23, 0x02, + 0x9F, 0x02, 0x6F, 0x05, 0xAF, 0x0E, 0x70, 0x0E, 0xC0, 0x00, 0x00, 0x0A, 0xAF, 0x00, 0x00, 0x0E, + 0x23, 0x35, 0xF8, 0x50, 0x01, 0x00, 0x30, 0x1F, 0xAB, 0x00, 0xEF, 0xAF, 0xD9, 0x16, 0x95, 0x0A, + 0x7A, 0x11, 0x20, 0xA3, 0x79, 0x00, 0x8D, 0x43, 0xEE, 0x00, 0x2F, 0xC0, 0x12, 0x02, 0x7C, 0x0A, + 0xAF, 0xD9, 0xBF, 0x0B, 0x3F, 0x25, 0xAD, 0x16, 0x25, 0x4A, 0xD9, 0x07, 0x41, 0xFB, 0x14, 0x2A, + 0x12, 0x00, 0xF3, 0x20, 0x8F, 0x30, 0x8F, 0x1A, 0xBA, 0x0B, 0x69, 0x51, 0x1F, 0x12, 0x45, 0x09, + 0x5E, 0x40, 0x01, 0x01, 0x82, 0x63, 0xF7, 0x03, 0xFF, 0x51, 0xA7, 0x08, 0x8A, 0xAA, 0x25, 0x80, + 0x0F, 0x08, 0x5D, 0x03, 0x64, 0x01, 0x4A, 0x00, 0x00, 0x12, 0x94, 0xAB, 0x40, 0x07, 0xA1, 0x05, + 0x14, 0x13, 0xC5, 0x20, 0x01, 0x33, 0x20, 0x2D, 0xDD, 0x00, 0x2F, 0x40, 0x29, 0x10, 0x09, 0x88, + 0xDD, 0x01, 0xB1, 0xD2, 0x4F, 0x98, 0x9F, 0x0A, 0xAF, 0x09, 0x5F, 0x23, 0x99, 0x00, 0x5F, 0x91, + 0x22, 0x19, 0x71, 0x00, 0x2F, 0xB4, 0xB6, 0x01, 0x61, 0x90, 0xC8, 0x02, 0x4E, 0xBB, 0xFF, 0x20, + 0x61, 0x03, 0xFF, 0x10, 0xF3, 0x00, 0xF5, 0x03, 0xFF, 0xC6, 0xDA, 0x00, 0x00, 0xB8, 0xF6, 0x00, + 0xF8, 0x0C, 0x01, 0x00, 0x55, 0x96, 0x9C, 0x98, 0x8F, 0x00, 0x30, 0x00, 0x2B, 0x26, 0x3B, 0x80, + 0x0F, 0x31, 0xED, 0x07, 0xC7, 0x08, 0x27, 0x00, 0x2F, 0x09, 0x2E, 0x9C, 0x7A, 0x00, 0x27, 0x10, + 0xA1, 0xA2, 0x83, 0x07, 0x47, 0x00, 0x00, 0x90, 0x52, 0x0D, 0x19, 0x03, 0x41, 0x50, 0xBF, 0x10, + 0xE4, 0xBE, 0x49, 0x06, 0xBE, 0x0D, 0xF9, 0x00, 0x2F, 0xA1, 0xDF, 0x01, 0x27, 0x00, 0x2F, 0x00, + 0x00, 0x80, 0x0E, 0x08, 0xF1, 0x93, 0xD9, 0x03, 0xD9, 0x07, 0x90, 0x00, 0x00, 0x20, 0x01, 0x15, + 0x69, 0x40, 0x19, 0x37, 0x63, 0x15, 0x1E, 0x20, 0x01, 0x16, 0x77, 0xC3, 0x3F, 0x01, 0x2E, 0x22, + 0xAD, 0x0C, 0x5F, 0x55, 0xBE, 0x10, 0x01, 0x50, 0x0B, 0x19, 0x15, 0x13, 0xFB, 0x59, 0xCE, 0x00, + 0x00, 0x10, 0x88, 0x15, 0x61, 0x1B, 0xCF, 0x1A, 0xBB, 0x01, 0x7F, 0x05, 0xEC, 0x07, 0x5D, 0x00, + 0x60, 0x07, 0xBE, 0x20, 0x01, 0x03, 0x0D, 0x1A, 0xDF, 0xBE, 0xAD, 0x0B, 0x19, 0xC0, 0x2F, 0x10, + 0x00, 0xA0, 0x0F, 0x10, 0x01, 0x00, 0x00, 0x50, 0x00, 0x60, 0x0F, 0x00, 0x8A, 0x27, 0xA0, 0x12, + 0x4D, 0x00, 0x00, 0x32, 0xF9, 0x12, 0x6F, 0x09, 0x4F, 0x1F, 0x59, 0x14, 0xCC, 0x10, 0x0F, 0x57, + 0x67, 0x06, 0xAB, 0x98, 0xF9, 0x90, 0x0F, 0xA0, 0x6A, 0x9B, 0x36, 0x0B, 0x0D, 0x10, 0x2E, 0x00, + 0xA8, 0x91, 0xD7, 0xEB, 0x7F, 0x03, 0xFE, 0xC2, 0x47, 0x21, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x82, + 0xA0, 0x00, 0x2E, 0x00, 0x5E, 0x80, 0x0F, 0xD2, 0xAF, 0x06, 0xB4, 0x08, 0xCF, 0x3B, 0x35, 0x97, + 0xFF, 0x14, 0x46, 0x09, 0x10, 0x18, 0x1E, 0x18, 0x95, 0xB0, 0x00, 0x04, 0x7A, 0x90, 0xB0, 0x00, + 0xBD, 0x14, 0x6B, 0xA5, 0xEF, 0x2E, 0xC5, 0x07, 0x5A, 0x0F, 0xFF, 0x86, 0x87, 0x16, 0x87, 0x00, + 0x8D, 0x07, 0x6D, 0x29, 0xF6, 0x92, 0x6A, 0x01, 0x22, 0x1B, 0x66, 0x00, 0x00, 0x00, 0xEF, 0xCF, + 0xB2, 0x00, 0x5E, 0x08, 0x00, 0xC0, 0x00, 0x09, 0x4F, 0x1B, 0xBC, 0x0F, 0x64, 0x1B, 0x68, 0x1B, + 0x0F, 0x08, 0xCE, 0x13, 0xFE, 0x10, 0xF6, 0x10, 0x01, 0x0F, 0x31, 0x1B, 0xFF, 0x63, 0xCE, 0x0B, + 0xEF, 0x1B, 0xC5, 0x13, 0x69, 0x10, 0x05, 0xC2, 0xD5, 0x02, 0xE3, 0x20, 0x01, 0x40, 0x09, 0x00, + 0x5F, 0x14, 0xE5, 0x4C, 0xE9, 0x20, 0x01, 0x02, 0x1E, 0x13, 0x67, 0x59, 0xA9, 0x42, 0x23, 0x03, + 0xFF, 0x03, 0xF7, 0x0A, 0x7F, 0x0B, 0x0F, 0x10, 0x03, 0x10, 0x01, 0xF6, 0x0D, 0x02, 0x77, 0x33, + 0x0F, 0x0A, 0xCB, 0xD3, 0x71, 0x22, 0xEA, 0x20, 0x01, 0x03, 0x0F, 0x09, 0xD8, 0x04, 0xCF, 0x0D, + 0x83, 0x32, 0x0D, 0xA2, 0x17, 0x00, 0x2F, 0x08, 0x95, 0xA6, 0x95, 0xEB, 0x5F, 0x08, 0x5F, 0x33, + 0xF5, 0x07, 0xC4, 0x3F, 0xF9, 0x40, 0x01, 0x02, 0x4B, 0xC4, 0x26, 0x02, 0x49, 0xDB, 0xFF, 0x00, + 0xCE, 0xE9, 0xBD, 0x01, 0x87, 0x50, 0x00, 0x23, 0xF5, 0xA0, 0x8F, 0x06, 0x19, 0x63, 0xFF, 0x04, + 0x07, 0x0A, 0x7F, 0x00, 0x00, 0x91, 0x5F, 0x00, 0x6E, 0x0D, 0x7D, 0x00, 0xEF, 0xB0, 0xEF, 0x00, + 0xEF, 0x00, 0x00, 0xF2, 0x9F, 0x04, 0x3C, 0x00, 0x00, 0x08, 0x3F, 0x12, 0xE5, 0x00, 0x98, 0x17, + 0xD7, 0x81, 0x09, 0x10, 0x01, 0x01, 0x98, 0x08, 0xEE, 0x0B, 0x0F, 0xE0, 0x8F, 0x09, 0x2F, 0xE0, + 0x2F, 0x14, 0x6C, 0x04, 0xF9, 0xE2, 0x0F, 0x00, 0xB7, 0x12, 0x37, 0x35, 0xEA, 0xD6, 0x79, 0x01, + 0x48, 0x19, 0xDC, 0x26, 0x0A, 0x50, 0x2F, 0x04, 0x2F, 0xC0, 0x5F, 0x00, 0x33, 0x1E, 0x07, 0x30, + 0x37, 0x20, 0x2F, 0x11, 0x15, 0x0E, 0x09, 0x7D, 0x49, 0x70, 0x8F, 0x00, 0x91, 0x10, 0x5D, 0x00, + 0xEF, 0x75, 0xE9, 0x1B, 0x0A, 0x91, 0xD0, 0x07, 0x78, 0x90, 0x8A, 0x07, 0x3F, 0x11, 0xE7, 0x05, + 0x88, 0x07, 0xCF, 0x90, 0x01, 0x48, 0x3E, 0x80, 0x01, 0x09, 0xE9, 0x40, 0x01, 0x13, 0xF5, 0x23, + 0xF7, 0x60, 0x07, 0x00, 0x00, 0x0C, 0x29, 0x04, 0x87, 0x80, 0x01, 0x0D, 0xB0, 0x01, 0x4B, 0x9B, + 0xCF, 0x07, 0x19, 0x30, 0x89, 0x10, 0xF4, 0x11, 0x43, 0xF0, 0x2F, 0x20, 0x2B, 0x2C, 0x8F, 0x23, + 0xD9, 0x1A, 0x39, 0xF9, 0xEA, 0x52, 0x0F, 0x13, 0xEE, 0x10, 0x62, 0x00, 0x5F, 0x30, 0xA8, 0x2D, + 0x4D, 0x01, 0x23, 0x1D, 0xAF, 0x1A, 0x6B, 0x20, 0xE7, 0x10, 0xC5, 0x01, 0xC5, 0xF0, 0x2F, 0x23, + 0x6F, 0x00, 0x2F, 0x00, 0x8F, 0x00, 0x2F, 0x21, 0x97, 0x53, 0x25, 0x43, 0xFB, 0x03, 0x2F, 0x14, + 0xB8, 0x4B, 0x35, 0x0E, 0x0E, 0x00, 0x00, 0x84, 0x01, 0x0C, 0x3E, 0x90, 0x01, 0x10, 0x00, 0xA3, + 0xFF, 0x00, 0x00, 0x11, 0x2B, 0x11, 0x2D, 0x01, 0x1F, 0x0F, 0xFB, 0x4D, 0x7F, 0x12, 0x1D, 0x96, + 0x15, 0x12, 0x4E, 0x01, 0x4E, 0x3F, 0x64, 0x60, 0x6F, 0x09, 0xB7, 0x14, 0x50, 0x96, 0xFE, 0x00, + 0x5F, 0x12, 0xC5, 0x21, 0xD1, 0x01, 0x4E, 0x22, 0xE0, 0xB0, 0x8F, 0x11, 0x89, 0x00, 0x2F, 0xD0, + 0x2F, 0x08, 0x24, 0x39, 0x23, 0x13, 0x31, 0x17, 0xF5, 0x20, 0x2E, 0x24, 0x65, 0x0F, 0xC9, 0xD9, + 0x8F, 0x14, 0x9F, 0x80, 0x00, 0x0F, 0x4B, 0x5B, 0xF9, 0x00, 0x2F, 0x26, 0xD7, 0x00, 0x2F, 0x31, + 0x20, 0x00, 0x2F, 0x14, 0xF9, 0x24, 0xF7, 0xB2, 0xE9, 0x00, 0xEF, 0x16, 0xA7, 0x11, 0x18, 0x00, + 0x2F, 0x11, 0x92, 0x00, 0x2F, 0x31, 0xC0, 0x00, 0x8F, 0x88, 0xF9, 0xA0, 0xEF, 0x11, 0xFB, 0x01, + 0xF8, 0x5A, 0xAF, 0x10, 0x99, 0x8A, 0xAF, 0x16, 0x61, 0x02, 0x59, 0x32, 0x1B, 0x1B, 0xD0, 0x48, + 0x97, 0x27, 0x13, 0x10, 0x46, 0x22, 0x36, 0x08, 0xA7, 0x25, 0x7B, 0x50, 0x2F, 0x50, 0x5F, 0x00, + 0x2F, 0x10, 0x5F, 0x20, 0xB8, 0x50, 0x2F, 0x10, 0x2F, 0x2B, 0xDE, 0x00, 0x2F, 0x1B, 0x9F, 0x80, + 0x2F, 0x10, 0xE5, 0x24, 0x90, 0x00, 0x2F, 0x1C, 0xA8, 0x3C, 0x90, 0x40, 0x2F, 0x29, 0xED, 0x31, + 0x06, 0x00, 0xBF, 0x50, 0x00, 0xEA, 0xFF, 0x03, 0x0F, 0x71, 0x1F, 0x40, 0x00, 0x00, 0x5F, 0x10, + 0xC1, 0x11, 0x68, 0x46, 0xCF, 0xEA, 0xE9, 0x0C, 0xBF, 0x60, 0x1F, 0x60, 0x52, 0x00, 0x2F, 0x4C, + 0xBF, 0xA0, 0x1F, 0x20, 0x33, 0x00, 0x2F, 0xA0, 0x4F, 0x6C, 0x8F, 0x00, 0x5F, 0x0B, 0x9F, 0x43, + 0x6B, 0xA0, 0x6F, 0x09, 0xC8, 0x00, 0x2F, 0x42, 0xF5, 0x80, 0x01, 0x0D, 0x54, 0x00, 0x2F, 0x40, + 0xE5, 0x40, 0x09, 0x15, 0x5E, 0x40, 0x39, 0x10, 0x09, 0x2E, 0xF6, 0xD7, 0x6F, 0x80, 0x69, 0x0A, + 0x1F, 0x2E, 0x44, 0x20, 0x57, 0x3A, 0x1F, 0x5D, 0x7A, 0x10, 0x01, 0xFE, 0xE8, 0x00, 0x2F, 0x20, + 0x01, 0x00, 0x5F, 0x00, 0x2F, 0x18, 0xBA, 0x62, 0x39, 0x14, 0x99, 0x03, 0xFF, 0x20, 0x5F, 0x11, + 0x49, 0x00, 0x5F, 0xCE, 0xCF, 0x4B, 0x79, 0x80, 0x01, 0x24, 0xF7, 0x00, 0x2F, 0x00, 0x3F, 0xA0, + 0x11, 0x0E, 0x77, 0x00, 0x2F, 0x13, 0x27, 0x0C, 0x50, 0x00, 0x2F, 0xA0, 0xEF, 0x12, 0xDA, 0x06, + 0xA5, 0xE5, 0xD5, 0x29, 0xF4, 0x00, 0x8F, 0x17, 0x28, 0x86, 0x8C, 0x16, 0xD5, 0x00, 0x2F, 0x00, + 0x00, 0x00, 0x5F, 0x15, 0x9D, 0x91, 0xAF, 0x11, 0xAF, 0x06, 0x9B, 0x00, 0x2F, 0x00, 0x42, 0x2C, + 0x72, 0x3F, 0x73, 0x6C, 0x79, 0x39, 0xF7, 0x02, 0x89, 0x00, 0x64, 0x15, 0x67, 0x99, 0x89, 0x35, + 0xC6, 0x00, 0x00, 0x03, 0x47, 0x6C, 0x95, 0x00, 0x00, 0x2C, 0x95, 0x00, 0x00, 0x90, 0x2F, 0x0D, + 0x5D, 0x6C, 0xC5, 0x00, 0x00, 0x1D, 0x23, 0x02, 0xD8, 0x00, 0x00, 0x01, 0x7F, 0x01, 0x4F, 0x1D, + 0x85, 0x10, 0x8C, 0x0A, 0xFD, 0xA4, 0x57, 0x3B, 0xA4, 0x00, 0x00, 0x2C, 0xC4, 0x0C, 0x7D, 0xA0, + 0x00, 0x00, 0x2D, 0x71, 0x21, 0x32, 0x6D, 0x02, 0x71, 0x02, 0x9F, 0x7E, 0x1C, 0x11, 0xED, 0x10, + 0x01, 0x04, 0x3B, 0x19, 0x2B, 0x13, 0xC7, 0x10, 0x03, 0x2D, 0xED, 0x40, 0x01, 0x04, 0x2D, 0x1A, + 0xA9, 0x10, 0x01, 0x10, 0x09, 0x11, 0x91, 0x69, 0x83, 0x3E, 0xDD, 0x01, 0xA1, 0x1E, 0x51, 0x1E, + 0xB7, 0x27, 0xFF, 0x80, 0x01, 0xF1, 0xB7, 0x00, 0x2F, 0x00, 0x5F, 0x00, 0x5F, 0x00, 0x8F, 0x50, + 0x2F, 0x20, 0x2F, 0x01, 0x81, 0x13, 0x0B, 0x41, 0xA3, 0x03, 0xA0, 0x30, 0x2F, 0x50, 0x91, 0x02, + 0x19, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x8F, 0xCA, 0xFF, 0x0E, 0xFF, 0x00, 0x2F, 0x11, 0xD9, 0x33, + 0x8E, 0x11, 0xD3, 0x63, 0x61, 0x07, 0x2F, 0x8B, 0xF7, 0x57, 0x57, 0x00, 0x2F, 0xF0, 0x00, 0x00, + 0x26, 0x0A, 0x7F, 0x00, 0x2F, 0x99, 0xBF, 0x13, 0xF5, 0x06, 0x16, 0xC4, 0xC7, 0x05, 0x38, 0x00, + 0x2F, 0x93, 0xC6, 0x00, 0x2F, 0x1A, 0x18, 0x04, 0x00, 0x20, 0x2F, 0xDD, 0x7F, 0x06, 0x47, 0x00, + 0x2F, 0x02, 0x9F, 0x00, 0x2F, 0x11, 0xAF, 0x70, 0x00, 0x02, 0x0D, 0x00, 0x5F, 0x03, 0xCE, 0x70, + 0x00, 0x29, 0x30, 0x0B, 0x6F, 0x91, 0x5E, 0x00, 0x00, 0x01, 0x9B, 0x01, 0x4F, 0xBA, 0x80, 0x0A, + 0x2C, 0x1A, 0x80, 0x00, 0x8D, 0x11, 0x4F, 0xC0, 0x2B, 0x00, 0x61, 0x01, 0xDF, 0x06, 0xDF, 0x01, + 0xAF, 0x00, 0x00, 0x00, 0x00, 0x68, 0x6D, 0x18, 0x6F, 0x25, 0x5F, 0xA8, 0x71, 0x00, 0x2F, 0xC8, + 0xA1, 0x0A, 0x7F, 0x00, 0x5F, 0x00, 0x2F, 0x00, 0x8F, 0x20, 0x04, 0x80, 0x0D, 0x2E, 0x2D, 0x01, + 0x1F, 0x10, 0x5F, 0x25, 0x8F, 0x00, 0x5F, 0xD9, 0xEF, 0x4D, 0x54, 0x00, 0x2F, 0x0B, 0x69, 0x54, + 0x86, 0x1A, 0x7F, 0x14, 0x8C, 0x40, 0x5F, 0x3D, 0xD7, 0x38, 0x6A, 0x30, 0x01, 0x05, 0x00, 0x30, + 0x5F, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x6F, 0x06, 0x17, 0x00, 0x2F, 0x07, 0xF1, 0x19, + 0x8F, 0x00, 0x5F, 0x9B, 0x8F, 0x09, 0x8F, 0x00, 0x2F, 0x0C, 0x58, 0x50, 0x00, 0x84, 0x61, 0x10, + 0x5D, 0x05, 0x4B, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0xFF, 0x18, 0x18, 0x00, 0x1C, 0x18, 0x00, 0x02, 0x00, 0x00, + 0x15, 0x00, 0x15, 0x02, 0x30, 0x00, 0x00, 0x01, 0x10, 0x55, 0xAA, 0xFF, 0x05, 0x05, 0x08, 0x0D, + 0x0B, 0x11, 0x0E, 0x05, 0x06, 0x06, 0x0B, 0x0B, 0x05, 0x06, 0x05, 0x07, 0x0B, 0x05, 0x05, 0x09, + 0x11, 0x0D, 0x0C, 0x0C, 0x0E, 0x0B, 0x0A, 0x0E, 0x0D, 0x07, 0x06, 0x0B, 0x0A, 0x11, 0x0E, 0x10, + 0x0C, 0x10, 0x0C, 0x0B, 0x0B, 0x0D, 0x0C, 0x12, 0x06, 0x08, 0x0C, 0x0B, 0x0C, 0x09, 0x0C, 0x0A, + 0x07, 0x0B, 0x0C, 0x05, 0x05, 0x0A, 0x05, 0x13, 0x0C, 0x08, 0x09, 0x07, 0x0C, 0x0A, 0x0F, 0x0A, + 0x0A, 0x09, 0x07, 0x0B, 0x07, 0x0B, 0x00, 0x0B, 0x00, 0x04, 0x0B, 0x08, 0x0C, 0x17, 0x0B, 0x06, + 0x12, 0x00, 0x04, 0x04, 0x08, 0x08, 0x08, 0x0A, 0x14, 0x0C, 0x0F, 0x09, 0x09, 0x0C, 0x11, 0x07, + 0x0A, 0x0B, 0x06, 0x11, 0x0A, 0x09, 0x0B, 0x07, 0x07, 0x0C, 0x0C, 0x0D, 0x05, 0x04, 0x07, 0x07, + 0x0A, 0x0F, 0x0F, 0x0F, 0x09, 0x0D, 0x11, 0x0B, 0x07, 0x07, 0x0E, 0x0E, 0x10, 0x0B, 0x10, 0x11, + 0x09, 0x0A, 0x05, 0x0B, 0x0C, 0x0C, 0x0A, 0x0C, 0x0A, 0x23, 0x3D, 0x18, 0x3C, 0x19, 0x34, 0x00, + 0x74, 0x00, 0xB0, 0x2C, 0x00, 0x38, 0x09, 0x38, 0x03, 0xFF, 0x1F, 0xFF, 0xD0, 0x00, 0xE0, 0x09, + 0x0B, 0xE0, 0x1E, 0xB4, 0x02, 0xC0, 0x03, 0x80, 0x0F, 0x07, 0x01, 0xFF, 0x07, 0xFB, 0x0F, 0x41, + 0x40, 0xF0, 0x09, 0x19, 0xF0, 0x03, 0xC0, 0x0B, 0x80, 0x18, 0xD0, 0x78, 0x19, 0xBD, 0xFB, 0x0A, + 0xA8, 0x3C, 0x0A, 0x02, 0xFE, 0x0B, 0xFF, 0x1F, 0x03, 0x80, 0x09, 0x7C, 0x01, 0xFC, 0x0B, 0xBC, + 0x18, 0x07, 0xFD, 0x2F, 0xEF, 0x18, 0x40, 0x00, 0xC0, 0x0B, 0x3F, 0xEF, 0x14, 0x07, 0x0A, 0x00, + 0x07, 0x00, 0x1F, 0x00, 0x3F, 0xC0, 0x3E, 0x3D, 0x16, 0x38, 0x3C, 0x2C, 0x28, 0x1B, 0xF0, 0x3F, + 0xFF, 0x1A, 0xE9, 0x02, 0xC0, 0x02, 0xC0, 0x57, 0xE6, 0xFF, 0xFF, 0x07, 0x43, 0x74, 0x00, 0xFF, + 0xC0, 0xFA, 0x40, 0xF0, 0xE5, 0x00, 0x3D, 0x38, 0x3C, 0x34, 0x2F, 0x78, 0x0B, 0xFD, 0x01, 0x7F, + 0x00, 0x3B, 0x35, 0x00, 0x2C, 0x3C, 0x3C, 0x1E, 0xBA, 0x0B, 0xE7, 0x00, 0x0F, 0x00, 0x1D, 0x1D, + 0xB6, 0xF8, 0xFB, 0xAE, 0xCF, 0x0F, 0x8E, 0x0B, 0x0E, 0x0F, 0x42, 0x07, 0xDF, 0x02, 0xFE, 0x07, + 0xFC, 0x0F, 0x7F, 0x3E, 0x0F, 0x3C, 0x03, 0x3D, 0x03, 0xC0, 0xC7, 0xC0, 0xFF, 0x40, 0x1A, 0x0F, + 0x00, 0x1E, 0x00, 0x2D, 0x19, 0x3C, 0x00, 0x2E, 0x0F, 0x0E, 0x7F, 0xFF, 0x02, 0xF8, 0x07, 0xBD, + 0x0F, 0x4F, 0x0B, 0x40, 0x09, 0xBE, 0xF4, 0x00, 0xA0, 0x3F, 0x3F, 0xC0, 0x3F, 0x40, 0x02, 0xD0, + 0x03, 0xC0, 0x07, 0x80, 0x0B, 0x40, 0x0F, 0x0E, 0x2E, 0x01, 0x3C, 0x01, 0x3D, 0x01, 0x2D, 0x01, + 0x09, 0x14, 0x03, 0x07, 0x2E, 0xF0, 0x03, 0xD0, 0x09, 0x1F, 0x03, 0xFD, 0x02, 0xBF, 0x80, 0x06, + 0xB7, 0x01, 0xE3, 0x03, 0xC3, 0x0F, 0x03, 0x2D, 0x03, 0x78, 0x03, 0xFA, 0xAB, 0xFF, 0xFF, 0xE8, + 0x00, 0xFC, 0x3F, 0x2D, 0x00, 0x3E, 0x1B, 0x1E, 0x0B, 0x03, 0x0F, 0x07, 0x0E, 0x09, 0x00, 0x3F, + 0xBF, 0x2F, 0xFF, 0xE0, 0x0B, 0xB4, 0x0F, 0x0F, 0x0B, 0xAE, 0x02, 0xF8, 0x08, 0x3E, 0x00, 0x1F, + 0xEF, 0x07, 0xBE, 0x00, 0xFF, 0x80, 0x43, 0x0F, 0x1A, 0x1E, 0x0B, 0x80, 0x03, 0xC0, 0x01, 0x13, + 0x1F, 0x3C, 0x00, 0x7C, 0x0B, 0x28, 0x10, 0x20, 0x7C, 0x19, 0x19, 0x2C, 0x1D, 0x78, 0x10, 0x03, + 0x0F, 0xEF, 0x02, 0xFE, 0x0D, 0x1D, 0x0F, 0x40, 0x3F, 0xAA, 0x3F, 0x90, 0x0C, 0x02, 0x3F, 0xAF, + 0x0D, 0x28, 0x1F, 0xFF, 0x2F, 0xAE, 0xC0, 0x0C, 0x03, 0xFE, 0x0F, 0x0E, 0x3F, 0xFF, 0x2B, 0xAE, + 0x00, 0x01, 0x0A, 0x02, 0xFE, 0x0F, 0xEF, 0x2E, 0x0D, 0xFF, 0x2E, 0x1A, 0x61, 0x3B, 0x24, 0x19, + 0x2F, 0xF4, 0xFF, 0xBE, 0x10, 0x1F, 0x1A, 0x00, 0xBE, 0x03, 0xFE, 0x40, 0xAB, 0xE0, 0x00, 0xB8, + 0x0B, 0x7E, 0x18, 0xFF, 0x3E, 0xAF, 0x3C, 0x09, 0x6F, 0x03, 0xFE, 0x0B, 0xF8, 0x00, 0xBC, 0x00, + 0x04, 0x0E, 0xF0, 0x0B, 0xAA, 0x1A, 0x1E, 0x00, 0x6F, 0xF9, 0x0A, 0x0E, 0xBF, 0xE0, 0x1F, 0x20, + 0x3C, 0x00, 0x3F, 0xFE, 0x2F, 0xBF, 0x04, 0x1F, 0x3D, 0xFF, 0x3F, 0xEB, 0x3E, 0x02, 0x40, 0x04, + 0x0B, 0xF4, 0x0A, 0x2D, 0x01, 0x9B, 0x03, 0xFF, 0x07, 0x3D, 0x02, 0x3E, 0x02, 0x1F, 0xAF, 0x07, + 0xFD, 0x02, 0x00, 0x1E, 0x1E, 0x3F, 0x03, 0xF4, 0x2F, 0x40, 0x3F, 0x40, 0x07, 0xF4, 0x00, 0x7F, + 0x00, 0x07, 0xF0, 0x02, 0x2A, 0xAA, 0xF0, 0x40, 0x7F, 0x07, 0xF4, 0x3F, 0x05, 0x7D, 0x01, 0xF4, + 0x03, 0x13, 0x40, 0x0E, 0x03, 0x2C, 0x1F, 0x3C, 0x2D, 0x38, 0x3C, 0x3C, 0x2E, 0x00, 0x2D, 0xFF, + 0x0E, 0x9F, 0x4F, 0x0B, 0x4B, 0x0B, 0x0F, 0x4F, 0x7F, 0xAD, 0xEB, 0x02, 0xD7, 0x03, 0xC3, 0x07, + 0x82, 0xFF, 0x2F, 0x40, 0xF4, 0x00, 0x01, 0x3C, 0x07, 0x02, 0x06, 0x2E, 0x3D, 0x13, 0x3D, 0x00, + 0x2F, 0x1F, 0x00, 0x08, 0x1E, 0x03, 0x3D, 0x02, 0xFF, 0x00, 0xAF, 0x03, 0xFC, 0x04, 0x14, 0x00, + 0x3E, 0xAF, 0x2F, 0xE0, 0x0D, 0x1F, 0x02, 0x0C, 0x19, 0x01, 0x09, 0x07, 0x06, 0xBF, 0x0F, 0x0D, + 0x20, 0x2D, 0x70, 0x3D, 0x15, 0xE0, 0x03, 0x19, 0x2C, 0x0B, 0x1E, 0xFA, 0xE2, 0xF4, 0x00, 0x0B, + 0x2D, 0x0A, 0x3C, 0x02, 0x3E, 0xAF, 0x0E, 0x0F, 0xC0, 0x07, 0xFE, 0x00, 0xBF, 0xBC, 0x0B, 0x3C, + 0x01, 0xFC, 0x0A, 0x37, 0x00, 0x0F, 0x80, 0x03, 0x0F, 0x06, 0x22, 0x40, 0xBF, 0x33, 0x1E, 0x0E, + 0x1E, 0x3F, 0x00, 0x3F, 0x80, 0x3F, 0xFC, 0x01, 0xFC, 0x03, 0x09, 0x3E, 0x0F, 0xF4, 0xFE, 0x3E, + 0xBF, 0x0B, 0x20, 0x18, 0x06, 0xFE, 0x1F, 0xEF, 0x0A, 0xFF, 0xFF, 0xAA, 0xFA, 0x04, 0x28, 0x78, + 0x0A, 0xF0, 0x02, 0xF0, 0x03, 0xB8, 0xE0, 0x03, 0xF0, 0x03, 0xF4, 0x07, 0x08, 0xF0, 0x00, 0x7C, + 0x02, 0x3D, 0x08, 0xB8, 0x01, 0x0A, 0xAF, 0x0A, 0x3F, 0xC0, 0x3E, 0x80, 0x1A, 0xF0, 0x00, 0xB0, + 0x0E, 0x19, 0x78, 0xEE, 0x24, 0x18, 0x3C, 0x0F, 0x3C, 0x7D, 0x3C, 0xF4, 0x3F, 0xF4, 0x3F, 0xFC, + 0x3D, 0x3D, 0x07, 0x0E, 0x0F, 0xD0, 0x3D, 0xE0, 0x3C, 0xF0, 0x3C, 0xB4, 0x3C, 0x7C, 0x3C, 0x3C, + 0x3C, 0x2D, 0x3C, 0x1F, 0x03, 0xBC, 0x0B, 0x7C, 0x0F, 0x3C, 0x1E, 0x3C, 0x2C, 0x74, 0x78, 0x3C, + 0x3D, 0x3C, 0x1F, 0x3C, 0x0B, 0x3C, 0x03, 0x3C, 0x02, 0x4F, 0x00, 0xCF, 0x00, 0xEF, 0x01, 0x3E, + 0x03, 0xD0, 0x01, 0x00, 0x3E, 0xBF, 0x3F, 0xFE, 0x08, 0x23, 0xFE, 0x3C, 0x2F, 0x02, 0x40, 0x0F, + 0xF8, 0x01, 0xFF, 0x07, 0x07, 0x1B, 0x7C, 0x03, 0x1E, 0x02, 0x0F, 0x03, 0x0F, 0x47, 0x07, 0x8B, + 0x03, 0xCF, 0x03, 0xDF, 0x04, 0x78, 0x0B, 0x3C, 0x0F, 0x3D, 0x0F, 0x2D, 0x1E, 0x1F, 0x2D, 0x0F, + 0x3C, 0x0F, 0x7C, 0x0B, 0xB4, 0xB8, 0x3C, 0x0F, 0x2D, 0x2E, 0x0E, 0x07, 0xB8, 0x0F, 0x0F, 0x0B, + 0xAE, 0x03, 0xFC, 0x01, 0xF4, 0x02, 0xFC, 0x03, 0xFD, 0x0B, 0x5F, 0x1F, 0x0F, 0x00, 0x2E, 0x07, + 0x5E, 0x07, 0xFD, 0x02, 0xF8, 0x0D, 0x00, 0x07, 0xC0, 0x11, 0x10, 0x2D, 0x40, 0x07, 0x80, 0x03, + 0xC0, 0x02, 0x0D, 0x22, 0x03, 0xCB, 0x0B, 0x47, 0x0F, 0x02, 0x2D, 0x00, 0x38, 0xB4, 0x15, 0x1F, + 0x00, 0xBD, 0x0B, 0x03, 0xE0, 0x00, 0x0A, 0x90, 0x0B, 0x3C, 0xD0, 0x3C, 0xC0, 0x09, 0xFF, 0x3F, + 0x0B, 0x07, 0xFA, 0x0B, 0xC0, 0xBF, 0x40, 0x31, 0x00, 0xFC, 0x00, 0x7D, 0x0B, 0x03, 0x0A, 0x00, + 0x07, 0xFD, 0x18, 0x14, 0xEB, 0x0A, 0x01, 0xFD, 0x0A, 0xF0, 0x07, 0x09, 0x78, 0x03, 0xC0, 0x0B, + 0x1D, 0x21, 0x0A, 0x78, 0x1A, 0x6F, 0x8B, 0xB8, 0x1F, 0x48, 0x16, 0x36, 0x15, 0x18, 0xFC, 0x0F, + 0xE8, 0x1C, 0x4C, 0x1E, 0x22, 0x1E, 0x5A, 0xFF, 0x55, 0x0B, 0xFE, 0x3F, 0xBF, 0x0B, 0xFF, 0x7F, + 0xEB, 0xF8, 0x03, 0xF0, 0x3D, 0xBF, 0x3F, 0xEF, 0x3F, 0x3D, 0xF0, 0x7C, 0x07, 0x07, 0xFE, 0x2F, + 0xFF, 0x7D, 0x10, 0x0B, 0xF9, 0x3F, 0xAF, 0x7C, 0x03, 0xF8, 0x01, 0x08, 0x07, 0xF8, 0x2F, 0xBF, + 0x7C, 0x0B, 0xF4, 0x03, 0xFF, 0xFF, 0xFA, 0xAA, 0xF4, 0x08, 0xBF, 0xF0, 0x5F, 0x90, 0x1A, 0x07, + 0xFF, 0x1F, 0xAF, 0x3D, 0x03, 0x3C, 0x03, 0x3D, 0x07, 0x2F, 0xAF, 0x0B, 0xFE, 0x2D, 0x08, 0x01, + 0x40, 0x08, 0x20, 0x1D, 0x02, 0x2F, 0x3C, 0xBC, 0x3E, 0xF0, 0x3F, 0xF4, 0x3E, 0x7C, 0x3C, 0x2E, + 0x3A, 0x38, 0x02, 0x43, 0xFD, 0xEF, 0xBF, 0xFC, 0x07, 0xF4, 0x19, 0x07, 0xFD, 0x2F, 0xEF, 0x7C, + 0x03, 0x01, 0x06, 0x19, 0x0B, 0xF8, 0xB0, 0x0D, 0x38, 0xBD, 0x3F, 0xFC, 0x3F, 0x40, 0x0F, 0xFD, + 0xBE, 0xBE, 0x7F, 0x40, 0x1B, 0xF8, 0x00, 0xBE, 0x2A, 0xFF, 0xFF, 0x6A, 0xA9, 0x40, 0xF4, 0x0B, + 0xBE, 0xBF, 0x2F, 0xF6, 0x0F, 0x00, 0x3F, 0xEB, 0x3C, 0x90, 0x09, 0x7C, 0x00, 0x3F, 0xEF, 0x0B, + 0xFE, 0x18, 0xBC, 0x03, 0x3F, 0xAF, 0x0B, 0x0E, 0x7D, 0x0A, 0x14, 0x00, 0x2F, 0xFF, 0x3F, 0xFF, + 0x01, 0xBE, 0x5B, 0x2F, 0x02, 0x0E, 0x1E, 0xFC, 0x11, 0x07, 0x3C, 0x0C, 0x24, 0x06, 0x1E, 0x7C, + 0x03, 0x0C, 0x3D, 0xFF, 0x0E, 0x04, 0x0E, 0x1A, 0xFA, 0xBE, 0xBF, 0x0A, 0x17, 0x01, 0x00, 0x0B, + 0x49, 0xE1, 0xB0, 0x02, 0xF0, 0x03, 0x14, 0x19, 0x7A, 0x3F, 0x01, 0xFF, 0xE0, 0x10, 0x6B, 0x3D, + 0xA0, 0x6A, 0xB0, 0x47, 0x7D, 0x00, 0xFF, 0x03, 0xC7, 0x12, 0xBF, 0xFC, 0x5F, 0x94, 0x1A, 0x0F, + 0xB8, 0x07, 0x0F, 0x2D, 0x1E, 0x0F, 0x3D, 0x0F, 0x3C, 0x08, 0xF0, 0x0F, 0xF0, 0x1F, 0xF4, 0x2F, + 0x74, 0x3E, 0x7C, 0x75, 0x3C, 0xB4, 0x3C, 0xF0, 0x1D, 0xE0, 0x40, 0xB0, 0x80, 0xF0, 0xC0, 0xF0, + 0xC1, 0xE0, 0xD2, 0xD0, 0xE3, 0xC0, 0xF3, 0xC0, 0xB7, 0xBC, 0x0B, 0x3D, 0x0F, 0x2F, 0x3E, 0x0F, + 0xBC, 0x07, 0xF8, 0x0F, 0x7C, 0x0E, 0x78, 0x1E, 0x2E, 0x0F, 0x08, 0xFF, 0x56, 0x6F, 0x78, 0x01, + 0xF0, 0x03, 0xD0, 0x0B, 0x40, 0x10, 0x03, 0xC0, 0x0F, 0x80, 0xFE, 0x00, 0xBF, 0x40, 0x07, 0x10, + 0x15, 0x40, 0x0B, 0xC0, 0x01, 0xFC, 0x03, 0xF8, 0x0F, 0x0A, 0xD0, 0x3E, 0xFE, 0x20, 0x2F, 0x10, + 0x05, 0x21, 0xC0, 0x0F, 0x40, 0x7F, 0xFF, 0x6F, 0xAA, 0x0F, 0x00, 0x7F, 0xFE, 0x6F, 0xE9, 0x0B, + 0x0A, 0x54, 0x02, 0xFF, 0x02, 0xBE, 0xD0, 0x0B, 0x4D, 0xFF, 0x3E, 0xFB, 0x0D, 0xFF, 0x2E, 0xFB, + 0x3F, 0xC0, 0x35, 0x40, 0x0F, 0xE8, 0x03, 0x0F, 0x02, 0x2F, 0xEF, 0x0B, 0xFD, 0x18, 0x0B, 0xB8, + 0x18, 0x1E, 0xD0, 0x0F, 0xC0, 0x0F, 0x7F, 0x40, 0x09, 0x2F, 0x3E, 0x3D, 0x0F, 0xFC, 0x40, 0x0B, + 0x07, 0xB4, 0x03, 0xF0, 0x02, 0xF0, 0x02, 0xD0, 0x03, 0xC0, 0x07, 0x80, 0xAF, 0x40, 0x07, 0xF9, + 0x55, 0xFF, 0x10, 0x14, 0x24, 0x69, 0x07, 0xD0, 0x02, 0xFE, 0x00, 0x7F, 0x15, 0x1A, 0xB4, 0x18, + 0x0A, 0xF8, 0x0F, 0x07, 0x3C, 0x3C, 0x7C, 0xB4, 0xB4, 0xB0, 0xF0, 0xF0, 0x11, 0x00, 0x00, 0x2C, + 0x0B, 0x02, 0x0D, 0xF4, 0x1D, 0x1A, 0x38, 0xE0, 0x1E, 0xB4, 0x3C, 0x0B, 0x00, 0x0D, 0x00, 0x2C, + 0x01, 0x0E, 0x0F, 0x03, 0xFC, 0x01, 0x07, 0xFF, 0x1F, 0xEF, 0x40, 0x0B, 0x21, 0xBF, 0x03, 0xFE, + 0x0F, 0xFF, 0xFF, 0xFE, 0xAA, 0x1B, 0x06, 0x3F, 0xFF, 0x2A, 0xAF, 0x6A, 0x0B, 0x00, 0x1D, 0x1B, + 0x0F, 0x00, 0x2E, 0x1B, 0x0B, 0x0B, 0x1D, 0x1D, 0x10, 0x2E, 0x2E, 0x1A, 0x7D, 0xF4, 0x03, 0xFF, + 0x03, 0x47, 0x70, 0x0F, 0xFF, 0xCF, 0x1E, 0x0F, 0x0D, 0x03, 0xC0, 0x47, 0xC0, 0x8A, 0x07, 0x38, + 0x2C, 0x0F, 0xF0, 0x07, 0x42, 0x5D, 0x38, 0x2C, 0x3C, 0x3E, 0x1E, 0xBB, 0x0B, 0xEB, 0x00, 0x0E, + 0x00, 0x2C, 0x38, 0xEB, 0xE0, 0xEE, 0xB8, 0xBC, 0x3C, 0x80, 0x7A, 0xE0, 0xB0, 0x3C, 0x2F, 0x40, + 0x0F, 0xF8, 0x01, 0xFF, 0x00, 0x1F, 0x00, 0x07, 0x00, 0x03, 0x04, 0x01, 0x80, 0x03, 0x80, 0x2D, + 0x11, 0x1F, 0x00, 0x3E, 0x3D, 0x1F, 0x2C, 0x1F, 0x7C, 0x07, 0x6E, 0x78, 0x1D, 0x78, 0x1D, 0x78, + 0xB8, 0xF4, 0x1B, 0x78, 0x78, 0xF0, 0x1D, 0x0F, 0xD0, 0x3F, 0xF0, 0x1F, 0x1D, 0x3F, 0xFF, 0x0E, + 0xFF, 0xFC, 0x20, 0x0D, 0xCD, 0xC0, 0xDD, 0xC0, 0xB9, 0xC0, 0x71, 0x07, 0x1F, 0xFD, 0xBF, 0xBE, + 0x7F, 0x40, 0x1B, 0xF8, 0x00, 0xBE, 0x11, 0x24, 0x07, 0x80, 0x10, 0xFD, 0x2F, 0xEF, 0x7C, 0x03, + 0xF4, 0x01, 0xF0, 0xF4, 0x00, 0xF4, 0x01, 0x07, 0xF8, 0xAF, 0xBF, 0xFC, 0x0B, 0xFF, 0xFA, 0xAA, + 0x39, 0x38, 0x00, 0x74, 0x00, 0x3C, 0x3C, 0x2E, 0xB8, 0x0B, 0xF0, 0xF0, 0xBA, 0xE0, 0x2F, 0x07, + 0x7E, 0xAF, 0x3F, 0xC0, 0x0B, 0x80, 0x02, 0x1B, 0x0F, 0x80, 0x07, 0xFA, 0x00, 0x3C, 0x00, 0xFE, + 0xAA, 0x2C, 0x3E, 0xAA, 0xFF, 0xFF, 0xFA, 0xBE, 0xBF, 0xF8, 0x18, 0x2D, 0x00, 0x28, 0x1B, 0x7C, + 0x03, 0x3F, 0xAF, 0x0B, 0xFE, 0xFD, 0x00, 0xAF, 0xEF, 0x07, 0x42, 0x18, 0x0F, 0x0F, 0x0F, 0xF0, + 0x00, 0xB8, 0x02, 0x3C, 0xF0, 0x6B, 0x0B, 0x7F, 0x02, 0xFF, 0x03, 0x90, 0x3D, 0x3C, 0x09, 0x15, + 0xBE, 0xF9, 0x17, 0x03, 0x82, 0x03, 0xC3, 0x1A, 0x01, 0xF5, 0x07, 0xF8, 0x00, 0x1F, 0x40, 0x01, + 0x09, 0x2F, 0xD0, 0x15, 0xB0, 0xA8, 0x17, 0xFF, 0xFF, 0xAA, 0xAA, 0x90, 0x11, 0x07, 0xF4, 0x1F, + 0xAD, 0x3C, 0x10, 0x34, 0x19, 0xC0, 0xB5, 0xF0, 0x1E, 0xFF, 0x56, 0x6F, 0x78, 0x01, 0x0B, 0x40, + 0x10, 0x2E, 0x07, 0x5E, 0x07, 0xFD, 0x02, 0xF8, 0x3E, 0x2D, 0x3D, 0x11, 0x03, 0xFF, 0x0F, 0x0F, + 0x80, 0x07, 0xFB, 0x00, 0x03, 0xC0, 0x3F, 0xFF, 0x2B, 0xEA, 0x0E, 0x18, 0x00, 0x2E, 0xFE, 0x0B, + 0xEF, 0x0F, 0x03, 0x0E, 0x01, 0x0F, 0x03, 0x0B, 0xEB, 0x1F, 0xFF, 0x90, 0x00, 0x07, 0x02, 0xEB, + 0x00, 0xFF, 0x0B, 0xFF, 0x06, 0xBE, 0x00, 0x3C, 0x0F, 0xFF, 0xD0, 0x01, 0x1D, 0x3F, 0xD0, 0x7E, + 0xFC, 0xF0, 0x2F, 0xF0, 0x0F, 0xBD, 0x0F, 0x2F, 0xFD, 0x02, 0xFD, 0x14, 0x1B, 0x07, 0x2C, 0x1F, + 0x34, 0x3C, 0x34, 0x3C, 0x30, 0x2C, 0x1F, 0x0D, 0x07, 0xF8, 0x70, 0xA8, 0x38, 0x00, 0x1C, 0x0C, + 0x00, 0x1C, 0x9C, 0x38, 0xFC, 0x74, 0xF0, 0x3D, 0xB0, 0x38, 0xF0, 0x2F, 0x1B, 0x01, 0x81, 0x03, + 0x87, 0x0F, 0x0F, 0x2E, 0x2E, 0x2D, 0x3D, 0x0F, 0x80, 0x0F, 0x3F, 0xFF, 0x2A, 0x0A, 0x3F, 0x11, + 0x0D, 0x3F, 0x2C, 0x3E, 0x34, 0x38, 0x34, 0x3D, 0x30, 0x3F, 0x34, 0x3E, 0x2C, 0x38, 0x0D, 0x3C, + 0xE0, 0x70, 0xF8, 0x38, 0x38, 0x1C, 0xF4, 0x0C, 0xE0, 0x1C, 0xF0, 0x1C, 0xF4, 0x38, 0x3C, 0x2F, + 0x38, 0x07, 0x3C, 0x0F, 0x1F, 0xAE, 0x07, 0x18, 0x3F, 0xFF, 0x1A, 0xBE, 0xF4, 0x00, 0xA0, 0x06, + 0xF0, 0x02, 0xD0, 0x07, 0x40, 0x2D, 0x00, 0xB5, 0x40, 0xFF, 0x19, 0xF9, 0x55, 0x0B, 0x4B, 0x11, + 0xFF, 0x0E, 0x0B, 0x80, 0x3F, 0xA0, 0x0B, 0x07, 0x0F, 0x16, 0x0F, 0xFA, 0xBF, 0x1A, 0x1E, 0x07, + 0x40, 0x01, 0xF5, 0x00, 0x6F, 0xD0, 0x1F, 0x80, 0x3A, 0x07, 0x87, 0x02, 0x82, 0x2A, 0x10, 0x40, + 0xF4, 0xE0, 0x5F, 0x3B, 0x1F, 0x10, 0x1A, 0x18, 0x2A, 0x7F, 0xC0, 0xA5, 0x1A, 0x2F, 0x49, 0xFF, + 0x2F, 0xFF, 0xFC, 0x5C, 0x00, 0x4C, 0x69, 0x07, 0x40, 0x2F, 0x80, 0x27, 0x19, 0x0B, 0xC0, 0x2D, + 0xE0, 0x38, 0x49, 0x0B, 0x00, 0x0E, 0x0B, 0x2C, 0x0A, 0x03, 0x80, 0x0B, 0x00, 0x1E, 0x31, 0x02, + 0xE0, 0x7C, 0x7E, 0x00, 0x04, 0x0F, 0x3D, 0x00, 0xFF, 0x03, 0xC7, 0x0B, 0xF4, 0x47, 0xBE, 0x30, + 0x0A, 0x0F, 0x7D, 0x00, 0xD7, 0x00, 0xE7, 0x14, 0xFF, 0xFC, 0xFA, 0xA8, 0x0B, 0x6F, 0x03, 0xFE, + 0x0B, 0xF8, 0x00, 0xBC, 0x00, 0x04, 0x10, 0xF0, 0x3F, 0xC0, 0x05, 0x95, 0xF0, 0xBF, 0x1C, 0x19, + 0x01, 0x00, 0x0F, 0xFF, 0x07, 0xFF, 0x00, 0x13, 0x00, 0x03, 0x5C, 0x02, 0x2C, 0x13, 0x25, 0x80, + 0x13, 0x70, 0x38, 0xB0, 0x2C, 0xE0, 0x0F, 0x1B, 0x24, 0x24, 0x2D, 0x2D, 0x0F, 0x0F, 0x07, 0xC7, + 0x07, 0x87, 0x0F, 0x04, 0x81, 0x03, 0x83, 0x03, 0x8B, 0x03, 0x8E, 0x74, 0x74, 0xD1, 0xE0, 0x83, + 0xE0, 0x4E, 0xE0, 0x1C, 0xE0, 0x34, 0xE0, 0xF0, 0xE0, 0x87, 0x03, 0x8F, 0x03, 0xAC, 0x03, 0xB8, + 0x00, 0xF0, 0x01, 0xD0, 0x7F, 0xC0, 0xB9, 0x0B, 0xB2, 0x95, 0xF7, 0xBF, 0xCB, 0x3C, 0x00, 0xF1, + 0xE0, 0xD3, 0xE0, 0x8E, 0x06, 0x80, 0x0B, 0xC0, 0x07, 0x0F, 0x10, 0xEB, 0x02, 0xD7, 0x03, 0xC3, + 0x07, 0x82, 0xFF, 0x2F, 0xAA, 0x3C, 0xE0, 0xF4, 0x00, 0xB8, 0x02, 0x4A, 0x1E, 0x3D, 0xB4, 0x03, + 0xC0, 0x07, 0x80, 0x0F, 0xFF, 0x1F, 0xAA, 0x2D, 0x02, 0x1F, 0x00, 0x2E, 0x3E, 0x40, 0x2F, 0x3D, + 0x02, 0x3F, 0xEF, 0x3F, 0xFD, 0x0F, 0x03, 0x38, 0x1C, 0x0F, 0xFD, 0x6F, 0x2D, 0x2D, 0x28, 0x17, + 0x01, 0xF8, 0x55, 0xE4, 0x08, 0xC0, 0x07, 0x40, 0x00, 0xB5, 0x10, 0xFF, 0x0A, 0x02, 0x0E, 0x3E, + 0xF4, 0x01, 0x7F, 0xBF, 0x1F, 0x11, 0x78, 0x1F, 0xF8, 0x3C, 0xF0, 0x00, 0xFA, 0xA8, 0xFF, 0xFC, + 0x09, 0xC0, 0x07, 0xFE, 0x00, 0xBF, 0x07, 0x3F, 0x00, 0xC0, 0x0C, 0x3F, 0xFF, 0x3E, 0xC0, 0x06, + 0x16, 0x01, 0xF4, 0x03, 0xFD, 0x16, 0x0E, 0x0B, 0x0F, 0x16, 0xB8, 0xBF, 0xE0, 0x1F, 0x07, 0xF0, + 0xBF, 0xF0, 0x14, 0x0F, 0x40, 0x3F, 0xC0, 0xF1, 0x16, 0xE0, 0xB0, 0xF0, 0x16, 0x3E, 0xAF, 0x40, + 0x03, 0x3F, 0x00, 0x3F, 0x04, 0x2E, 0x03, 0xFE, 0x06, 0xBC, 0x07, 0x07, 0xD0, 0x07, 0x00, 0xD1, + 0x0C, 0xF8, 0x06, 0x3C, 0x0A, 0x0B, 0xF7, 0xC0, 0xBF, 0x40, 0x2F, 0x02, 0xF8, 0x08, 0x07, 0x7D, + 0x06, 0x03, 0xC3, 0x03, 0x83, 0x07, 0x01, 0x06, 0x7E, 0x10, 0x7C, 0xFF, 0xF0, 0xBE, 0xA0, 0x3D, + 0x1F, 0xE0, 0x3C, 0xF0, 0x3C, 0x78, 0x3C, 0x3D, 0x3C, 0x1F, 0x3C, 0x0B, 0x3C, 0x03, 0x3C, 0x02, + 0x4F, 0x00, 0xCF, 0x00, 0xEF, 0x01, 0x3C, 0x03, 0xD0, 0x02, 0x01, 0x03, 0x28, 0x4A, 0x14, 0x1E, + 0x01, 0x0B, 0x47, 0x02, 0xEE, 0x00, 0xBC, 0x01, 0xEE, 0x07, 0x8B, 0x1E, 0x02, 0x04, 0x80, 0x00, + 0x3D, 0x01, 0x0F, 0x3D, 0x2E, 0x3E, 0x3C, 0x1F, 0xF4, 0x3F, 0xD0, 0xF6, 0xF0, 0xE1, 0xF0, 0xC0, + 0xF0, 0x40, 0x05, 0x8C, 0x00, 0x3E, 0xAA, 0x3F, 0xFF, 0x04, 0x86, 0x40, 0xBF, 0x0B, 0x8C, 0x3C, + 0x01, 0x3E, 0xAF, 0xFC, 0x0A, 0xFF, 0x00, 0x7F, 0x08, 0x80, 0x07, 0xFA, 0xF8, 0xC9, 0x1E, 0x0F, + 0xE0, 0x07, 0xFE, 0x0F, 0xBF, 0x01, 0x17, 0xEB, 0x02, 0xF8, 0x06, 0x82, 0x03, 0x3D, 0xF0, 0x00, + 0xB8, 0x02, 0x3C, 0xE0, 0x0A, 0x3F, 0xFE, 0x16, 0x06, 0xFF, 0x1F, 0xEB, 0xE0, 0x06, 0x0B, 0x80, + 0x03, 0xD0, 0x01, 0x19, 0x17, 0xF4, 0x03, 0xFC, 0x16, 0x03, 0xD0, 0x0B, 0xFF, 0x0D, 0x0E, 0x14, + 0x5C, 0x03, 0x9C, 0x01, 0x3A, 0x2D, 0x7E, 0x1C, 0x10, 0x17, 0xC7, 0x0D, 0x82, 0xC0, 0xC3, 0x19, + 0x00, 0x00, 0x0A, 0x42, 0x07, 0xFF, 0x07, 0xFD, 0x0B, 0x80, 0x08, 0x1E, 0x07, 0x0F, 0x0F, 0x0B, + 0x5E, 0x03, 0xFD, 0x02, 0xF8, 0x0E, 0x3E, 0xBF, 0x03, 0x3E, 0xBF, 0x3F, 0xFE, 0x04, 0x3C, 0x07, + 0x3D, 0x2F, 0x80, 0x00, 0x1B, 0xFE, 0x2F, 0xBF, 0x03, 0x0B, 0xFF, 0x7F, 0xEB, 0xF8, 0x03, 0xF0, + 0xFE, 0x3F, 0x1B, 0x1E, 0x1B, 0x1D, 0x4E, 0x0B, 0xFD, 0x3F, 0xAF, 0x97, 0x1F, 0xE0, 0xBE, 0xFD, + 0xF0, 0x2E, 0xD0, 0x0F, 0xFF, 0xFF, 0xEA, 0xAA, 0xD0, 0x01, 0x07, 0xFE, 0x2F, 0xFF, 0x7D, 0x10, + 0x07, 0xF8, 0x2F, 0xBF, 0x7C, 0x0B, 0xF4, 0x03, 0xFF, 0xFF, 0xFA, 0xAA, 0xF4, 0x40, 0x04, 0x07, + 0xFC, 0x1C, 0x0B, 0x1D, 0x1E, 0x16, 0x78, 0xFE, 0x3F, 0xEF, 0xBC, 0x03, 0xF4, 0xF4, 0x01, 0xC0, + 0x02, 0x3C, 0xFF, 0x3F, 0xEF, 0x3F, 0x01, 0x80, 0x06, 0x15, 0x06, 0x0F, 0x40, 0x3C, 0xFA, 0x3C, + 0x3C, 0xE0, 0x0A, 0x0B, 0xBE, 0xBF, 0x2F, 0xF6, 0x0A, 0xDF, 0x0F, 0xBE, 0xBD, 0x2F, 0xF4, 0x00, + 0xFF, 0xBE, 0x2F, 0xFD, 0x08, 0x7C, 0x00, 0x3F, 0xEF, 0x0B, 0xFE, 0x00, 0xB0, 0x7C, 0x00, 0x3C, + 0x03, 0x0F, 0x7D, 0x0A, 0x7E, 0x1F, 0x7D, 0xBC, 0x03, 0x3F, 0xAF, 0xD0, 0x0B, 0x00, 0x1D, 0x02, + 0x7C, 0xD6, 0x2B, 0x08, 0x1F, 0x0B, 0x00, 0x0F, 0x03, 0xC7, 0x16, 0xC2, 0x03, 0x83, 0x0D, 0x1D, + 0x17, 0x2C, 0xFF, 0x2E, 0x07, 0xFD, 0x2F, 0xEF, 0x7C, 0x10, 0x01, 0x1C, 0x1E, 0x1D, 0x00, 0x50, + 0x7F, 0xFF, 0x6A, 0xAA, 0x90, 0x06, 0xFF, 0x0F, 0xF4, 0x3F, 0xF0, 0x7C, 0xF0, 0xF0, 0xF6, 0xE0, + 0xFB, 0xC1, 0x0A, 0x99, 0xF0, 0x03, 0xF0, 0x03, 0x78, 0x0B, 0x3C, 0x0F, 0x3C, 0x0F, 0x1E, 0x2E, + 0x0F, 0x3C, 0x0B, 0x3C, 0x08, 0xBF, 0x3D, 0x00, 0x07, 0x18, 0xFF, 0x3D, 0x7C, 0x03, 0x2F, 0xE0, + 0x0D, 0xAE, 0x1E, 0x7F, 0x3F, 0xFE, 0x34, 0x17, 0x3D, 0x03, 0x2F, 0xEF, 0x0B, 0x0E, 0x7E, 0x07, + 0xB4, 0x03, 0xF0, 0x02, 0xF0, 0x02, 0xD0, 0x03, 0xC0, 0x07, 0x80, 0xAF, 0x40, 0x09, 0x3F, 0x00, + 0x3F, 0xEB, 0x3D, 0xFF, 0xF8, 0x06, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x00, +}; + +// stuff from the game that we call. source: rb3 decompilation +#define OS_BUS_CLOCK_SPEED (*(uint32_t *)0x800000F8) +#define OS_TIME_SPEED (OS_BUS_CLOCK_SPEED / 4) +#define OSMillisecondsToTicks(x) ((x) * (OS_TIME_SPEED / 1000)) +typedef struct _GXColor { + uint8_t r, g, b, a; +} GXColor; +typedef enum _OSErrorType { + OS_ERR_SYSTEM_RESET, + OS_ERR_MACHINE_CHECK, + OS_ERR_DSI, + OS_ERR_ISI, + OS_ERR_EXT_INTERRUPT, + OS_ERR_ALIGNMENT, + OS_ERR_PROGRAM, + OS_ERR_FP_UNAVAIL, + OS_ERR_DECREMENTER, + OS_ERR_SYSTEM_CALL, + OS_ERR_TRACE, + OS_ERR_PERF_MONITOR, + OS_ERR_IABR, + OS_ERR_SMI, + OS_ERR_THERMAL_INT, + OS_ERR_PROTECTION, + OS_ERR_FP_EXCEPTION, + OS_ERR_MAX +} OSErrorType; +void OSFatal(GXColor textColor, GXColor bgColor, const char* msg); +void *OSSetErrorHandler(uint16_t error, void *handler); +void PPCHalt(); +void OSReturnToMenu(); +uint64_t OSGetTime(); + +char RB3E_ExceptionHandlerEntered = 0; +char RB3E_ExceptionText[0x600]; +char RB3E_ExceptionFormatBuffer[128]; + +void RB3EBase(); + +static inline char IsAddressValid(uint32_t address) { + // 64MB MEM1 + if (address >= 0x80000000 && address < 0x84000000) + return 1; + // 256MB MEM2 + if (address >= 0x90000000 && address < 0xA0000000) + return 1; + return 0; +} + +// this is a function that gets called after OSFatal writes to the screen +#define REBOOT_SECONDS 30 +static void ReplaceHaltAfterFatal() { + for (int i = REBOOT_SECONDS; i > 0; i--) { + RB3E_PRINT("Restarting in %i seconds...\n", i); + uint64_t start = OSGetTime(); + do { + } while (OSGetTime() - start < OSMillisecondsToTicks(1000)); + } + // if we have the HBC stub, use that + if (*(uint64_t *)0x80001804 == 0x5354554248415858ull) { // detect 'STUBHAXX' + RB3E_DEBUG("Using HBC stub\n", NULL); + //*(uint64_t *)0x80002F00 = 0x4c4f41444b544858ull; // 'LOADKTHX' + //*(uint64_t *)0x80002F08 = 0x0000000100000002ull; // system menu + ((void(*)(void))0x80001800)(); + } + // this doesn't work... + // TODO(Emma): rig up manual IPC to reload to the Wii Menu in lieu of STUBHAXX + OSReturnToMenu(); +} + +void RB3E_ErrorOnException(uint8_t error, OSContext *ctx, uint32_t dsisr, uint32_t dar) { + // if we've already entered our exception handler, we must've crashed *in* our handler.. oops + // so we just immediately crash + if (RB3E_ExceptionHandlerEntered) + PPCHalt(); + RB3E_ExceptionHandlerEntered = 1; + + RB3E_PRINT("--== RB3ENHANCED EXCEPTION ==--\n"); + + // build out the exception text + RB3E_ExceptionText[0] = '\0'; +#ifndef RB3E_WII_BANK8 + strcat(RB3E_ExceptionText, "Rock Band 3 has crashed. Restarting in 30 seconds...\n" "If this happens again, report to the RB3Enhanced developers!\n\n"); +#else + strcat(RB3E_ExceptionText, "Rock Band 3 has crashed. (DEBUG BUILD)\n\n"); +#endif +#define fmtExc(...) { sprintf(RB3E_ExceptionFormatBuffer, __VA_ARGS__); strcat(RB3E_ExceptionText, RB3E_ExceptionFormatBuffer); printf(RB3E_ExceptionFormatBuffer); } + // print out the RB3E version and base address + fmtExc("RB3Enhanced " RB3E_BUILDTAG " @ 0x%08X\n", (uint32_t)&RB3EBase); + + // print info about the exception + if (error == OS_ERR_DSI) { // data storage exception - we want to know the instruction (SRR0), address (DAR) and info (DSISR) + fmtExc("Data Error: %08X %08X %08X\n", ctx->srr0, dar, dsisr); + } else if (error == OS_ERR_ISI) { // instruction storage exception - we want to know the attempted addy (SRR0) + fmtExc("Instruction Error: %08X\n", ctx->srr0); + } else if (error == OS_ERR_ALIGNMENT) { // alignment exception - we want to know the instruction (SRR0) and address (DAR) + fmtExc("Alignment Error: %08X %08X\n", ctx->srr0, dar); + } else if (error == OS_ERR_PROGRAM) { // program exception - god help us all + fmtExc("Program Error: %08X probably??\n", ctx->srr0); + } else { + fmtExc("Error %i: %08X", error, ctx->srr0); + } + + // print out all the registers + for (int i = 0; i < 32; i++) { + if (i < 10) { + fmtExc("R%i:%08X ", i, ctx->gprs[i]); + } else { + fmtExc("%i:%08X ", i, ctx->gprs[i]); + } + // split every 4 registers + if ((i % 4) == 3) fmtExc("\n"); + } + fmtExc("LR:%08X PC:%08X CR:%08X MSR:%08X\n", ctx->lr, ctx->srr0, ctx->cr, ctx->srr1); + + // walk back the stack + fmtExc("Stack: "); + int iters = 0; + int iterating = 1; + uint32_t *sp = (uint32_t *)ctx->gprs[1]; + do { + // if the stack pointer isn't valid, break out + if (!IsAddressValid((uint32_t)sp)) { + iterating = 0; + break; + } + fmtExc("%08X ", sp[1]); + sp = (uint32_t *)sp[0]; + // start a new line after 4 stack addresses + // break after 9 or after the address is invalid + if (iters == 4) { + fmtExc("\n"); + } else if (iters == 9 || !IsAddressValid((uint32_t)sp)) { + iterating = 0; + break; + } + } while (iterating == 1); + + RB3E_PRINT("\n--== END EXCEPTION ==--\n"); + +#undef fmtExc + + GXColor bl = {127, 0, 40, 255}; + GXColor wh = {255, 255, 255, 255}; + OSFatal(wh, bl, RB3E_ExceptionText); +} + +int OSReadROMReplacement(void *dst, int size, int src) { + src -= 0x1FCF00; // offset of the ANSI font in the IPL + memcpy(dst, dolphin_osfatal_font + src, size); + return 1; +} + +void RB3E_InstallWiiExceptionHandler() { + // register our functions + POKE_B(OSFatal, PORT_OSFATAL); + POKE_B(OSSetErrorHandler, PORT_OSSETERRORHANDLER); + POKE_B(PPCHalt, PORT_PPCHALT); + POKE_B(OSReturnToMenu, PORT_OSRETURNTOMENU); + // clear instruction cache probably + uint32_t addr = (((uint32_t)OSFatal)) & ~0x1F; + DCFlushRange((void *)(addr), 0x80); + ICInvalidateRange((void *)(addr), 0x80); + // install the exception handler + OSSetErrorHandler(OS_ERR_MACHINE_CHECK, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_DSI, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_ISI, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_ALIGNMENT, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_PROGRAM, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_SYSTEM_CALL, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_IABR, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_SMI, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_THERMAL_INT, RB3E_ErrorOnException); + OSSetErrorHandler(OS_ERR_PROTECTION, RB3E_ErrorOnException); + // hack OSFatal-Halt routine to be more favorable + POKE_32(PORT_OSFATAL_HALT_X_OFFSET, LI(7, 30)); // x offset for text rendering + POKE_32(PORT_OSFATAL_HALT_Y_OFFSET, LI(8, 20)); // y offset for text rendering + POKE_32(PORT_SCREENREPORT_X_NEWLINE, 0x3bc4FFE1); // x offset for text rendering newline cutoff (-30) +#ifndef RB3E_WII_BANK8 + POKE_32(PORT_OSFATAL_HALT_OSREPORT, NOP); // call to OSReport + POKE_B(PORT_OSFATAL_HALT_PPCHALT, ReplaceHaltAfterFatal); // call to PPCHalt +#endif + // hook __OSReadROM + POKE_B(PORT_OSREADROM, OSReadROMReplacement); + RB3E_DEBUG("Installed exception handler!", NULL); +} + +#endif diff --git a/source/wii_usbhid.c b/source/wii_usbhid.c index df5893b..41fc808 100644 --- a/source/wii_usbhid.c +++ b/source/wii_usbhid.c @@ -13,9 +13,9 @@ int UsbWiiGetTypeHook(HIDDevice *device) { int r = 0; - if (device->vendorID == 0x12BA) + if (device->vid == 0x12BA) { // sony computer entertainment - switch (device->productID) + switch (device->pid) { case 0x0100: // gh guitar case 0x0200: // rb guitar @@ -31,7 +31,7 @@ int UsbWiiGetTypeHook(HIDDevice *device) { r = UsbWiiGetType(device); } - RB3E_DEBUG("Device connected: 0x%04x 0x%04x - type %i", device->vendorID, device->productID, r); + RB3E_DEBUG("Device connected: 0x%04x 0x%04x - type %i", device->vid, device->pid, r); return r; } diff --git a/source/xbox360.c b/source/xbox360.c index 15701a6..bd2d12f 100644 --- a/source/xbox360.c +++ b/source/xbox360.c @@ -6,10 +6,13 @@ #ifdef RB3E_XBOX #include +#include "rb3/XboxContent.h" #include "ppcasm.h" #include "ports.h" +#include "utilities.h" #include "rb3enhanced.h" #include "xbox360.h" +#include "version.h" static int HasRunDetection = 0; static int DetectionResult = 0; @@ -70,17 +73,24 @@ int RB3E_CreateThread(void *address, void *arg, int stack_size) int RB3E_RelaunchGame() { - // no idea if this actually works + // we should probably get the current exec name from the kernel XLaunchNewImage("default.xex", 0); return 0; } +void RB3E_FlushCache(void * address, unsigned int size) +{ + // TODO(Emma): clear dcache (and icache if possible) on xbox +} + static void CTHook(void *ThisApp, int argc, char **argv) { // initialise hooks for XeCrypt InitCryptoHooks(); // initialise hooks for input InitInputHooks(); + // initialise hooks for content + InitContentHooks(); // initialise hooks for liveless - this has to be done *after* systeminit POKE_BL(PORT_SYSTEMINIT_BLANK, &InitLivelessHooks); // launch game @@ -127,11 +137,15 @@ static void EnableSockpatch() } } +// defined in xbox360_exceptions.c +int RB3E_ExceptionHandler(EXCEPTION_RECORD *ExceptionRecord, void *EstablisherFrame, CONTEXT *ContextRecord, struct _DISPATCHER_CONTEXT *DispatcherContext); + BOOL APIENTRY DllMain(HANDLE hInstDLL, DWORD reason, LPVOID lpReserved) { if (reason == DLL_PROCESS_ATTACH) { - RB3E_DEBUG("DLL has been loaded"); + RB3E_DEBUG("DLL has been loaded", NULL); + POKE_32(PORT_MAINSEH, (DWORD)RB3E_ExceptionHandler); // Replace App::Run with App::RunWithoutDebugging POKE_BL(PORT_APP_RUN, PORT_APP_RUNNODEBUG); // Apply our hook diff --git a/source/xbox360_content.c b/source/xbox360_content.c new file mode 100644 index 0000000..cd53c71 --- /dev/null +++ b/source/xbox360_content.c @@ -0,0 +1,85 @@ +/* + RB3Enhanced - xbox360_content.c + Xbox 360 content and cache hooks. +*/ + +#ifdef RB3E_XBOX + +#include +#include +#include +#include "rb3/XboxCache.h" +#include "rb3/XboxContent.h" +#include "config.h" +#include "ports.h" +#include "utilities.h" +#include "rb3enhanced.h" +#include "xbox360.h" + +int hasLastMountedCache = 0; +CacheIDXbox lastMountedCache; + +int RB3E_DeleteSongCache() +{ + DWORD r; + XDEVICE_DATA deviceData; + if (hasLastMountedCache == 0) + { + RB3E_MSG("Tried to delete a song cache, but we don't have one...", NULL); + return 0; + } + if (XContentGetDeviceData(lastMountedCache.mContentData.DeviceID, &deviceData) == 0) + { + RB3E_DEBUG("Deleting song cache '%s' on \"%S\"", lastMountedCache.mContentData.szFileName, deviceData.wszFriendlyName); + r = XContentDelete(0xFF, &lastMountedCache.mContentData, NULL); + if (r != 0) + { + RB3E_MSG("Song cache delete failed (%i)", r); + return 0; + } + return 1; + } + else + { + RB3E_MSG("Tried to delete a song cache, but the device is gone...", NULL); + return 0; + } +} + +int CacheMgrXbox_MountAsync_Hook(CacheMgrXbox *thisCacheMgr, CacheIDXbox *cacheID, Cache **outCache, void *object) +{ + XDEVICE_DATA deviceData; + int r = CacheMgrXbox_MountAsync(thisCacheMgr, cacheID, outCache, object); + if ((strcmp(cacheID->mContentData.szFileName, "songcache") == 0 || strcmp(cacheID->mContentData.szFileName, "rbdxcache") == 0) && + XContentGetDeviceData(cacheID->mContentData.DeviceID, &deviceData) == 0) + { + RB3E_DEBUG("Song cache '%s' on \"%S\" mounted", cacheID->mContentData.szFileName, deviceData.wszFriendlyName); + memcpy(&lastMountedCache, cacheID, sizeof(CacheIDXbox)); + hasLastMountedCache = 1; + } + return r; +} + +XboxContent *XboxContentConstructHook(XboxContent *thisXboxContent, XCONTENT_CROSS_TITLE_DATA *titleData, int contentIndex, int padNum, char needsMount) +{ + XDEVICE_DATA deviceData; + XboxContent *r = XboxContentConstruct(thisXboxContent, titleData, contentIndex, padNum, needsMount); + if (config.ContentLogging && XContentGetDeviceData(titleData->DeviceID, &deviceData) == 0) + { + RB3E_DEBUG("Content \"%S\" (\"%S\", %08x/%08x/%s) = %s", + thisXboxContent->mTitleData.szDisplayName, + deviceData.wszFriendlyName, + thisXboxContent->mTitleData.dwTitleId, thisXboxContent->mTitleData.dwContentType, thisXboxContent->mTitleData.szFileName, + thisXboxContent->mRoot.buf + ); + } + return r; +} + +void InitContentHooks() +{ + HookFunction(PORT_CACHEMGRXBOX_MOUNTASYNC, &CacheMgrXbox_MountAsync, &CacheMgrXbox_MountAsync_Hook); + HookFunction(PORT_XBOXCONTENT_CONSTRUCTOR, &XboxContentConstruct, &XboxContentConstructHook); +} + +#endif // RB3E_XBOX diff --git a/source/xbox360_exceptions.c b/source/xbox360_exceptions.c new file mode 100644 index 0000000..ce67ae2 --- /dev/null +++ b/source/xbox360_exceptions.c @@ -0,0 +1,301 @@ +/* + RB3Enhanced - xbox360_exceptions.c + Low-level exception handling for RB3Enhanced on Xbox. +*/ +#ifdef RB3E_XBOX + +#include +#include +#include +#include "exceptions.h" +#include "ppcasm.h" +#include "ports.h" +#include "rb3enhanced.h" +#include "xbox360.h" +#include "version.h" + +static int hadException = 0; + +static EXCEPTION_RECORD exceptionRecord; +static CONTEXT exceptionContext; +static BYTE exceptionHandlerStack[0x2000]; + +static LPWSTR buttons[1] = {L"Return to Dashboard"}; +static MESSAGEBOX_RESULT result; +static XOVERLAPPED overlapped; +static void MessageBoxAndWait(wchar_t *text) +{ + if (XShowMessageBoxUI(XUSER_INDEX_ANY, L"RB3Enhanced Exception", text, 1, buttons, 0, XMB_ERRORICON, &result, &overlapped) == ERROR_IO_PENDING) + { + while (!XHasOverlappedIoCompleted(&overlapped)) + Sleep(50); + } +} + +static int didSuccessfullyWriteFile = 0; +static char exceptionOutputFilename[64]; + +static char *dataReadWriteNames[2] = { "read", "write" }; + +void DxRndSuspend(void *theDxRnd); + +static void GraphicalExceptionDisplay() +{ + wchar_t exceptionText[1024] = {0}; + wchar_t exceptionFmtBuffer[128]; + wcscat(exceptionText, L"Rock Band 3 has crashed. If this happens again, report this to the RB3Enhanced developers!\n\n"); +#define fmtExc(...) { wsprintfW(exceptionFmtBuffer, __VA_ARGS__); wcscat(exceptionText, exceptionFmtBuffer); } + + fmtExc(L"RB3Enhanced %S\n", RB3E_BUILDTAG); + + // print info about the exception + if (exceptionRecord.ExceptionCode == STATUS_ACCESS_VIOLATION) { + fmtExc(L"Data Error: %08X %08X %S\n", exceptionRecord.ExceptionAddress, exceptionRecord.ExceptionInformation[1], dataReadWriteNames[exceptionRecord.ExceptionInformation[0] & 1]); + } else if (exceptionRecord.ExceptionCode == STATUS_ILLEGAL_INSTRUCTION) { + fmtExc(L"Instruction Error: %08X\n", exceptionRecord.ExceptionAddress); + } else { + fmtExc(L"Error %X: %08X\n", exceptionRecord.ExceptionCode, exceptionRecord.ExceptionAddress); + } + + // print some core registers - not worth having them all... + fmtExc(L"R1:%08X R12:%08X R13:%08X\n", exceptionContext.Gpr1, exceptionContext.Gpr12, exceptionContext.Gpr13); + fmtExc(L"PC:%08X LR:%08X MSR:%08X\n", exceptionContext.Iar, exceptionContext.Lr, exceptionContext.Msr); + + // walk the stack + fmtExc(L"Stack: "); + { + int i = 0; + DWORD *stackPtr = (DWORD *)exceptionContext.Gpr1; + // sanity check: make sure our stack pointer is valid + if (MmIsAddressValid((DWORD)stackPtr)) + { + do { + DWORD lr; + stackPtr = (DWORD *)stackPtr[0]; + // break if the stack pointer isn't valid + if (!MmIsAddressValid((DWORD)stackPtr)) + break; + lr = stackPtr[-2]; + // break if the LR isn't valid + if (!MmIsAddressValid((DWORD)lr)) + break; + // print it out + fmtExc(L"%08X ", lr); + i++; + } while (i < 7); + } + } + fmtExc(L"\n"); + + if (didSuccessfullyWriteFile) + { + char *exceptionOutputFilenameCut = exceptionOutputFilename; + if (exceptionOutputFilenameCut[0] == 'R') // starts with RB3 so skip it + exceptionOutputFilenameCut += 3; + fmtExc(L"\nCrash dump saved to\n%S", exceptionOutputFilenameCut); + } + else + { + fmtExc(L"\nFailed to save crash dump."); + } +#undef fmtExc + // suspend TheDxRnd so XAM can render our error message + DxRndSuspend((void *)PORT_DXRND); + MessageBoxAndWait(exceptionText); +} + +static int openExceptionFile = -1; +static uint16_t totalStackwalks = 0; +static uint16_t totalMemchunks = 0; +static uint32_t totalMemchunkData = 0; + +static void WriteHeaderToFile(uint16_t num_stackwalk, uint16_t num_memchunks) +{ + rb3e_exception_header header; + header.magic = EXCEPTIONPACK_HEADER_XBOX; + header.version = 0; + strncpy(header.rb3e_buildtag, RB3E_BUILDTAG, sizeof(header.rb3e_buildtag)); + strncpy(header.rb3e_commit, RB3E_BUILDCOMMIT, sizeof(header.rb3e_commit)); + header.num_stackwalk = num_stackwalk; + header.num_memchunks = num_memchunks; + // always write to the start of the file + RB3E_WriteFile(openExceptionFile, 0, &header, sizeof(rb3e_exception_header)); +} + +static void WriteExceptionInfoToFile() +{ + // always comes after the header + RB3E_WriteFile(openExceptionFile, sizeof(rb3e_exception_header) + 0, &exceptionRecord, sizeof(exceptionRecord)); + RB3E_WriteFile(openExceptionFile, sizeof(rb3e_exception_header) + sizeof(EXCEPTION_RECORD), &exceptionContext, EXCEPTION_CONTEXT_SIZE); +} + +static void WriteMemChunkToFile(void *buffer, uint32_t length) +{ + rb3e_exception_memchunk chunkheader; + // make sure the address is valid as well as the total range + if (!MmIsAddressValid((DWORD)buffer) || !MmIsAddressValid((DWORD)buffer + length)) + return; + // craft the header + chunkheader.address = (uint32_t)buffer; + chunkheader.length = length; + // write the header to the file + // oh geez that offset calculation is getting long im starting to regret not just having fwrite equivalent but we stay silly + RB3E_WriteFile(openExceptionFile, sizeof(rb3e_exception_header) + sizeof(EXCEPTION_RECORD) + EXCEPTION_CONTEXT_SIZE + + (totalStackwalks * sizeof(DWORD)) + (totalMemchunks * sizeof(rb3e_exception_memchunk)) + totalMemchunkData, &chunkheader, sizeof(rb3e_exception_memchunk)); + // increment our pointers + totalMemchunks++; + // for the sake of simplicity write data here + RB3E_WriteFile(openExceptionFile, sizeof(rb3e_exception_header) + sizeof(EXCEPTION_RECORD) + EXCEPTION_CONTEXT_SIZE + + (totalStackwalks * sizeof(DWORD)) + (totalMemchunks * sizeof(rb3e_exception_memchunk)) + totalMemchunkData, buffer, length); + totalMemchunkData += length; +} + +static void WriteStackWalkToFile() +{ + DWORD *stackPtr = (DWORD *)exceptionContext.Gpr1; + // sanity check: make sure our stack pointer is valid + if (MmIsAddressValid((DWORD)stackPtr)) + { + do { + DWORD lr; + stackPtr = (DWORD *)stackPtr[0]; + // break if the stack pointer isn't valid + if (!MmIsAddressValid((DWORD)stackPtr)) + break; + lr = stackPtr[-2]; + // break if the LR isn't valid + if (!MmIsAddressValid((DWORD)lr)) + break; + // write the address out to the file + RB3E_WriteFile(openExceptionFile, sizeof(rb3e_exception_header) + sizeof(EXCEPTION_RECORD) + EXCEPTION_CONTEXT_SIZE + (totalStackwalks * sizeof(DWORD)), &lr, sizeof(DWORD)); + totalStackwalks++; + } while (TRUE); + } + // this code is ugly, but do that all again so we can actually dump out the contents of the stack as memchunks + stackPtr = (DWORD *)exceptionContext.Gpr1; + if (MmIsAddressValid((DWORD)stackPtr)) + { + DWORD totalStackBytes = 0; + DWORD lastPtr = (DWORD)stackPtr; + do { + DWORD length; + // set an upper limit for the amount of stack we can dump + if (totalStackBytes > 0x4000) + break; + stackPtr = (DWORD *)stackPtr[0]; + // break if the stack pointer isn't valid + if (!MmIsAddressValid((DWORD)stackPtr)) + break; + // calculate the length of this chunk of the stack and if its too long then dont bother + length = (DWORD)stackPtr - lastPtr; + if (length > 0x4000) + break; + // write the chunk out to the file + WriteMemChunkToFile((void *)lastPtr, length); + lastPtr = (DWORD)stackPtr; + totalStackBytes += length; + } while (TRUE); + } +} + +static void ExceptionWriteToFile() +{ + char exceptionFolder[32]; + char exceptionFilename[32]; + // get the path of rb3.ini, this is the folder we'll write the crash dump to + char *configFilePath = RB3E_GetRawfilePath("rb3.ini", 1); + // create the filename for the execption + SYSTEMTIME sysTime; + GetSystemTime(&sysTime); + sprintf(exceptionFilename, "crash_%04d%02d%02d_%02d%02d%02d.exc", sysTime.wYear, sysTime.wMonth, sysTime.wDay, sysTime.wHour, sysTime.wMinute, sysTime.wSecond); + // try to open the file + if (configFilePath == NULL) + { + // try saving to the game directory first + sprintf(exceptionOutputFilename, "GAME:\\%s", exceptionFilename); + openExceptionFile = RB3E_OpenFile(exceptionOutputFilename, 1); + // if that fails, try saving to the root of the HDD + if (openExceptionFile == -1 && RB3E_Mounted) + { + sprintf(exceptionOutputFilename, "RB3HDD:\\%s", exceptionFilename); + openExceptionFile = RB3E_OpenFile(exceptionOutputFilename, 1); + } + } + else + { + // save to the path of rb3.ini, so remove it from the filename path + char *rb3inipos = strstr(configFilePath, "rb3.ini"); + strncpy(exceptionFolder, configFilePath, rb3inipos - configFilePath); + sprintf(exceptionOutputFilename, "%s%s", exceptionFolder, exceptionFilename); + openExceptionFile = RB3E_OpenFile(exceptionOutputFilename, 1); + } + // if we couldn't create the new file then bail out + if (openExceptionFile == -1) + { + didSuccessfullyWriteFile = 0; + return; + } + // write the header and exception info + WriteHeaderToFile(0, 0); + WriteExceptionInfoToFile(); + // write the contents of the stack + WriteStackWalkToFile(); + // write memory chunks for valid memory addresses we have in GPRs + // TODO(Emma): dedupe + { + ULONGLONG *registers = &exceptionContext.Gpr0; + int i = 0; + for (i = 0; i < 32; i++) + { + DWORD reg32 = (DWORD)registers[i]; + if ((reg32 & 0xF0000000) == 0x40000000 || // heap memory + (reg32 & 0xF0000000) == 0x70000000 || // stack memory + (reg32 & 0xF0000000) == 0x80000000 || // 64K executable memory + (reg32 & 0xF0000000) == 0x90000000) // 4K executable memory + { + if (MmIsAddressValid(reg32 & 0xFFFFFF00)) + { + WriteMemChunkToFile((void *)(reg32 & 0xFFFFFF00), 0x200); + } + } + } + } + // TODO(Emma): write heap debugging information, and other useful trinkets + // rewrite the header with the total stack walked + WriteHeaderToFile(totalStackwalks, totalMemchunks); + didSuccessfullyWriteFile = 1; + // close the file and flush to disk + RB3E_CloseFile(openExceptionFile); +} + +static void ExceptionHandler() +{ + ExceptionWriteToFile(); + GraphicalExceptionDisplay(); + // XamLoaderTerminateTitle + ((void(*)(void))PORT_XAMLOADERTERMINATETITLE)(); +} + +int RB3E_ExceptionHandler(EXCEPTION_RECORD *ExceptionRecord, void *EstablisherFrame, CONTEXT *ContextRecord, struct _DISPATCHER_CONTEXT *DispatcherContext) +{ + POKE_B(&DxRndSuspend, PORT_DXRND_SUSPEND); + + // if we've already had an exception, try to terminate title without displaying to the user + if (hadException) + { + ContextRecord->Iar = PORT_XAMLOADERTERMINATETITLE; + return 0; + } + + // back up our exception record and stack + memcpy(&exceptionRecord, ExceptionRecord, sizeof(EXCEPTION_RECORD)); + memcpy(&exceptionContext, ContextRecord, sizeof(CONTEXT)); + // change the thread to go to our exception handler + ContextRecord->Iar = (DWORD)ExceptionHandler; + // we change the stack too to avoid clobbering the original stack and make it viable to dump info from + ContextRecord->Gpr1 = (DWORD)exceptionHandlerStack + sizeof(exceptionHandlerStack); + hadException = 1; + return 0; // continue execution +} + +#endif diff --git a/source/xbox360_files.c b/source/xbox360_files.c index 97cdbdb..ccf37f1 100644 --- a/source/xbox360_files.c +++ b/source/xbox360_files.c @@ -105,7 +105,10 @@ int RB3E_FileExists(char *filename) } int RB3E_OpenFile(char *filename, char readWrite) { - HANDLE handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + DWORD desiredAccess = readWrite ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ; + DWORD shareMode = readWrite ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : FILE_SHARE_READ; + DWORD creationDisposition = readWrite ? OPEN_ALWAYS : OPEN_EXISTING; + HANDLE handle = CreateFile(filename, desiredAccess, shareMode, NULL, creationDisposition, 0, NULL); if (handle == INVALID_HANDLE_VALUE) return -1; return (int)handle; @@ -122,6 +125,14 @@ int RB3E_ReadFile(int file, int offset, void *buffer, int size) return -1; return readBytes; } +int RB3E_WriteFile(int file, int offset, void *buffer, int size) +{ + DWORD wroteBytes; + SetFilePointer((HANDLE)file, offset, NULL, FILE_BEGIN); + if (WriteFile((HANDLE)file, buffer, size, &wroteBytes, NULL) == FALSE) + return -1; + return wroteBytes; +} void RB3E_CloseFile(int file) { XCloseHandle((HANDLE)file); diff --git a/source/xbox360_net.c b/source/xbox360_net.c index 05ad229..0614087 100644 --- a/source/xbox360_net.c +++ b/source/xbox360_net.c @@ -67,7 +67,7 @@ unsigned int RB3E_GetGatewayIP() XnpRouteEntry routes[2] = {0}; int size = sizeof(routes); // XNET_OPTID_ROUTE_ENTRY - int ret = XNetGetOpt(0x1392, routes, &size); + int ret = XNetGetOpt(0x1392, (BYTE *)routes, &size); if (ret == 0) { return routes[1].interface_addr; @@ -106,7 +106,7 @@ int RB3E_SetSendTimeout(int socket, int timeout_ms) int RB3E_SetTimeout(int socket, int timeout_ms) { int r = RB3E_SetRecvTimeout(socket, timeout_ms); - r = RB3E_SetRecvTimeout(socket, timeout_ms); + r = RB3E_SetSendTimeout(socket, timeout_ms); return r; } diff --git a/source/xbox_keyboard.c b/source/xbox_keyboard.c new file mode 100644 index 0000000..5bd20b9 --- /dev/null +++ b/source/xbox_keyboard.c @@ -0,0 +1,131 @@ +#ifdef RB3E_XBOX +#include +#include "rb3/Data.h" +#include "ports.h" + +// Reimplementation of DC3's TranslateVK +// This function translates the system XInput VirtualKey value into Harmonix's keycode values +int TranslateVK(DWORD virtualKey, char shift) +{ + // ASCII range + // Honestly not sure why this range is even mapped, at these ranges the Unicode translation should be handling it + if (virtualKey >= 0x41 && virtualKey <= 0x5a) { + if (!shift) { + return virtualKey; + } else { + return tolower(virtualKey); + } + } + + // Function key range + if (virtualKey >= VK_F1 && virtualKey <= VK_F12) { + return virtualKey + 0x121; + } + + // Other keycode mappings + switch (virtualKey) { + case VK_HOME: + return 0x138; + case VK_CAPITAL: + return 0x122; + case VK_BACK: + return 0x08; + case VK_TAB: + return 0x09; + case VK_RETURN: + return 0x0a; + case VK_PAUSE: + return 0x12d; + case VK_ESCAPE: + return 0x12e; + case VK_PRIOR: + return 0x13a; + case VK_NEXT: + return 0x13b; + case VK_END: + return 0x139; + case VK_PRINT: + return 0x12c; + case VK_LEFT: + return 0x140; + case VK_UP: + return 0x142; + case VK_RIGHT: + return 0x141; + case VK_DOWN: + return 0x143; + case VK_INSERT: + return 0x136; + case VK_DELETE: + return 0x137; + case VK_NUMLOCK: + return 0x123; + case VK_SCROLL: + return 0x124; + } + + return virtualKey; +} + + +// Reimplementation of KeyboardPoll from DC3 +void KeyboardPoll() { + XINPUT_KEYSTROKE keystroke; + DWORD keystrokeResult; + static DataNode msgNodes[6] = {0}; + static DataArray keyMsg = {0}; + static DataNode retNode; + Symbol uiSym; + Symbol keySym; + char unicodeChar[4]; + int keyCode; + + keystrokeResult = XInputGetKeystroke(XUSER_INDEX_ANY, 2, &keystroke); + if (keystrokeResult == ERROR_SUCCESS && (keystroke.Flags & XINPUT_KEYSTROKE_KEYUP) == 0) { + WideCharToMultiByte(0, 0, &keystroke.Unicode, 1, unicodeChar, 2, 0x0, 0x0); + if (unicodeChar[0] == '\0' || unicodeChar[0] < ' ' || unicodeChar[0] > '~') { + keyCode = TranslateVK(keystroke.VirtualKey, (keystroke.Flags & XINPUT_KEYSTROKE_SHIFT) > 0); + } else { + keyCode = (int)unicodeChar[0]; + } + + keyMsg.mNodes = (DataNodes *)&msgNodes; + keyMsg.mNodeCount = 6; + keyMsg.mRefCount = 1; + keyMsg.mFile = *(Symbol *)PORT_NULLSYMBOL; + + SymbolConstruct(&uiSym, "ui"); + SymbolConstruct(&keySym, "key"); + + // Construct a command array to do this: + // {ui key $key $shift $ctrl $alt} + + // ui object + msgNodes[0].type = SYMBOL; + msgNodes[0].value.string = uiSym.sym; + + // key handler + msgNodes[1].type = SYMBOL; + msgNodes[1].value.string = keySym.sym; + + // key + msgNodes[2].type = INT_VALUE; + msgNodes[2].value.intVal = keyCode; + + // shift + msgNodes[3].type = INT_VALUE; + msgNodes[3].value.intVal = (keystroke.Flags & XINPUT_KEYSTROKE_SHIFT) > 0; + + // ctrl + msgNodes[4].type = INT_VALUE; + msgNodes[4].value.intVal = (keystroke.Flags & XINPUT_KEYSTROKE_CTRL) > 0; + + // alt + msgNodes[5].type = INT_VALUE; + msgNodes[5].value.intVal = (keystroke.Flags & XINPUT_KEYSTROKE_ALT) > 0; + + DataArrayExecute(&retNode, &keyMsg); + + } +} +#endif \ No newline at end of file