mirror of
https://github.com/wiiu-env/AutobootModule.git
synced 2024-11-25 03:56:58 +01:00
Initial commit
This commit is contained in:
commit
be672f5a86
65
.github/workflows/ci.yml
vendored
Normal file
65
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
name: CI-Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build-binary:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Checkout submodules using a PAT
|
||||||
|
run: |
|
||||||
|
git config --file .gitmodules --get-regexp url | while read url; do
|
||||||
|
git config --file=.gitmodules $(echo "$url" | sed -E "s/git@github.com:|https:\/\/github.com\//https:\/\/${{ secrets.CI_PAT }}:${{ secrets.CI_PAT }}@github.com\//")
|
||||||
|
done
|
||||||
|
git submodule sync
|
||||||
|
git submodule update --init --recursive
|
||||||
|
- 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: "*.rpx"
|
||||||
|
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
|
||||||
|
- name: zip artifact
|
||||||
|
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.rpx
|
||||||
|
- 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
|
24
.github/workflows/pr.yml
vendored
Normal file
24
.github/workflows/pr.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
name: CI-PR
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-binary:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Checkout submodules using a PAT
|
||||||
|
run: |
|
||||||
|
git config --file .gitmodules --get-regexp url | while read url; do
|
||||||
|
git config --file=.gitmodules $(echo "$url" | sed -E "s/git@github.com:|https:\/\/github.com\//https:\/\/${{ secrets.CI_PAT }}:${{ secrets.CI_PAT }}@github.com\//")
|
||||||
|
done
|
||||||
|
git submodule sync
|
||||||
|
git submodule update --init --recursive
|
||||||
|
- 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: "*.rpx"
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.elf
|
||||||
|
*.rpx
|
||||||
|
build/
|
3
Dockerfile
Normal file
3
Dockerfile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
FROM wiiuenv/devkitppc:20211106
|
||||||
|
|
||||||
|
WORKDIR project
|
126
Makefile
Normal file
126
Makefile
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
|
||||||
|
include $(DEVKITPRO)/wut/share/wut_rules
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# 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 := 99_autoboot
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := source include
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
CFLAGS := -g -Wall -O3 -ffunction-sections -fno-exceptions \
|
||||||
|
$(MACHDEP)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -std=c++20 -fno-rtti
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -g $(ARCH) $(RPXSPECS) --entry=_start -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := -lfreetype -lpng -lbz2 -lwut -lz
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level
|
||||||
|
# containing include and lib
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(WUT_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).rpx $(TARGET).elf
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
all : $(OUTPUT).rpx
|
||||||
|
|
||||||
|
$(OUTPUT).rpx : $(OUTPUT).elf
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
$(OFILES) :
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#-------------------------------------------------------------------------------
|
28
include/nn/cmpt/cmpt.h
Normal file
28
include/nn/cmpt/cmpt.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <wut.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum ScreenType {
|
||||||
|
SCREEN_TYPE_TV = 1,
|
||||||
|
SCREEN_TYPE_DRC,
|
||||||
|
SCREEN_TYPE_BOTH,
|
||||||
|
} ScreenType;
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
CMPTGetDataSize(uint32_t* outSize);
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
CMPTLaunchTitle(void* dataBuffer, uint32_t bufferSize, uint32_t titleId_low, uint32_t titleId_high);
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
CMPTAcctSetScreenType(ScreenType type);
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
CMPTCheckScreenState(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
28
include/nn/sl/FileStream.h
Normal file
28
include/nn/sl/FileStream.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wut.h>
|
||||||
|
#include <nn/result.h>
|
||||||
|
#include <coreinit/filesystem.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
namespace nn::sl {
|
||||||
|
class FileStream {
|
||||||
|
public:
|
||||||
|
FileStream() {
|
||||||
|
instance = __ct__Q3_2nn2sl10FileStreamFv(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
~FileStream() {
|
||||||
|
if (instance != nullptr) {
|
||||||
|
__dt__Q3_2nn2sl10FileStreamFv(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nn::Result Initialize(FSClient *client, FSCmdBlock *cmdBlock, char const *path, char const *mode) {
|
||||||
|
return Initialize__Q3_2nn2sl10FileStreamFP8FSClientP10FSCmdBlockPCcT3(this->instance, client, cmdBlock, path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStreamInternal *instance = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
33
include/nn/sl/LaunchInfoDatabase.h
Normal file
33
include/nn/sl/LaunchInfoDatabase.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wut.h>
|
||||||
|
#include <nn/result.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "FileStream.h"
|
||||||
|
|
||||||
|
namespace nn::sl {
|
||||||
|
class LaunchInfoDatabase {
|
||||||
|
public:
|
||||||
|
LaunchInfoDatabase() {
|
||||||
|
instance = __ct__Q3_2nn2sl18LaunchInfoDatabaseFv(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
~LaunchInfoDatabase() {
|
||||||
|
Finalize__Q3_2nn2sl18LaunchInfoDatabaseFv(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nn::sl::LaunchInfoDatabase::Load(nn::sl::IStream &, nn::sl::Region)
|
||||||
|
nn::Result Load(nn::sl::FileStream *fileStream, nn::sl::Region region) {
|
||||||
|
return Load__Q3_2nn2sl18LaunchInfoDatabaseFRQ3_2nn2sl7IStreamQ3_2nn2sl6Region(instance, fileStream->instance, region);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nn::sl::LaunchInfoDatabase::GetLaunchInfoById(nn::sl::LaunchInfo *, unsigned long long) const
|
||||||
|
nn::Result GetLaunchInfoById(nn::sl::LaunchInfo *launchInfo, uint64_t titleId) {
|
||||||
|
return GetLaunchInfoById__Q3_2nn2sl18LaunchInfoDatabaseCFPQ3_2nn2sl10LaunchInfoUL(instance, launchInfo, titleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LaunchInfoDatabaseInternal *instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
54
include/nn/sl/common.h
Normal file
54
include/nn/sl/common.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wut.h>
|
||||||
|
#include <nn/result.h>
|
||||||
|
#include <coreinit/filesystem.h>
|
||||||
|
#include <coreinit/memdefaultheap.h>
|
||||||
|
|
||||||
|
namespace nn::sl {
|
||||||
|
typedef struct WUT_PACKED FileStreamInternal {
|
||||||
|
WUT_UNKNOWN_BYTES(0x10);
|
||||||
|
} FileStreamInternal;
|
||||||
|
WUT_CHECK_SIZE(FileStreamInternal, 0x10);
|
||||||
|
|
||||||
|
extern "C" nn::Result Initialize__Q3_2nn2sl10FileStreamFP8FSClientP10FSCmdBlockPCcT3(FileStreamInternal *, FSClient *, FSCmdBlock *, char const *, char const *);
|
||||||
|
extern "C" FileStreamInternal *__ct__Q3_2nn2sl10FileStreamFv(FileStreamInternal *);
|
||||||
|
extern "C" void __dt__Q3_2nn2sl10FileStreamFv(FileStreamInternal *);
|
||||||
|
|
||||||
|
typedef struct WUT_PACKED LaunchInfo {
|
||||||
|
uint64_t titleId;
|
||||||
|
WUT_UNKNOWN_BYTES(0x810 - 0x08);
|
||||||
|
} LaunchInfo;
|
||||||
|
|
||||||
|
WUT_CHECK_OFFSET(LaunchInfo, 0x00, titleId);
|
||||||
|
WUT_CHECK_SIZE(LaunchInfo, 0x810);
|
||||||
|
|
||||||
|
void
|
||||||
|
GetDefaultDatabasePath(char *, int size, uint32_t tid_hi, uint32_t tid_low)
|
||||||
|
asm("GetDefaultDatabasePath__Q2_2nn2slFPcUiUL");
|
||||||
|
|
||||||
|
nn::Result
|
||||||
|
Initialize(MEMAllocFromDefaultHeapExFn, MEMFreeToDefaultHeapFn)
|
||||||
|
asm("Initialize__Q2_2nn2slFPFUiT1_PvPFPv_v");
|
||||||
|
|
||||||
|
nn::Result
|
||||||
|
Finalize()
|
||||||
|
asm("Finalize__Q2_2nn2slFv");
|
||||||
|
|
||||||
|
typedef enum Region {
|
||||||
|
REGION_JPN = 0,
|
||||||
|
REGION_USA = 1,
|
||||||
|
REGION_EUR = 2
|
||||||
|
} Region;
|
||||||
|
|
||||||
|
typedef struct WUT_PACKED LaunchInfoDatabaseInternal {
|
||||||
|
WUT_UNKNOWN_BYTES(0x1C);
|
||||||
|
} LaunchInfoDatabaseInternal;
|
||||||
|
WUT_CHECK_SIZE(LaunchInfoDatabaseInternal, 0x1C);
|
||||||
|
|
||||||
|
extern "C" LaunchInfoDatabaseInternal *__ct__Q3_2nn2sl18LaunchInfoDatabaseFv(LaunchInfoDatabaseInternal *);
|
||||||
|
extern "C" nn::Result Load__Q3_2nn2sl18LaunchInfoDatabaseFRQ3_2nn2sl7IStreamQ3_2nn2sl6Region(LaunchInfoDatabaseInternal *, nn::sl::FileStreamInternal *, nn::sl::Region);
|
||||||
|
extern "C" void Finalize__Q3_2nn2sl18LaunchInfoDatabaseFv(LaunchInfoDatabaseInternal *);
|
||||||
|
extern "C" nn::Result GetLaunchInfoById__Q3_2nn2sl18LaunchInfoDatabaseCFPQ3_2nn2sl10LaunchInfoUL(LaunchInfoDatabaseInternal *, nn::sl::LaunchInfo *, uint64_t titleId);
|
||||||
|
}
|
||||||
|
|
332
source/DrawUtils.cpp
Normal file
332
source/DrawUtils.cpp
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
#include "DrawUtils.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <coreinit/memory.h>
|
||||||
|
#include <coreinit/screen.h>
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include <png.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 class 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void png_read_data(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead)
|
||||||
|
{
|
||||||
|
void** data = (void**) png_get_io_ptr(png_ptr);
|
||||||
|
|
||||||
|
memcpy(outBytes, *data, byteCountToRead);
|
||||||
|
*((uint8_t**) data) += byteCountToRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawUtils::drawPNG(uint32_t x, uint32_t y, const uint8_t* data)
|
||||||
|
{
|
||||||
|
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
if(png_ptr == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if(info_ptr == NULL) {
|
||||||
|
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_set_read_fn(png_ptr, (void*) &data, png_read_data);
|
||||||
|
|
||||||
|
png_read_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
uint32_t width = 0;
|
||||||
|
uint32_t height = 0;
|
||||||
|
int bitDepth = 0;
|
||||||
|
int colorType = -1;
|
||||||
|
uint32_t retval = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth, &colorType, NULL, NULL, NULL);
|
||||||
|
if(retval != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t bytesPerRow = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
|
uint8_t* rowData = new uint8_t[bytesPerRow];
|
||||||
|
|
||||||
|
for(uint32_t yy = y; yy < y + height; yy++) {
|
||||||
|
png_read_row(png_ptr, (png_bytep)rowData, NULL);
|
||||||
|
|
||||||
|
for (uint32_t xx = x; xx < x + width; xx++) {
|
||||||
|
if (colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
|
||||||
|
uint32_t i = (xx - x) * 4;
|
||||||
|
drawPixel(xx, yy, rowData[i], rowData[i + 1], rowData[i + 2], rowData[i + 3]);
|
||||||
|
}
|
||||||
|
else if (colorType == PNG_COLOR_TYPE_RGB) {
|
||||||
|
uint32_t i = (xx - x) * 3;
|
||||||
|
drawPixel(xx, yy, rowData[i], rowData[i + 1], rowData[i + 2], 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] rowData;
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawUtils::initFont()
|
||||||
|
{
|
||||||
|
void* font = NULL;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
wchar_t* 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)
|
||||||
|
{
|
||||||
|
wchar_t* 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;
|
||||||
|
}
|
57
source/DrawUtils.h
Normal file
57
source/DrawUtils.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// visible screen sizes
|
||||||
|
#define SCREEN_WIDTH 854
|
||||||
|
#define SCREEN_HEIGHT 480
|
||||||
|
|
||||||
|
union Color {
|
||||||
|
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 drawPNG(uint32_t x, uint32_t y, 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;
|
||||||
|
};
|
36
source/crt.c
Normal file
36
source/crt.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
void __init_wut_malloc();
|
||||||
|
|
||||||
|
void __init_wut_newlib();
|
||||||
|
|
||||||
|
void __init_wut_stdcpp();
|
||||||
|
|
||||||
|
void __init_wut_devoptab();
|
||||||
|
|
||||||
|
void __attribute__((weak)) __init_wut_socket();
|
||||||
|
|
||||||
|
void __fini_wut_malloc();
|
||||||
|
|
||||||
|
void __fini_wut_newlib();
|
||||||
|
|
||||||
|
void __fini_wut_stdcpp();
|
||||||
|
|
||||||
|
void __fini_wut_devoptab();
|
||||||
|
|
||||||
|
void __attribute__((weak)) __fini_wut_socket();
|
||||||
|
|
||||||
|
void __attribute__((weak))
|
||||||
|
__init_wut_() {
|
||||||
|
__init_wut_malloc();
|
||||||
|
__init_wut_newlib();
|
||||||
|
__init_wut_stdcpp();
|
||||||
|
__init_wut_devoptab();
|
||||||
|
if (&__init_wut_socket) __init_wut_socket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((weak))
|
||||||
|
__fini_wut_() {
|
||||||
|
__fini_wut_devoptab();
|
||||||
|
__fini_wut_stdcpp();
|
||||||
|
__fini_wut_newlib();
|
||||||
|
__fini_wut_malloc();
|
||||||
|
}
|
29
source/crt0.s
Normal file
29
source/crt0.s
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
.extern main
|
||||||
|
.extern exit
|
||||||
|
.extern __init_wut_
|
||||||
|
.extern __fini_wut_
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
stwu 1, -0x28(1)
|
||||||
|
mflr 0
|
||||||
|
stw 0, 0x2C(1)
|
||||||
|
stw 31, 0x24(1)
|
||||||
|
or 31, 1, 1
|
||||||
|
stw 3, 0x18(31)
|
||||||
|
stw 4, 0x1C(31)
|
||||||
|
bl __init_wut_
|
||||||
|
lwz 4, 0x1C(31)
|
||||||
|
lwz 3, 0x18(31)
|
||||||
|
bl main
|
||||||
|
or 9, 3, 3
|
||||||
|
stw 9, 0x8(31)
|
||||||
|
bl __fini_wut_
|
||||||
|
lwz 9, 0x8(31)
|
||||||
|
or 3, 9, 9
|
||||||
|
addi 11, 31, 0x28
|
||||||
|
lwz 0, 0x4(11)
|
||||||
|
mtlr 0
|
||||||
|
lwz 31, -0x4(11)
|
||||||
|
or 1, 11, 11
|
||||||
|
blr
|
23
source/logger.h
Normal file
23
source/logger.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <whb/log.h>
|
||||||
|
|
||||||
|
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||||
|
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
|
||||||
|
|
||||||
|
#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \
|
||||||
|
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## 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
|
337
source/main.cpp
Normal file
337
source/main.cpp
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
#include <malloc.h>
|
||||||
|
#include <coreinit/screen.h>
|
||||||
|
#include <coreinit/filesystem.h>
|
||||||
|
#include <coreinit/memdefaultheap.h>
|
||||||
|
#include <vpad/input.h>
|
||||||
|
#include <sysapp/launch.h>
|
||||||
|
#include <sysapp/title.h>
|
||||||
|
#include <padscore/kpad.h>
|
||||||
|
#include <nn/acp.h>
|
||||||
|
#include <nn/act.h>
|
||||||
|
#include <nn/cmpt/cmpt.h>
|
||||||
|
#include <nn/ccr/sys_caffeine.h>
|
||||||
|
#include <nn/sl/common.h>
|
||||||
|
#include <nn/sl/FileStream.h>
|
||||||
|
#include <nn/sl/LaunchInfoDatabase.h>
|
||||||
|
#include <nn/spm.h>
|
||||||
|
|
||||||
|
#include <whb/log_module.h>
|
||||||
|
#include <whb/log_udp.h>
|
||||||
|
#include <whb/log_cafe.h>
|
||||||
|
|
||||||
|
#include "DrawUtils.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#define COLOR_WHITE Color(0xffffffff)
|
||||||
|
#define COLOR_BLACK Color(0, 0, 0, 255)
|
||||||
|
#define COLOR_BACKGROUND COLOR_BLACK
|
||||||
|
#define COLOR_TEXT COLOR_WHITE
|
||||||
|
#define COLOR_TEXT2 Color(0xB3ffffff)
|
||||||
|
#define COLOR_AUTOBOOT Color(0xaeea00ff)
|
||||||
|
#define COLOR_BORDER Color(204, 204, 204, 255)
|
||||||
|
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff)
|
||||||
|
|
||||||
|
static const char* menu_options[] = {
|
||||||
|
"Wii U Menu",
|
||||||
|
"Homebrew Launcher",
|
||||||
|
"vWii System Menu",
|
||||||
|
"vWii Homebrew Channel",
|
||||||
|
};
|
||||||
|
|
||||||
|
bool getQuickBoot() {
|
||||||
|
auto bootCheck = CCRSysCaffeineBootCheck();
|
||||||
|
if (bootCheck == 0) {
|
||||||
|
nn::sl::Initialize(MEMAllocFromDefaultHeapEx, MEMFreeToDefaultHeap);
|
||||||
|
char path[0x80];
|
||||||
|
nn::sl::GetDefaultDatabasePath(path, 0x80, 0x00050010, 0x10066000); // ECO process
|
||||||
|
FSCmdBlock cmdBlock;
|
||||||
|
FSInitCmdBlock(&cmdBlock);
|
||||||
|
|
||||||
|
auto fileStream = new nn::sl::FileStream;
|
||||||
|
auto *fsClient = (FSClient *) memalign(0x40, sizeof(FSClient));
|
||||||
|
memset(fsClient, 0, sizeof(*fsClient));
|
||||||
|
FSAddClient(fsClient, FS_ERROR_FLAG_NONE);
|
||||||
|
|
||||||
|
fileStream->Initialize(fsClient, &cmdBlock, path, "r");
|
||||||
|
|
||||||
|
auto database = new nn::sl::LaunchInfoDatabase;
|
||||||
|
database->Load(fileStream, nn::sl::REGION_EUR);
|
||||||
|
|
||||||
|
CCRAppLaunchParam data; // load sys caffeine data
|
||||||
|
// load app launch param
|
||||||
|
CCRSysCaffeineGetAppLaunchParam(&data);
|
||||||
|
|
||||||
|
// get launch info for id
|
||||||
|
nn::sl::LaunchInfo info;
|
||||||
|
auto result = database->GetLaunchInfoById(&info, data.titleId);
|
||||||
|
|
||||||
|
delete database;
|
||||||
|
delete fileStream;
|
||||||
|
|
||||||
|
FSDelClient(fsClient, FS_ERROR_FLAG_NONE);
|
||||||
|
|
||||||
|
nn::sl::Finalize();
|
||||||
|
|
||||||
|
if (!result.IsSuccess()) {
|
||||||
|
DEBUG_FUNCTION_LINE("GetLaunchInfoById failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.titleId == 0x0005001010040000L ||
|
||||||
|
info.titleId == 0x0005001010040100L ||
|
||||||
|
info.titleId == 0x0005001010040200L) {
|
||||||
|
DEBUG_FUNCTION_LINE("Skip quick booting into the Wii U Menu");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!SYSCheckTitleExists(info.titleId)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Title %016llX doesn't exist", info.titleId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MCPTitleListType titleInfo;
|
||||||
|
int32_t handle = MCP_Open();
|
||||||
|
auto err = MCP_GetTitleInfo(handle, info.titleId, &titleInfo);
|
||||||
|
MCP_Close(handle);
|
||||||
|
if (err == 0) {
|
||||||
|
nn::act::Initialize();
|
||||||
|
for (int i = 0; i < 13; i++) {
|
||||||
|
char uuid[16];
|
||||||
|
result = nn::act::GetUuidEx(uuid, i);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
if (memcmp(uuid, data.uuid, 8) == 0) {
|
||||||
|
DEBUG_FUNCTION_LINE("Load Console account %d", i);
|
||||||
|
nn::act::LoadConsoleAccount(i, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nn::act::Finalize();
|
||||||
|
|
||||||
|
ACPAssignTitlePatch(&titleInfo);
|
||||||
|
_SYSLaunchTitleWithStdArgsInNoSplash(info.titleId, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE("No quick boot");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initExternalStorage(void){
|
||||||
|
nn::spm::Initialize();
|
||||||
|
|
||||||
|
nn::spm::StorageListItem items[0x20];
|
||||||
|
int32_t numItems = nn::spm::GetStorageList(items, 0x20);
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < numItems; i++) {
|
||||||
|
if (items[i].type == nn::spm::STORAGE_TYPE_WFS) {
|
||||||
|
nn::spm::StorageInfo info{};
|
||||||
|
if (nn::spm::GetStorageInfo(&info, &items[i].index) == 0) {
|
||||||
|
DEBUG_FUNCTION_LINE("Using %s for extended storage", info.path);
|
||||||
|
|
||||||
|
nn::spm::SetExtendedStorage(&items[i].index);
|
||||||
|
ACPMountExternalStorage();
|
||||||
|
|
||||||
|
nn::spm::SetDefaultExtendedStorageVolumeId(info.volumeId);
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
DEBUG_FUNCTION_LINE("Fallback to empty ExtendedStorage");
|
||||||
|
nn::spm::VolumeId empty{};
|
||||||
|
nn::spm::SetDefaultExtendedStorageVolumeId(empty);
|
||||||
|
|
||||||
|
nn::spm::StorageIndex storageIndex = 0;
|
||||||
|
nn::spm::SetExtendedStorage(&storageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
nn::spm::Finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootSystemMenu(void){
|
||||||
|
nn::act::Initialize();
|
||||||
|
nn::act::SlotNo slot = nn::act::GetSlotNo();
|
||||||
|
nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount();
|
||||||
|
nn::act::Finalize();
|
||||||
|
|
||||||
|
if (defaultSlot) { //normal menu boot
|
||||||
|
SYSLaunchMenu();
|
||||||
|
} else { //show mii select
|
||||||
|
_SYSLaunchMenuWithCheckingAccount(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootHomebrewLauncher(void){
|
||||||
|
uint64_t titleId = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_MII_MAKER);
|
||||||
|
_SYSLaunchTitleWithStdArgsInNoSplash(titleId, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void launchvWiiTitle(uint32_t titleId_low, uint32_t titleId_high){
|
||||||
|
// we need to init kpad for cmpt
|
||||||
|
KPADInit();
|
||||||
|
|
||||||
|
// Try to find a screen type that works
|
||||||
|
CMPTAcctSetScreenType(SCREEN_TYPE_BOTH);
|
||||||
|
if (CMPTCheckScreenState() < 0) {
|
||||||
|
CMPTAcctSetScreenType(SCREEN_TYPE_DRC);
|
||||||
|
if (CMPTCheckScreenState() < 0) {
|
||||||
|
CMPTAcctSetScreenType(SCREEN_TYPE_TV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t dataSize = 0;
|
||||||
|
CMPTGetDataSize(&dataSize);
|
||||||
|
|
||||||
|
void* dataBuffer = memalign(0x40, dataSize);
|
||||||
|
CMPTLaunchTitle(dataBuffer, dataSize, titleId_low, titleId_high);
|
||||||
|
free(dataBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootvWiiMenu(void){
|
||||||
|
launchvWiiTitle(0x00000001, 0x00000002);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootHomebrewChannel(void){
|
||||||
|
launchvWiiTitle(0x00010001, 'OHBC');
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleMenuScreen(void){
|
||||||
|
OSScreenInit();
|
||||||
|
|
||||||
|
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||||
|
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
||||||
|
|
||||||
|
uint8_t* screenBuffer = (uint8_t*) memalign(0x100, tvBufferSize + drcBufferSize);
|
||||||
|
|
||||||
|
OSScreenSetBufferEx(SCREEN_TV, screenBuffer);
|
||||||
|
OSScreenSetBufferEx(SCREEN_DRC, screenBuffer + tvBufferSize);
|
||||||
|
|
||||||
|
OSScreenEnableEx(SCREEN_TV, TRUE);
|
||||||
|
OSScreenEnableEx(SCREEN_DRC, TRUE);
|
||||||
|
|
||||||
|
DrawUtils::initBuffers(screenBuffer, tvBufferSize, screenBuffer + tvBufferSize, drcBufferSize);
|
||||||
|
DrawUtils::initFont();
|
||||||
|
|
||||||
|
uint32_t selected = 0;
|
||||||
|
int autoBoot = -1;
|
||||||
|
bool redraw = true;
|
||||||
|
while (true) {
|
||||||
|
VPADStatus vpad{};
|
||||||
|
VPADRead(VPAD_CHAN_0, &vpad, 1, NULL);
|
||||||
|
|
||||||
|
if (vpad.trigger & VPAD_BUTTON_UP) {
|
||||||
|
if (selected > 0) {
|
||||||
|
selected--;
|
||||||
|
redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (vpad.trigger & VPAD_BUTTON_DOWN) {
|
||||||
|
if (selected < sizeof(menu_options) / sizeof(char*) - 1) {
|
||||||
|
selected++;
|
||||||
|
redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (vpad.trigger & VPAD_BUTTON_A) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (vpad.trigger & VPAD_BUTTON_X) {
|
||||||
|
autoBoot = -1;
|
||||||
|
redraw = true;
|
||||||
|
}
|
||||||
|
else if (vpad.trigger & VPAD_BUTTON_Y) {
|
||||||
|
autoBoot = selected;
|
||||||
|
redraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redraw) {
|
||||||
|
DrawUtils::beginDraw();
|
||||||
|
DrawUtils::clear(COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
// draw buttons
|
||||||
|
uint32_t index = 8 + 24 + 8 + 4;
|
||||||
|
for (uint32_t i = 0; i < sizeof(menu_options) / sizeof(char*); i++) {
|
||||||
|
if (i == selected) {
|
||||||
|
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16*2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
|
||||||
|
} else {
|
||||||
|
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16*2, 44, 2, (i == autoBoot) ? COLOR_AUTOBOOT : COLOR_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(24);
|
||||||
|
DrawUtils::setFontColor((i == autoBoot) ? COLOR_AUTOBOOT : COLOR_TEXT);
|
||||||
|
DrawUtils::print(16*2, index + 8 + 24, menu_options[i]);
|
||||||
|
index += 42 + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawUtils::setFontColor(COLOR_TEXT);
|
||||||
|
|
||||||
|
// draw top bar
|
||||||
|
DrawUtils::setFontSize(24);
|
||||||
|
DrawUtils::print(16, 6 + 24, "Tiramisu Boot Selector");
|
||||||
|
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||||
|
|
||||||
|
// draw bottom bar
|
||||||
|
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8*2, 3, COLOR_WHITE);
|
||||||
|
DrawUtils::setFontSize(18);
|
||||||
|
DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate ");
|
||||||
|
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue000 Choose", true);
|
||||||
|
const char* autobootHints = "\ue002 Clear Autoboot / \ue003 Select Autboot";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(autobootHints) / 2, SCREEN_HEIGHT - 8, autobootHints, true);
|
||||||
|
|
||||||
|
DrawUtils::endDraw();
|
||||||
|
|
||||||
|
redraw = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawUtils::deinitFont();
|
||||||
|
|
||||||
|
switch (selected) {
|
||||||
|
case 0:
|
||||||
|
bootSystemMenu();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
bootHomebrewLauncher();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
bootvWiiMenu();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
bootHomebrewChannel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(screenBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
if (!WHBLogModuleInit()) {
|
||||||
|
WHBLogCafeInit();
|
||||||
|
WHBLogUdpInit();
|
||||||
|
}
|
||||||
|
DEBUG_FUNCTION_LINE("Hello from Autoboot");
|
||||||
|
|
||||||
|
initExternalStorage();
|
||||||
|
|
||||||
|
if (getQuickBoot()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VPADStatus vpad{};
|
||||||
|
VPADRead(VPAD_CHAN_0, &vpad, 1, NULL);
|
||||||
|
|
||||||
|
if (vpad.hold & VPAD_BUTTON_PLUS) {
|
||||||
|
handleMenuScreen();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bootSystemMenu();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user