From 6e0fb4080f171907a6cfbad27e69335b0e7502c1 Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 1 Oct 2021 17:30:47 +0200 Subject: [PATCH] first commit --- .github/workflows/ci.yml | 58 ++++ .github/workflows/pr.yml | 17 ++ .gitignore | 11 + Dockerfile | 6 + Makefile | 137 ++++++++++ README.md | 45 +++ src/ConfigUtils.cpp | 362 ++++++++++++++++++++++++ src/ConfigUtils.h | 11 + src/DrawUtils.cpp | 252 +++++++++++++++++ src/DrawUtils.h | 74 +++++ src/globals.cpp | 19 ++ src/globals.h | 35 +++ src/main.cpp | 577 +++++++++++++++++++++++++++++++++++++++ src/utils/logger.h | 32 +++ 14 files changed, 1636 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pr.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/ConfigUtils.cpp create mode 100644 src/ConfigUtils.h create mode 100644 src/DrawUtils.cpp create mode 100644 src/DrawUtils.h create mode 100644 src/globals.cpp create mode 100644 src/globals.h create mode 100644 src/main.cpp create mode 100644 src/utils/logger.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b59fa68 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,58 @@ +name: CI-Release + +on: + push: + branches: + - master + +jobs: + build-binary: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.wps" + deploy-binary: + needs: build-binary + runs-on: ubuntu-18.04 + steps: + - name: Get environment variables + id: get_repository_name + run: | + echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//") >> $GITHUB_ENV + echo DATETIME=$(echo $(date '+%Y%m%d-%H%M%S')) >> $GITHUB_ENV + - uses: actions/download-artifact@master + with: + name: binary + path: wiiu/plugins + - name: zip artifact + run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip wiiu + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + draft: false + prerelease: true + body: | + Not a stable release: + ${{ github.event.head_commit.message }} + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_content_type: application/unknown \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..f21e109 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,17 @@ +name: CI-PR + +on: [pull_request] + +jobs: + build-binary: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.wps" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c56927f --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +build/* +*.mod +sysapp.layout +sysapp.cbp +src/filelist.h +cmake-build-debug/CMakeCache.txt +cmake-build-debug/ +.idea/ +*.wps +*.elf +CMakeLists.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..011d1f3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM wiiuenv/devkitppc:20210920 + +COPY --from=wiiuenv/wiiupluginsystem:20211001 /artifacts $DEVKITPRO +COPY --from=wiiuenv/libmappedmemory:20210924 /artifacts $DEVKITPRO + +WORKDIR project \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..871f481 --- /dev/null +++ b/Makefile @@ -0,0 +1,137 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wups/share/wups_rules + +WUT_ROOT := $(DEVKITPRO)/wut +WUMS_ROOT := $(DEVKITPRO)/wums + +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := regionfree +BUILD := build +SOURCES := src +DATA := data +INCLUDES := src + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -g -Wall -O0 -ffunction-sections \ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__ + +CXXFLAGS := $(CFLAGS) + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libmappedmemory.ld $(WUPSSPECS) + +LIBS := -lwut -lwups -lmappedmemory -lfreetype -lbz2 -lz -lpng + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUPS_ROOT) $(WUMS_ROOT) + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#------------------------------------------------------------------------------- + export LD := $(CC) +#------------------------------------------------------------------------------- +else +#------------------------------------------------------------------------------- + export LD := $(CXX) +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) -I$(DEVKITPRO)/portlibs/ppc/include/freetype2 + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).wps + +$(OUTPUT).wps : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..b652f49 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# Region Free Plugin + +This plugin allows you to launch an title of an other region and / or force a language of an title. + +## Installation + +Load the plugin with the [Wii U Plugin System Backend](https://github.com/wiiu-env/WiiUPluginLoaderBackend) by placing it on the SD Card into the +directory `sd:/wiiu/plugin/` + +At the first launch with the plugin open the config menu (press ZL, DPAD Down and Minus on the gamepad) and set your default language for each region. + +## Usage + +The plugin has a built in auto detection, so in most cases it just works out of the box. + +Via the plugin config menu (press ZL, DPAD Down and Minus on the gamepad) you can configurate the plugin. The avaible options are the following: + +- **Auto Detection**: Enabled/Disabled the auto detection of the region/language. When you disable it, you need/can set the region and language for a title on each title start. Enabled by default. +- **Prefer System Settings For Own Region**: Forces the region and language of your console when starting an title or your region (Ignoring "Default Language for Region"). Enabled by default. +- **Default Language for EUR**: Sets the default language for EUR titles. Set to English by default. +- **Default Language for USA**: Sets the default language for USA titles. Set to English by default. + +If the auto detection fails, the user needs to enter a region/language on the title boot. + +The plugin keeps tracks the region/language the user has selected for an title. + +Scenario: + - The User has disabled the auto detection and booted a EUR version of Mario Kart 8 in German on their US console. + - The User then enables the auto detection and set the default language for EUR titles to English. + - The EUR version of Mario Kart 8 will still boot in German. To change the language the user has to disable the auto detection and reboot the title. + +## Building using the Dockerfile + +It's possible to use a docker image for building. This way you don't need anything installed on your host system. + +``` +# Build docker image (only needed once) +docker build . -t regionfree_plugin-builder + +# make +docker run -it --rm -v ${PWD}:/project regionfree_plugin-builder make + +# make clean +docker run -it --rm -v ${PWD}:/project regionfree_plugin-builder make clean +``` diff --git a/src/ConfigUtils.cpp b/src/ConfigUtils.cpp new file mode 100644 index 0000000..8750265 --- /dev/null +++ b/src/ConfigUtils.cpp @@ -0,0 +1,362 @@ +#include "ConfigUtils.h" + +#include "utils/logger.h" +#include "DrawUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "globals.h" + +#define COLOR_BACKGROUND Color(238, 238, 238, 255) +#define COLOR_TEXT Color(51, 51, 51, 255) +#define COLOR_TEXT2 Color(72, 72, 72, 255) +#define COLOR_DISABLED Color(255, 0, 0, 255) +#define COLOR_BORDER Color(204, 204, 204, 255) +#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4FF) +#define COLOR_WHITE Color(0xFFFFFFFF) +#define COLOR_BLACK Color(0, 0, 0, 255) + +#define MAX_BUTTONS_ON_SCREEN 8 + +static uint32_t remapWiiMoteButtons(uint32_t buttons) { + uint32_t conv_buttons = 0; + + if (buttons & WPAD_BUTTON_LEFT) + conv_buttons |= VPAD_BUTTON_LEFT; + + if (buttons & WPAD_BUTTON_RIGHT) + conv_buttons |= VPAD_BUTTON_RIGHT; + + if (buttons & WPAD_BUTTON_DOWN) + conv_buttons |= VPAD_BUTTON_DOWN; + + if (buttons & WPAD_BUTTON_UP) + conv_buttons |= VPAD_BUTTON_UP; + + if (buttons & WPAD_BUTTON_PLUS) + conv_buttons |= VPAD_BUTTON_PLUS; + + if (buttons & WPAD_BUTTON_B) + conv_buttons |= VPAD_BUTTON_B; + + if (buttons & WPAD_BUTTON_A) + conv_buttons |= VPAD_BUTTON_A; + + if (buttons & WPAD_BUTTON_MINUS) + conv_buttons |= VPAD_BUTTON_MINUS; + + if (buttons & WPAD_BUTTON_HOME) + conv_buttons |= VPAD_BUTTON_HOME; + + return conv_buttons; +} + +static uint32_t remapClassicButtons(uint32_t buttons) { + uint32_t conv_buttons = 0; + + if (buttons & WPAD_CLASSIC_BUTTON_LEFT) + conv_buttons |= VPAD_BUTTON_LEFT; + + if (buttons & WPAD_CLASSIC_BUTTON_RIGHT) + conv_buttons |= VPAD_BUTTON_RIGHT; + + if (buttons & WPAD_CLASSIC_BUTTON_DOWN) + conv_buttons |= VPAD_BUTTON_DOWN; + + if (buttons & WPAD_CLASSIC_BUTTON_UP) + conv_buttons |= VPAD_BUTTON_UP; + + if (buttons & WPAD_CLASSIC_BUTTON_PLUS) + conv_buttons |= VPAD_BUTTON_PLUS; + + if (buttons & WPAD_CLASSIC_BUTTON_X) + conv_buttons |= VPAD_BUTTON_X; + + if (buttons & WPAD_CLASSIC_BUTTON_Y) + conv_buttons |= VPAD_BUTTON_Y; + + if (buttons & WPAD_CLASSIC_BUTTON_B) + conv_buttons |= VPAD_BUTTON_B; + + if (buttons & WPAD_CLASSIC_BUTTON_A) + conv_buttons |= VPAD_BUTTON_A; + + if (buttons & WPAD_CLASSIC_BUTTON_MINUS) + conv_buttons |= VPAD_BUTTON_MINUS; + + if (buttons & WPAD_CLASSIC_BUTTON_HOME) + conv_buttons |= VPAD_BUTTON_HOME; + + if (buttons & WPAD_CLASSIC_BUTTON_ZR) + conv_buttons |= VPAD_BUTTON_ZR; + + if (buttons & WPAD_CLASSIC_BUTTON_ZL) + conv_buttons |= VPAD_BUTTON_ZL; + + if (buttons & WPAD_CLASSIC_BUTTON_R) + conv_buttons |= VPAD_BUTTON_R; + + if (buttons & WPAD_CLASSIC_BUTTON_L) + conv_buttons |= VPAD_BUTTON_L; + + return conv_buttons; +} + +void ConfigUtils::displayMenu() { + bool redraw = true; + uint32_t buttonsTriggered; + uint32_t buttonsReleased; + + VPADStatus vpad_data{}; + VPADReadError vpad_error; + KPADStatus kpad_data{}; + int32_t kpad_error; + + auto selectedBtn = 0; + + std::map region_map{ + {MCP_REGION_JAPAN, "Japan"}, + {MCP_REGION_USA, "USA"}, + {MCP_REGION_EUROPE, "Europe"}, + }; + + std::map region_map_to_index{ + {MCP_REGION_JAPAN, 0}, + {MCP_REGION_USA, 1}, + {MCP_REGION_EUROPE, 2}, + }; + + + std::map region_index_to_map{ + {0, MCP_REGION_JAPAN}, + {1, MCP_REGION_USA}, + {2, MCP_REGION_EUROPE}, + }; + + auto curSelectedRegion = gCurrentProductArea; + DEBUG_FUNCTION_LINE("Current %d", curSelectedRegion); + + std::map lang_map{ + {LANG_JAPANESE, "Japanese"}, + {LANG_ENGLISH, "English"}, + {LANG_FRANCAIS, "Francais"}, + {LANG_DEUTSCH, "Deutsch"}, + {LANG_ITALIANO, "Italiano"}, + {LANG_ESPANOL, "Espanol"}, + {LANG_NEDERLANDS, "Nederlands"}, + {LANG_PORTUGUES, "Portugues"}, + {LANG_RUSSKI, "Russki"}, + }; + std::map lang_map_to_index{ + {LANG_JAPANESE, 0}, + {LANG_ENGLISH, 1}, + {LANG_FRANCAIS, 2}, + {LANG_DEUTSCH, 3}, + {LANG_ITALIANO, 4}, + {LANG_ESPANOL, 5}, + {LANG_NEDERLANDS, 6}, + {LANG_PORTUGUES, 7}, + {LANG_RUSSKI, 8}, + }; + std::map lang_index_to_map{ + {0, LANG_JAPANESE}, + {1, LANG_ENGLISH}, + {2, LANG_FRANCAIS}, + {3, LANG_DEUTSCH}, + {4, LANG_ITALIANO}, + {5, LANG_ESPANOL}, + {6, LANG_NEDERLANDS}, + {7, LANG_PORTUGUES}, + {8, LANG_RUSSKI}, + }; + + auto curSelectedLanguage = gCurrentLanguage; + + int32_t curRegionIndex = region_map_to_index[curSelectedRegion]; + int32_t curLangIndex = lang_map_to_index[curSelectedLanguage]; + + while (true) { + buttonsTriggered = 0; + buttonsReleased = 0; + + VPADRead(VPAD_CHAN_0, &vpad_data, 1, &vpad_error); + if (vpad_error == VPAD_READ_SUCCESS) { + buttonsTriggered = vpad_data.trigger; + buttonsReleased = vpad_data.release; + } + + for (int i = 0; i < 4; i++) { + if (KPADReadEx((KPADChan) i, &kpad_data, 1, &kpad_error) > 0) { + if (kpad_error == KPAD_ERROR_OK) { + if (kpad_data.extensionType == WPAD_EXT_CORE || kpad_data.extensionType == WPAD_EXT_NUNCHUK) { + buttonsTriggered |= remapWiiMoteButtons(kpad_data.trigger); + buttonsReleased |= remapWiiMoteButtons(kpad_data.release); + } else { + buttonsTriggered |= remapClassicButtons(kpad_data.classic.trigger); + buttonsReleased |= remapClassicButtons(kpad_data.classic.release); + } + } + } + } + + if (buttonsTriggered & VPAD_BUTTON_HOME) { + break; + } + + if (buttonsTriggered & VPAD_BUTTON_DOWN) { + selectedBtn++; + redraw = true; + } else if (buttonsTriggered & VPAD_BUTTON_UP) { + selectedBtn--; + redraw = true; + } + if (selectedBtn < 0) { + selectedBtn = 0; + } + if (selectedBtn >= 1) { + selectedBtn = 1; + } + + if (selectedBtn == 0) { + if (buttonsTriggered & VPAD_BUTTON_LEFT) { + curRegionIndex--; + redraw = true; + } else if (buttonsTriggered & VPAD_BUTTON_RIGHT) { + curRegionIndex++; + redraw = true; + } + if (curRegionIndex < 0) { + curRegionIndex = 0; + } + if (curRegionIndex >= region_map.size()) { + curRegionIndex = region_map.size() - 1; + } + gCurrentProductArea = region_index_to_map[curRegionIndex]; + curSelectedRegion = gCurrentProductArea; + } else if (selectedBtn == 1) { + if (buttonsTriggered & VPAD_BUTTON_LEFT) { + curLangIndex--; + redraw = true; + } else if (buttonsTriggered & VPAD_BUTTON_RIGHT) { + curLangIndex++; + redraw = true; + } + if (curLangIndex < 0) { + curLangIndex = 0; + } + if (curLangIndex >= lang_map.size()) { + curLangIndex = lang_map.size() - 1; + } + gCurrentLanguage = lang_index_to_map[curLangIndex]; + curSelectedLanguage = gCurrentLanguage; + } + + if (redraw) { + DrawUtils::beginDraw(); + DrawUtils::clear(COLOR_BACKGROUND); + + DrawUtils::setFontColor(COLOR_TEXT); + + std::string headline = "Region Free Plugin"; + + // draw top bar + DrawUtils::setFontSize(24); + DrawUtils::print(16, 6 + 24, headline.c_str()); + DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK); + DrawUtils::setFontSize(18); + + // draw buttons + uint32_t index = 8 + 24 + 8 + 4; + + DEBUG_FUNCTION_LINE("%d", selectedBtn); + + DrawUtils::setFontColor(COLOR_TEXT); + DrawUtils::setFontSize(24); + + if (selectedBtn == 0) { + DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED); + } else { + DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER); + } + + DrawUtils::print(16 * 2, index + 8 + 24, "Region"); + DrawUtils::print(SCREEN_WIDTH - 16 * 2, index + 8 + 24, region_map[curSelectedRegion], true); + index += 42 + 8; + + if (selectedBtn == 1) { + DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED); + } else { + DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER); + } + DrawUtils::print(16 * 2, index + 8 + 24, "Language"); + + DrawUtils::print(SCREEN_WIDTH - 16 * 2, index + 8 + 24, lang_map[curSelectedLanguage], true); + + index += 42 + 8; + + // draw bottom bar + DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK); + DrawUtils::setFontSize(18); + DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate "); + DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue001 Back", true); + + DrawUtils::endDraw(); + redraw = false; + } + } + DrawUtils::beginDraw(); + DrawUtils::clear(COLOR_BLACK); + DrawUtils::endDraw(); + +} + +void ConfigUtils::openConfigMenu() { + OSScreenInit(); + + uint32_t screen_buf0_size = OSScreenGetBufferSizeEx(SCREEN_TV); + uint32_t screen_buf1_size = OSScreenGetBufferSizeEx(SCREEN_DRC); + void *screenbuffer0 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size, 0x100); + void *screenbuffer1 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf1_size, 0x100); + + if (!screenbuffer0 || !screenbuffer1) { + DEBUG_FUNCTION_LINE("Failed to alloc buffers"); + goto error_exit; + } + + OSScreenSetBufferEx(SCREEN_TV, screenbuffer0); + OSScreenSetBufferEx(SCREEN_DRC, screenbuffer1); + + OSScreenEnableEx(SCREEN_TV, 1); + OSScreenEnableEx(SCREEN_DRC, 1); + + // Clear screens + OSScreenClearBufferEx(SCREEN_TV, 0); + OSScreenClearBufferEx(SCREEN_DRC, 0); + + // Flip buffers + OSScreenFlipBuffersEx(SCREEN_TV); + OSScreenFlipBuffersEx(SCREEN_DRC); + + DrawUtils::initBuffers(screenbuffer0, screen_buf0_size, screenbuffer1, screen_buf1_size); + DrawUtils::initFont(); + + displayMenu(); + + DrawUtils::deinitFont(); + + error_exit: + + if (screenbuffer0) { + MEMFreeToMappedMemory(screenbuffer0); + } + + if (screenbuffer1) { + MEMFreeToMappedMemory(screenbuffer1); + } +} diff --git a/src/ConfigUtils.h b/src/ConfigUtils.h new file mode 100644 index 0000000..d908b28 --- /dev/null +++ b/src/ConfigUtils.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class ConfigUtils { +public: + static void openConfigMenu(); + +private: + static void displayMenu(); +}; diff --git a/src/DrawUtils.cpp b/src/DrawUtils.cpp new file mode 100644 index 0000000..49f8537 --- /dev/null +++ b/src/DrawUtils.cpp @@ -0,0 +1,252 @@ +#include "DrawUtils.h" + +#include +#include +#include +#include FT_FREETYPE_H + +// buffer width +#define TV_WIDTH 0x500 +#define DRC_WIDTH 0x380 + +bool DrawUtils::isBackBuffer; + +uint8_t *DrawUtils::tvBuffer = nullptr; +uint32_t DrawUtils::tvSize = 0; +uint8_t *DrawUtils::drcBuffer = nullptr; +uint32_t DrawUtils::drcSize = 0; + +// Don't put those into the clase or we have to include ft everywhere +static FT_Library ft_lib = nullptr; +static FT_Face ft_face = nullptr; +static Color font_col(0xFFFFFFFF); + +void DrawUtils::initBuffers(void *tvBuffer_, uint32_t tvSize_, void *drcBuffer_, uint32_t drcSize_) { + DrawUtils::tvBuffer = (uint8_t *) tvBuffer_; + DrawUtils::tvSize = tvSize_; + DrawUtils::drcBuffer = (uint8_t *) drcBuffer_; + DrawUtils::drcSize = drcSize_; +} + +void DrawUtils::beginDraw() { + uint32_t pixel = *(uint32_t *) tvBuffer; + + // check which buffer is currently used + OSScreenPutPixelEx(SCREEN_TV, 0, 0, 0xABCDEF90); + if (*(uint32_t *) tvBuffer == 0xABCDEF90) { + isBackBuffer = false; + } else { + isBackBuffer = true; + } + + // restore the pixel we used for checking + *(uint32_t *) tvBuffer = pixel; +} + +void DrawUtils::endDraw() { + // OSScreenFlipBuffersEx already flushes the cache? + // DCFlushRange(tvBuffer, tvSize); + // DCFlushRange(drcBuffer, drcSize); + + OSScreenFlipBuffersEx(SCREEN_DRC); + OSScreenFlipBuffersEx(SCREEN_TV); +} + +void DrawUtils::clear(Color col) { + OSScreenClearBufferEx(SCREEN_TV, col.color); + OSScreenClearBufferEx(SCREEN_DRC, col.color); +} + +void DrawUtils::drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + float opacity = a / 255.0f; + + // put pixel in the drc buffer + uint32_t i = (x + y * DRC_WIDTH) * 4; + if (i + 3 < drcSize / 2) { + if (isBackBuffer) { + i += drcSize / 2; + } + if (a == 0xFF) { + drcBuffer[i] = r; + drcBuffer[i + 1] = g; + drcBuffer[i + 2] = b; + } else { + drcBuffer[i] = r * opacity + drcBuffer[i] * (1 - opacity); + drcBuffer[i + 1] = g * opacity + drcBuffer[i + 1] * (1 - opacity); + drcBuffer[i + 2] = b * opacity + drcBuffer[i + 2] * (1 - opacity); + } + } + + // scale and put pixel in the tv buffer + for (uint32_t yy = (y * 1.5); yy < ((y * 1.5) + 1); yy++) { + for (uint32_t xx = (x * 1.5); xx < ((x * 1.5) + 1); xx++) { + uint32_t i = (xx + yy * TV_WIDTH) * 4; + if (i + 3 < tvSize / 2) { + if (isBackBuffer) { + i += tvSize / 2; + } + if (a == 0xFF) { + tvBuffer[i] = r; + tvBuffer[i + 1] = g; + tvBuffer[i + 2] = b; + } else { + tvBuffer[i] = r * opacity + tvBuffer[i] * (1 - opacity); + tvBuffer[i + 1] = g * opacity + tvBuffer[i + 1] * (1 - opacity); + tvBuffer[i + 2] = b * opacity + tvBuffer[i + 2] * (1 - opacity); + } + } + } + } +} + +void DrawUtils::drawRectFilled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, Color col) { + for (uint32_t yy = y; yy < y + h; yy++) { + for (uint32_t xx = x; xx < x + w; xx++) { + drawPixel(xx, yy, col); + } + } +} + +void DrawUtils::drawRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, Color col) { + drawRectFilled(x, y, w, borderSize, col); + drawRectFilled(x, y + h - borderSize, w, borderSize, col); + drawRectFilled(x, y, borderSize, h, col); + drawRectFilled(x + w - borderSize, y, borderSize, h, col); +} + +void DrawUtils::drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32_t target_height, const uint8_t *data) { + if (data[0] != 'B' || data[1] != 'M') { + // invalid header + return; + } + + uint32_t dataPos = __builtin_bswap32(*(uint32_t *) &(data[0x0A])); + uint32_t width = __builtin_bswap32(*(uint32_t *) &(data[0x12])); + uint32_t height = __builtin_bswap32(*(uint32_t *) &(data[0x16])); + + if (dataPos == 0) { + dataPos = 54; + } + + data += dataPos; + + // TODO flip image since bitmaps are stored upside down + + for (uint32_t yy = y; yy < y + target_height; yy++) { + for (uint32_t xx = x; xx < x + target_width; xx++) { + uint32_t i = (((xx - x) * width / target_width) + ((yy - y) * height / target_height) * width) * 3; + drawPixel(xx, yy, data[i + 2], data[i + 1], data[i], 0xFF); + } + } +} + +void DrawUtils::initFont() { + void *font = nullptr; + uint32_t size = 0; + OSGetSharedData(OS_SHAREDDATATYPE_FONT_STANDARD, 0, &font, &size); + + if (font && size) { + FT_Init_FreeType(&ft_lib); + FT_New_Memory_Face(ft_lib, (FT_Byte *) font, size, 0, &ft_face); + } +} + +void DrawUtils::deinitFont() { + FT_Done_Face(ft_face); + FT_Done_FreeType(ft_lib); +} + +void DrawUtils::setFontSize(uint32_t size) { + FT_Set_Pixel_Sizes(ft_face, 0, size); +} + +void DrawUtils::setFontColor(Color col) { + font_col = col; +} + +static void draw_freetype_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) { + FT_Int i, j, p, q; + FT_Int x_max = x + bitmap->width; + FT_Int y_max = y + bitmap->rows; + + for (i = x, p = 0; i < x_max; i++, p++) { + for (j = y, q = 0; j < y_max; j++, q++) { + if (i < 0 || j < 0 || i >= SCREEN_WIDTH || j >= SCREEN_HEIGHT) { + continue; + } + + float opacity = bitmap->buffer[q * bitmap->pitch + p] / 255.0f; + DrawUtils::drawPixel(i, j, font_col.r, font_col.g, font_col.b, font_col.a * opacity); + } + } +} + +void DrawUtils::print(uint32_t x, uint32_t y, const char *string, bool alignRight) { + auto *buffer = new wchar_t[strlen(string) + 1]; + + size_t num = mbstowcs(buffer, string, strlen(string)); + if (num > 0) { + buffer[num] = 0; + } else { + wchar_t *tmp = buffer; + while ((*tmp++ = *string++)); + } + + print(x, y, buffer, alignRight); + delete[] buffer; +} + +void DrawUtils::print(uint32_t x, uint32_t y, const wchar_t *string, bool alignRight) { + FT_GlyphSlot slot = ft_face->glyph; + FT_Vector pen = {(int) x, (int) y}; + + if (alignRight) { + pen.x -= getTextWidth(string); + } + + for (; *string; string++) { + uint32_t charcode = *string; + + if (charcode == '\n') { + pen.y += ft_face->size->metrics.height >> 6; + pen.x = x; + continue; + } + + FT_Load_Glyph(ft_face, FT_Get_Char_Index(ft_face, charcode), FT_LOAD_DEFAULT); + FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL); + + draw_freetype_bitmap(&slot->bitmap, pen.x + slot->bitmap_left, pen.y - slot->bitmap_top); + pen.x += slot->advance.x >> 6; + } +} + +uint32_t DrawUtils::getTextWidth(const char *string) { + auto *buffer = new wchar_t[strlen(string) + 1]; + + size_t num = mbstowcs(buffer, string, strlen(string)); + if (num > 0) { + buffer[num] = 0; + } else { + wchar_t *tmp = buffer; + while ((*tmp++ = *string++)); + } + + uint32_t width = getTextWidth(buffer); + delete[] buffer; + + return width; +} + +uint32_t DrawUtils::getTextWidth(const wchar_t *string) { + FT_GlyphSlot slot = ft_face->glyph; + uint32_t width = 0; + + for (; *string; string++) { + FT_Load_Glyph(ft_face, FT_Get_Char_Index(ft_face, *string), FT_LOAD_BITMAP_METRICS_ONLY); + + width += slot->advance.x >> 6; + } + + return width; +} diff --git a/src/DrawUtils.h b/src/DrawUtils.h new file mode 100644 index 0000000..6ebcf5e --- /dev/null +++ b/src/DrawUtils.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +// visible screen sizes +#define SCREEN_WIDTH 854 +#define SCREEN_HEIGHT 480 + +union Color { + explicit Color(uint32_t color) { + this->color = color; + } + + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + this->r = r; + this->g = g; + this->b = b; + this->a = a; + } + + uint32_t color{}; + struct { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + }; +}; + +class DrawUtils { +public: + static void initBuffers(void *tvBuffer, uint32_t tvSize, void *drcBuffer, uint32_t drcSize); + + static void beginDraw(); + + static void endDraw(); + + static void clear(Color col); + + static void drawPixel(uint32_t x, uint32_t y, Color col) { drawPixel(x, y, col.r, col.g, col.b, col.a); } + + static void drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a); + + static void drawRectFilled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, Color col); + + static void drawRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, Color col); + + static void drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32_t target_height, const uint8_t *data); + + + static void initFont(); + + static void deinitFont(); + + static void setFontSize(uint32_t size); + + static void setFontColor(Color col); + + static void print(uint32_t x, uint32_t y, const char *string, bool alignRight = false); + + static void print(uint32_t x, uint32_t y, const wchar_t *string, bool alignRight = false); + + static uint32_t getTextWidth(const char *string); + + static uint32_t getTextWidth(const wchar_t *string); + +private: + static bool isBackBuffer; + + static uint8_t *tvBuffer; + static uint32_t tvSize; + static uint8_t *drcBuffer; + static uint32_t drcSize; +}; diff --git a/src/globals.cpp b/src/globals.cpp new file mode 100644 index 0000000..ef630d0 --- /dev/null +++ b/src/globals.cpp @@ -0,0 +1,19 @@ +#include "globals.h" + +int gPreferSystemSettings = 1; +int gAutoDetection = 1; +int gForceSettingsEnabled = 0; +Lanuages gDefaultLanguage = LANG_ENGLISH; +int32_t gDefaultCountry = 78; +MCPRegion gDefaultProductArea = MCP_REGION_EUROPE; + +Lanuages gCurrentLanguage = gDefaultLanguage; +int32_t gCurrentCountry = gDefaultCountry; +MCPRegion gCurrentProductArea = gDefaultProductArea; + +Lanuages gDefaultLangForEUR = LANG_ENGLISH; +int32_t gDefaultCountryForEUR = 110; +Lanuages gDefaultLangForUSA = LANG_ENGLISH; +int32_t gDefaultCountryForUSA = 49; +Lanuages gDefaultLangForJPN = LANG_JAPANESE; +int32_t gDefaultCountryForJPN = 1; \ No newline at end of file diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 0000000..f4058ed --- /dev/null +++ b/src/globals.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +extern int gForceSettingsEnabled; + +enum Lanuages { + LANG_JAPANESE = 0, + LANG_ENGLISH = 1, + LANG_FRANCAIS = 2, + LANG_DEUTSCH = 3, + LANG_ESPANOL = 5, + LANG_ITALIANO = 4, + LANG_NEDERLANDS = 8, + LANG_PORTUGUES = 9, + LANG_RUSSKI = 10, +}; + + +extern int gPreferSystemSettings; +extern int gAutoDetection; +extern Lanuages gDefaultLanguage; +extern int32_t gDefaultCountry; +extern MCPRegion gDefaultProductArea; + +extern Lanuages gCurrentLanguage; +extern int32_t gCurrentCountry; +extern MCPRegion gCurrentProductArea; + +extern Lanuages gDefaultLangForEUR; +extern int32_t gDefaultCountryForEUR; +extern Lanuages gDefaultLangForUSA; +extern int32_t gDefaultCountryForUSA; +extern Lanuages gDefaultLangForJPN; +extern int32_t gDefaultCountryForJPN; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..60bcf80 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,577 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils/logger.h" +#include "ConfigUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "globals.h" + +WUPS_PLUGIN_NAME("Region Free Plugin"); +WUPS_PLUGIN_DESCRIPTION("Allows the user to load titles from other regions"); +WUPS_PLUGIN_VERSION("0.1"); +WUPS_PLUGIN_AUTHOR("Maschell"); +WUPS_PLUGIN_LICENSE("GPL"); + +WUPS_USE_WUT_DEVOPTAB(); +WUPS_USE_STORAGE("region_free_plugin"); + +bool getRealProductArea(MCPRegion* out); + +INITIALIZE_PLUGIN() { + WHBLogUdpInit(); +} + +DECL_FUNCTION(int32_t, ACPGetLaunchMetaXml, ACPMetaXml *metaxml) { + int result = real_ACPGetLaunchMetaXml(metaxml); + if (metaxml != nullptr) { + metaxml->region = 0xFFFFFFFF; + } + return result; +} + + +DECL_FUNCTION(int, UCReadSysConfig, int IOHandle, int count, struct UCSysConfig *settings) { + int result = real_UCReadSysConfig(IOHandle, count, settings); + + if (gForceSettingsEnabled) { + if (result != 0) { + return result; + } + + if (strcmp(settings->name, "cafe.language") == 0) { + DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: cafe.language found!"); + DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: forcing language..."); + DEBUG_FUNCTION_LINE("UCReadSysConfig: original lang %d, new %d", *((int *) settings->data), gCurrentLanguage); + *((int *) settings->data) = gCurrentLanguage; + } + if (strcmp(settings->name, "cafe.cntry_reg") == 0) { + DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: cafe.cntry_reg found!"); + DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: forcing cntry_reg..."); + DEBUG_FUNCTION_LINE("UCReadSysConfig: original cntry_reg %d, new %d", *((int *) settings->data), gCurrentCountry); + *((int *) settings->data) = gCurrentCountry; + } + } + + return result; +} + +ON_APPLICATION_ENDS() { + gCurrentLanguage = gDefaultLanguage; + gCurrentCountry = gDefaultCountry; + gCurrentProductArea = gDefaultProductArea; +} + +#define CAT_GENERAL_ROOT "root" +#define CAT_GENERAL_SETTINGS "general_settings" +#define CAT_TITLE_SETTINGS "title_settings" + +#define VAL_LANGUAGE "language" +#define VAL_COUNTRY "cntry_reg" +#define VAL_PRODUCT_AREA "product_area" + +#define VAL_PREFER_SYSTEM_SETTINGS "prefer_system_settings" +#define VAL_AUTO_DETECTION "auto_detection" +#define VAL_DEFAULT_LANG_EUR "default_lang_eur" +#define VAL_DEFAULT_LANG_USA "default_lang_usa" +#define VAL_DEFAULT_LANG_JPN "default_lang_jpn" + +#define VAL_DEFAULT_COUNTRY_EUR "default_cntry_reg_eur" +#define VAL_DEFAULT_COUNTRY_USA "default_cntry_reg_usa" +#define VAL_DEFAULT_COUNTRY_JPN "default_cntry_reg_jpn" + +extern "C" void ACPInitialize(); +extern "C" void ACPFinalize(); + +DECL_FUNCTION(int32_t, ACPGetTitleMetaXmlByDevice, uint32_t titleid_upper, uint32_t titleid_lower, ACPMetaXml *metaxml, uint32_t device) { + int result = real_ACPGetTitleMetaXmlByDevice(titleid_upper, titleid_lower, metaxml, device); + if (metaxml != nullptr) { + metaxml->region = 0xFFFFFFFF; + } + return result; +} + +ON_FUNCTIONS_PATCHED() { + bool forceConfigMenu = false; + auto *acpMetaXml = (ACPMetaXml *) memalign(0x40, 0x4000); + + memset(acpMetaXml, 0, sizeof(ACPMetaXml)); + auto regionFromXML = 0; + if (acpMetaXml) { + ACPInitialize(); + auto res = real_ACPGetLaunchMetaXml(acpMetaXml); + if (res >= 0) { + regionFromXML = acpMetaXml->region; + if (acpMetaXml->region == 1) { + DEBUG_FUNCTION_LINE("Set default to JAPAN"); + gDefaultProductArea = MCP_REGION_JAPAN; + gDefaultLanguage = gDefaultLangForJPN; + gDefaultCountry = gDefaultCountryForJPN; + } else if (acpMetaXml->region == 2) { + DEBUG_FUNCTION_LINE("Set default to USA"); + gDefaultProductArea = MCP_REGION_USA; + gDefaultLanguage = gDefaultLangForUSA; + gDefaultCountry = gDefaultCountryForUSA; + } else if (acpMetaXml->region == 4) { + DEBUG_FUNCTION_LINE("Set default to EUR"); + gDefaultProductArea = MCP_REGION_EUROPE; + gDefaultLanguage = gDefaultLangForEUR; + gDefaultCountry = gDefaultCountryForEUR; + } else { + DEBUG_FUNCTION_LINE("Unknown area %d, forcing language will be disabled", acpMetaXml->region); + forceConfigMenu = true; + } + } else { + forceConfigMenu = true; + } + ACPFinalize(); + free(acpMetaXml); + } else { + forceConfigMenu = true; + } + + // Get region and lang from console and set these as default. + gCurrentLanguage = gDefaultLanguage; + gCurrentCountry = gDefaultCountry; + gCurrentProductArea = gDefaultProductArea; + + MCPRegion real_product_area; + if(gPreferSystemSettings && getRealProductArea(&real_product_area)){ + if((regionFromXML & real_product_area) == real_product_area){ + gCurrentProductArea = real_product_area; + + auto ucHandle = UCOpen(); + if(ucHandle >= 0){ + DEBUG_FUNCTION_LINE("Got UCHandle %08X0", ucHandle); + UCSysConfig sysConfig; + memset((void *) &sysConfig, 0, sizeof(sysConfig)); + uint32_t data = 0xFFFFFFFF; + sysConfig.dataType = UC_DATATYPE_UNSIGNED_INT; + sysConfig.dataSize = 4; + sysConfig.data = &data; + strncpy(sysConfig.name, "cafe.language", 64); + int ucRes = real_UCReadSysConfig(ucHandle, 1, &sysConfig); + + if(ucRes >= 0){ + DEBUG_FUNCTION_LINE("Force default language to system title for own region"); + gCurrentLanguage = static_cast(*(uint32_t *) sysConfig.data); + gDefaultLanguage = static_cast(*(uint32_t *) sysConfig.data); + } else { + DEBUG_FUNCTION_LINE("UCReadSysConfig failed"); + } + UCClose(ucHandle); + } else { + DEBUG_FUNCTION_LINE("UCOpen failed"); + } + } + } + + wups_storage_item_t *root = nullptr; + auto resa = WUPS_GetSubItem(nullptr, CAT_GENERAL_ROOT, &root); + if (resa != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE("Failed to read %s subitem", CAT_GENERAL_ROOT); + return; + } + + wups_storage_item_t *title_settings; + if (WUPS_GetSubItem(root, CAT_TITLE_SETTINGS, &title_settings) != WUPS_STORAGE_ERROR_SUCCESS) { + if (WUPS_CreateSubItem(root, CAT_TITLE_SETTINGS, &title_settings) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE("WUPS_CreateSubItem %s failed", CAT_TITLE_SETTINGS); + return; + } + } + + char buffer[18]; + snprintf(buffer, 17, "%016llX", OSGetTitleID()); + + wups_storage_item_t *curTitleItem; + if (WUPS_GetSubItem(title_settings, buffer, &curTitleItem) != WUPS_STORAGE_ERROR_SUCCESS) { + if (WUPS_CreateSubItem(title_settings, buffer, &curTitleItem) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE("WUPS_CreateSubItem %s failed", buffer); + return; + } + } + + if (curTitleItem != nullptr) { + if (WUPS_GetInt(curTitleItem, VAL_LANGUAGE, (int32_t *) &gCurrentLanguage) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(curTitleItem, VAL_LANGUAGE, gDefaultLanguage); + gCurrentLanguage = gDefaultLanguage; + } + + + if (WUPS_GetInt(curTitleItem, VAL_COUNTRY, (int32_t *) &gCurrentCountry) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(curTitleItem, VAL_COUNTRY, gDefaultCountry); + gCurrentCountry = gDefaultCountry; + } + + + if (WUPS_GetInt(curTitleItem, VAL_PRODUCT_AREA, (int32_t *) &gCurrentProductArea) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(curTitleItem, VAL_PRODUCT_AREA, gDefaultProductArea); + gCurrentProductArea = gDefaultProductArea; + } + + // this overrides the current settings + if (forceConfigMenu || !gAutoDetection) { + ConfigUtils::openConfigMenu(); + // Save settings to storage + WUPS_StoreInt(curTitleItem, VAL_LANGUAGE, gCurrentLanguage); + WUPS_StoreInt(curTitleItem, VAL_COUNTRY, gCurrentCountry); + WUPS_StoreInt(curTitleItem, VAL_PRODUCT_AREA, gCurrentProductArea); + } + + DEBUG_FUNCTION_LINE("Language will be force to %d", gCurrentLanguage); + DEBUG_FUNCTION_LINE("Country will be force to %d", gDefaultCountry); + DEBUG_FUNCTION_LINE("Product Area will be forced to %d", gCurrentProductArea); + } + + WUPS_CloseStorage(); + +} + +ON_APPLICATION_START() { + WHBLogUdpInit(); + + WUPS_OpenStorage(); + + wups_storage_item_t *root; + if (WUPS_GetSubItem(nullptr, CAT_GENERAL_ROOT, &root) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_CreateSubItem(nullptr, CAT_GENERAL_ROOT, &root); + } + + wups_storage_item_t *general_settings; + if (WUPS_GetSubItem(root, CAT_GENERAL_SETTINGS, &general_settings) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_CreateSubItem(root, CAT_GENERAL_SETTINGS, &general_settings); + } + + + if (WUPS_GetInt(general_settings, VAL_AUTO_DETECTION, (int32_t *) &gAutoDetection) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(general_settings, VAL_AUTO_DETECTION, gAutoDetection); + } + if (WUPS_GetInt(general_settings, VAL_PREFER_SYSTEM_SETTINGS, (int32_t *) &gPreferSystemSettings) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(general_settings, VAL_PREFER_SYSTEM_SETTINGS, gPreferSystemSettings); + } + + if (WUPS_GetInt(general_settings, VAL_DEFAULT_LANG_EUR, (int32_t *) &gDefaultLangForEUR) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(general_settings, VAL_DEFAULT_LANG_EUR, gDefaultLangForEUR); + } + + if (WUPS_GetInt(general_settings, VAL_DEFAULT_COUNTRY_EUR, &gDefaultCountryForEUR) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(general_settings, VAL_DEFAULT_COUNTRY_EUR, gDefaultCountryForEUR); + } + + if (WUPS_GetInt(general_settings, VAL_DEFAULT_LANG_USA, (int32_t *) &gDefaultLangForUSA) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(general_settings, VAL_DEFAULT_LANG_USA, gDefaultLangForUSA); + } + + if (WUPS_GetInt(general_settings, VAL_DEFAULT_COUNTRY_USA, &gDefaultCountryForUSA) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(general_settings, VAL_DEFAULT_COUNTRY_USA, gDefaultCountryForUSA); + } + + if (WUPS_GetInt(general_settings, VAL_DEFAULT_LANG_JPN, (int32_t *) &gDefaultLangForJPN) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(general_settings, VAL_DEFAULT_LANG_JPN, gDefaultLangForJPN); + } + + if (WUPS_GetInt(general_settings, VAL_DEFAULT_COUNTRY_JPN, &gDefaultCountryForJPN) != WUPS_STORAGE_ERROR_SUCCESS) { + WUPS_StoreInt(general_settings, VAL_DEFAULT_COUNTRY_JPN, gDefaultCountryForJPN); + } + + gForceSettingsEnabled = 1; + if (OSGetTitleID() == 0x0005001010040000L || // Wii U Menu JPN + OSGetTitleID() == 0x0005001010040100L || // Wii U Menu USA + OSGetTitleID() == 0x0005001010040200L) { // Wii U Menu EUR + gForceSettingsEnabled = 0; + } + DEBUG_FUNCTION_LINE("Start done"); +} + +void auto_detection_changed(ConfigItemBoolean *item, bool newValue) { + DEBUG_FUNCTION_LINE("New value in auto_detection changed: %d", newValue); + + wups_storage_item_t *root; + if (WUPS_GetSubItem(nullptr, CAT_GENERAL_ROOT, &root) != WUPS_STORAGE_ERROR_SUCCESS) { + return; + } + + wups_storage_item_t *general_settings; + if (WUPS_GetSubItem(root, CAT_GENERAL_SETTINGS, &general_settings) != WUPS_STORAGE_ERROR_SUCCESS) { + return; + } + + WUPS_StoreInt(general_settings, VAL_AUTO_DETECTION, newValue); + gAutoDetection = newValue; +} + +void prefer_system_changed(ConfigItemBoolean *item, bool newValue) { + DEBUG_FUNCTION_LINE("New value in prefer_system_settings changed: %d", newValue); + + wups_storage_item_t *root; + if (WUPS_GetSubItem(nullptr, CAT_GENERAL_ROOT, &root) != WUPS_STORAGE_ERROR_SUCCESS) { + return; + } + + wups_storage_item_t *general_settings; + if (WUPS_GetSubItem(root, CAT_GENERAL_SETTINGS, &general_settings) != WUPS_STORAGE_ERROR_SUCCESS) { + return; + } + + WUPS_StoreInt(general_settings, VAL_PREFER_SYSTEM_SETTINGS, newValue); + gPreferSystemSettings = newValue; +} + +void default_lang_changed(ConfigItemMultipleValues *item, uint32_t newValue) { + DEBUG_FUNCTION_LINE("New value in %s changed: %d", item->configID, newValue); + + wups_storage_item_t *root; + if (WUPS_GetSubItem(nullptr, CAT_GENERAL_ROOT, &root) != WUPS_STORAGE_ERROR_SUCCESS) { + return; + } + + wups_storage_item_t *general_settings; + if (WUPS_GetSubItem(root, CAT_GENERAL_SETTINGS, &general_settings) != WUPS_STORAGE_ERROR_SUCCESS) { + return; + } + + WUPS_StoreInt(general_settings, item->configID, (int32_t) newValue); + if (strcmp(item->configID, VAL_DEFAULT_LANG_EUR) == 0) { + DEBUG_FUNCTION_LINE("Updated default eur lang"); + gDefaultLangForEUR = (Lanuages) newValue; + } else if (strcmp(item->configID, VAL_DEFAULT_LANG_USA) == 0) { + DEBUG_FUNCTION_LINE("Updated default usa lang"); + gDefaultLangForUSA = (Lanuages) newValue; + } else if (strcmp(item->configID, VAL_DEFAULT_LANG_JPN) == 0) { + DEBUG_FUNCTION_LINE("Updated default jpn lang"); + gDefaultLangForJPN = (Lanuages) newValue; + } +} + +void getConfigInfoForLangMap(std::map &curLangMap, ConfigItemMultipleValuesPair *pair, uint32_t default_lang, uint32_t *default_index, uint32_t *len) { + uint32_t i = 0; + for (auto &curEntry: curLangMap) { + if (default_lang == curEntry.first) { + *default_index = i; + } + pair[i].value = curEntry.first; + pair[i].valueName = (char *) curEntry.second; + i++; + } + *len = i; +} + +WUPS_GET_CONFIG() { + // We open the storage so we can persist the configuration the user did. + WUPS_OpenStorage(); + + WUPSConfigHandle config; + WUPSConfig_CreateHandled(&config, "Region Free Plugin"); + + WUPSConfigCategoryHandle cat; + WUPSConfig_AddCategoryByNameHandled(config, "Settings", &cat); + + WUPSConfigItemBoolean_AddToCategoryHandled(config, cat, VAL_DEFAULT_LANG_USA, "Auto Detection", gAutoDetection, &auto_detection_changed); + WUPSConfigItemBoolean_AddToCategoryHandled(config, cat, VAL_PREFER_SYSTEM_SETTINGS, "Prefer System Settings For Own Region", gPreferSystemSettings, &prefer_system_changed); + + std::map eur_lang_map{ + {LANG_ENGLISH, "English"}, + {LANG_FRANCAIS, "Francais"}, + {LANG_DEUTSCH, "Deutsch"}, + {LANG_ITALIANO, "Italiano"}, + {LANG_ESPANOL, "Espanol"}, + {LANG_NEDERLANDS, "Nederlands"}, + {LANG_PORTUGUES, "Portugues"}, + {LANG_RUSSKI, "Russki"}, + }; + + std::map usa_lang_map{ + {LANG_ENGLISH, "English"}, + {LANG_FRANCAIS, "Francais"}, + {LANG_ESPANOL, "Espanol"}, + {LANG_PORTUGUES, "Portugues"} + }; + + ConfigItemMultipleValuesPair lang_eur_pair[eur_lang_map.size()]; + uint32_t number_lang_eur_values = 0; + uint32_t default_index_eur = 0; + + getConfigInfoForLangMap(eur_lang_map, lang_eur_pair, gDefaultLangForEUR, &default_index_eur, &number_lang_eur_values); + + WUPSConfigItemMultipleValues_AddToCategoryHandled(config, cat, VAL_DEFAULT_LANG_EUR, "Default Language for EUR", default_index_eur, lang_eur_pair, number_lang_eur_values, + &default_lang_changed); + + ConfigItemMultipleValuesPair lang_usa_pair[eur_lang_map.size()]; + uint32_t number_lang_usa_values = 0; + uint32_t default_index_usa = 0; + + getConfigInfoForLangMap(usa_lang_map, lang_usa_pair, gDefaultLangForUSA, &default_index_usa, &number_lang_usa_values); + + WUPSConfigItemMultipleValues_AddToCategoryHandled(config, cat, VAL_DEFAULT_LANG_USA, "Default Language for USA", default_index_usa, lang_usa_pair, number_lang_usa_values, + &default_lang_changed); + + + return config; +} + +WUPS_CONFIG_CLOSED() { + // Save all changes + WUPS_CloseStorage(); +} + + +DECL_FUNCTION(int, MCP_GetSysProdSettings, int IOHandle, struct MCPSysProdSettings *settings) { + int result = real_MCP_GetSysProdSettings(IOHandle, settings); + + if (gForceSettingsEnabled) { + if (result != 0) { + return result; + } + + DEBUG_FUNCTION_LINE_VERBOSE("MCP_GetSysProdSettings: forcing platform region..."); + DEBUG_FUNCTION_LINE("MCP_GetSysProdSettings: original region %d, new %d...", settings->product_area, gCurrentProductArea); + settings->product_area = gCurrentProductArea; + } + + return result; +} + +static const uint64_t + sSysAppTitleId[][3] = + { + { + // Updater + 0x0005001010040000ull, + 0x0005001010040100ull, + 0x0005001010040200ull, + }, + + { + // System Settings + 0x0005001010047000ull, + 0x0005001010047100ull, + 0x0005001010047200ull, + }, + + { + // Parental Controls + 0x0005001010048000ull, + 0x0005001010048100ull, + 0x0005001010048200ull, + }, + + { + // User Settings + 0x0005001010049000ull, + 0x0005001010049100ull, + 0x0005001010049200ull, + }, + + { + // Mii Maker + 0x000500101004A000ull, + 0x000500101004A100ull, + 0x000500101004A200ull, + }, + + { + // Account Settings + 0x000500101004B000ull, + 0x000500101004B100ull, + 0x000500101004B200ull, + }, + + { + // Daily log + 0x000500101004C000ull, + 0x000500101004C100ull, + 0x000500101004C200ull, + }, + + { + // Notifications + 0x000500101004D000ull, + 0x000500101004D100ull, + 0x000500101004D200ull, + }, + + { + // Health and Safety Information + 0x000500101004E000ull, + 0x000500101004E100ull, + 0x000500101004E200ull, + }, + + { + // Electronic Manual + 0x0005001B10059000ull, + 0x0005001B10059100ull, + 0x0005001B10059200ull, + }, + + { + // Wii U Chat + 0x000500101005A000ull, + 0x000500101005A100ull, + 0x000500101005A200ull, + }, + + { + // "Software/Data Transfer" + 0x0005001010062000ull, + 0x0005001010062100ull, + 0x0005001010062200ull, + }, + }; + +bool getRealProductArea(MCPRegion* out){ + if(out == nullptr){ + return false; + } + auto handle = MCP_Open(); + if (handle >= 0) { + auto data = (struct MCPSysProdSettings *) memalign(0x40, sizeof(struct MCPSysProdSettings)); + auto res = real_MCP_GetSysProdSettings(handle, data); + if (res >= 0) { + *out = data->product_area; + } + MCP_Close(handle); + free(data); + return res >= 0; + } + return false; +} + +DECL_FUNCTION(uint64_t, _SYSGetSystemApplicationTitleIdByProdArea, SYSTEM_APP_ID param_1, MCPRegion param_2) { + MCPRegion cur_product_area; + if(!getRealProductArea(&cur_product_area)){ + DEBUG_FUNCTION_LINE("Failed to get real region"); + cur_product_area = param_2; + } + + auto regionIdx = 1u; + + if (cur_product_area == MCP_REGION_JAPAN) { + regionIdx = 0u; + } else if (cur_product_area == MCP_REGION_EUROPE || cur_product_area == 0x08) { + regionIdx = 2u; + } + + uint64_t res = sSysAppTitleId[param_1][regionIdx]; + return res; +} + +WUPS_MUST_REPLACE(ACPGetTitleMetaXmlByDevice, WUPS_LOADER_LIBRARY_NN_ACP, ACPGetTitleMetaXmlByDevice); +WUPS_MUST_REPLACE(ACPGetLaunchMetaXml, WUPS_LOADER_LIBRARY_NN_ACP, ACPGetLaunchMetaXml); +WUPS_MUST_REPLACE(MCP_GetSysProdSettings, WUPS_LOADER_LIBRARY_COREINIT, MCP_GetSysProdSettings); +WUPS_MUST_REPLACE(UCReadSysConfig, WUPS_LOADER_LIBRARY_COREINIT, UCReadSysConfig); +WUPS_MUST_REPLACE(_SYSGetSystemApplicationTitleIdByProdArea, WUPS_LOADER_LIBRARY_SYSAPP, _SYSGetSystemApplicationTitleIdByProdArea); + diff --git a/src/utils/logger.h b/src/utils/logger.h new file mode 100644 index 0000000..65e8da1 --- /dev/null +++ b/src/utils/logger.h @@ -0,0 +1,32 @@ +#ifndef __LOGGER_H_ +#define __LOGGER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \ + OSFatal_printf("[%s]%s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \ + WHBLogWritef("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif