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!)
+
+
+
+
+
+ Loading... this may take a while!
+
+
+
+
+
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