first commit

This commit is contained in:
Maschell 2021-10-01 17:30:47 +02:00
commit 6e0fb4080f
14 changed files with 1636 additions and 0 deletions

58
.github/workflows/ci.yml vendored Normal file
View File

@ -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

17
.github/workflows/pr.yml vendored Normal file
View File

@ -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"

11
.gitignore vendored Normal file
View File

@ -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

6
Dockerfile Normal file
View File

@ -0,0 +1,6 @@
FROM wiiuenv/devkitppc:20210920
COPY --from=wiiuenv/wiiupluginsystem:20211001 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libmappedmemory:20210924 /artifacts $DEVKITPRO
WORKDIR project

137
Makefile Normal file
View File

@ -0,0 +1,137 @@
#-------------------------------------------------------------------------------
.SUFFIXES:
#-------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/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
#-------------------------------------------------------------------------------

45
README.md Normal file
View File

@ -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
```

362
src/ConfigUtils.cpp Normal file
View File

@ -0,0 +1,362 @@
#include "ConfigUtils.h"
#include "utils/logger.h"
#include "DrawUtils.h"
#include <string>
#include <vector>
#include <coreinit/screen.h>
#include <memory/mappedmemory.h>
#include <vpad/input.h>
#include <padscore/kpad.h>
#include <coreinit/mcp.h>
#include <map>
#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<MCPRegion, const char *> region_map{
{MCP_REGION_JAPAN, "Japan"},
{MCP_REGION_USA, "USA"},
{MCP_REGION_EUROPE, "Europe"},
};
std::map<MCPRegion, int32_t> region_map_to_index{
{MCP_REGION_JAPAN, 0},
{MCP_REGION_USA, 1},
{MCP_REGION_EUROPE, 2},
};
std::map<int32_t, MCPRegion> 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<Lanuages, const char *> 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<Lanuages, int32_t> 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<int32_t, Lanuages> 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);
}
}

11
src/ConfigUtils.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <gx2/enum.h>
class ConfigUtils {
public:
static void openConfigMenu();
private:
static void displayMenu();
};

252
src/DrawUtils.cpp Normal file
View File

@ -0,0 +1,252 @@
#include "DrawUtils.h"
#include <coreinit/memory.h>
#include <coreinit/screen.h>
#include <ft2build.h>
#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;
}

74
src/DrawUtils.h Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include <cstdint>
// 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;
};

19
src/globals.cpp Normal file
View File

@ -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;

35
src/globals.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <coreinit/mcp.h>
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;

577
src/main.cpp Normal file
View File

@ -0,0 +1,577 @@
#include <wups.h>
#include <whb/log_udp.h>
#include <nn/acp.h>
#include <coreinit/title.h>
#include <coreinit/mcp.h>
#include <coreinit/userconfig.h>
#include <coreinit/filesystem.h>
#include <sysapp/title.h>
#include <coreinit/thread.h>
#include "utils/logger.h"
#include "ConfigUtils.h"
#include <malloc.h>
#include <cstdio>
#include <wups/config/WUPSConfigItemMultipleValues.h>
#include <map>
#include <coreinit/screen.h>
#include <vpad/input.h>
#include <padscore/kpad.h>
#include <wups/config/WUPSConfigItemBoolean.h>
#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<Lanuages>(*(uint32_t *) sysConfig.data);
gDefaultLanguage = static_cast<Lanuages>(*(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<Lanuages, const char *> &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<Lanuages, const char *> 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<Lanuages, const char *> 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);

32
src/utils/logger.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __LOGGER_H_
#define __LOGGER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include <whb/log.h>
#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