Initial commit

This commit is contained in:
PabloMK7 2025-03-06 15:34:50 +01:00
commit f36e54a5b5
77 changed files with 11237 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
images/logo.pdn
.vscode/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "plugin/ArticProtocol"]
path = plugin/ArticProtocol
url = https://github.com/PabloMK7/ArticProtocol.git

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 PabloMK7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

18
Makefile Normal file
View File

@ -0,0 +1,18 @@
VERSION_MAJOR := 1
VERSION_MINOR := 2
VERSION_REVISION := 0
all:
mkdir -p plugin/build
sed -e 's/VERSION_MAJOR/$(VERSION_MAJOR)/' -e 's/VERSION_MINOR/$(VERSION_MINOR)/' -e 's/VERSION_REVISION/$(VERSION_REVISION)/' plugin/AzaharArticSetup.plgInfo > plugin/build/AzaharArticSetup.plgInfo
$(MAKE) -C plugin VERSION_MAJOR=$(VERSION_MAJOR) VERSION_MINOR=$(VERSION_MINOR) VERSION_REVISION=$(VERSION_REVISION)
bin2c -d app/includes/plugin.h -o app/sources/plugin.c plugin/AzaharArticSetup.3gx
$(MAKE) -C app VERSION_MAJOR=$(VERSION_MAJOR) VERSION_MINOR=$(VERSION_MINOR) VERSION_REVISION=$(VERSION_REVISION)
clean:
$(MAKE) -C plugin clean
rm -f app/sources/plugin.c
rm -f app/includes/plugin.h
$(MAKE) -C app clean
re: clean all

15
README.md Normal file
View File

@ -0,0 +1,15 @@
# Azahar Artic Setup Tool
## Description
**Azahar Artic Setup Tool** is a 3DS homebrew application that helps doing the initial setup of the [Azahar Emulator](https://github.com/azahar-emu/azahar) using your console. It broadcasts the System Settings application as well as the NIM sysmodule to be able to perform a system update. Furthermore, it copies your console unique data to be able to use online functionality.
## Usage instructions
1) Download the `.cia` or `.3dsx` file from the [releases page](https://github.com/azahar-emu/ArticSetupTool/releases) and install it.
2) Run the Azahar Artic Setup Tool application. Press A to confirm you want to start it.
3) On the Azahar Emulator, go to `File -> Initial Setup` and enter the IP address displayed on your console.
NOTE: A recent version of Luma3DS (v13.3.1 or newer) is requires to use Azahar Artic Setup Tool. You can get it [here](https://github.com/LumaTeam/Luma3DS/releases/latest).
## License
See [LICENSE](LICENSE)

8
app/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
/build
*.3gx
.vscode/
output/
*.map
includes/plugin.h
sources/plugin.c

82
app/Makefile Normal file
View File

@ -0,0 +1,82 @@
# TARGET #
TARGET := 3DS
LIBRARY := 0
ifeq ($(TARGET),$(filter $(TARGET),3DS WIIU))
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro")
endif
endif
# COMMON CONFIGURATION #
NAME := Azahar Artic Setup
BUILD_DIR := build
OUTPUT_DIR := output
SOURCE_DIRS := sources
INCLUDE_DIRS := $(SOURCE_DIRS) includes
EXTRA_OUTPUT_FILES :=
LIBRARY_DIRS := $(PORTLIBS) $(CTRULIB) $(DEVKITPRO)/libcwav $(DEVKITPRO)/libncsnd
LIBRARIES := ctru
VERSION_MAJOR := 1
VERSION_MINOR := 0
VERSION_MICRO := 0
BUILD_FLAGS := -march=armv6k -mtune=mpcore -mfloat-abi=hard
BUILD_FLAGS_CC := -g -Wall -Wno-strict-aliasing -O3 -mword-relocations \
-fomit-frame-pointer -ffast-math $(ARCH) $(INCLUDE) -D__3DS__ $(BUILD_FLAGS) \
-DVERSION_MAJOR=${VERSION_MAJOR} \
-DVERSION_MINOR=${VERSION_MINOR} \
-DVERSION_REVISION=${VERSION_MICRO}
BUILD_FLAGS_CXX := $(BUILD_FLAGS_CC) $(COMMON_FLAGS) -fno-rtti -fno-exceptions -std=gnu++20
RUN_FLAGS :=
# 3DS/Wii U CONFIGURATION #
ifeq ($(TARGET),$(filter $(TARGET),3DS WIIU))
TITLE := Azahar Artic Setup Tool
DESCRIPTION := Setup tool for Azahar Emulator
AUTHOR := PabloMK7
endif
# 3DS CONFIGURATION #
ifeq ($(TARGET),3DS)
LIBRARY_DIRS += $(DEVKITPRO)/libctru $(DEVKITPRO)/portlibs/3ds/
LIBRARIES += ctru
PRODUCT_CODE := CTR-P-AAST
UNIQUE_ID := 0xAE5E7
CATEGORY := Application
USE_ON_SD := true
MEMORY_TYPE := Application
SYSTEM_MODE := 64MB
SYSTEM_MODE_EXT := Legacy
CPU_SPEED := 268MHz
ENABLE_L2_CACHE := true
ICON_FLAGS := --flags visible,recordusage
BANNER_AUDIO := resources/audio.cwav
BANNER_IMAGE := resources/banner.png
ICON := resources/icon.png
endif
# INTERNAL #
include buildtools/make_base
re : clean all
.PHONY: re

View File

@ -0,0 +1,10 @@
[default]
name=Default
runtime=host
config-opts=
run-opts=
prefix=/home/steven/.cache/gnome-builder/install/buildtools/host
app-id=
postbuild=
prebuild=
default=true

Binary file not shown.

View File

@ -0,0 +1,92 @@
#!/usr/bin/env python
# coding: utf-8 -*-
import os
import socket
import struct
import sys
import threading
import time
import urllib
try:
from SimpleHTTPServer import SimpleHTTPRequestHandler
from SocketServer import TCPServer
from urlparse import urljoin
from urllib import pathname2url, quote
except ImportError:
from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from urllib.parse import urljoin, quote
from urllib.request import pathname2url
if len(sys.argv) < 3 or len(sys.argv) > 5:
print('Usage: ' + sys.argv[0] + ' <target ip> <file / directory> [host ip] [host port]')
sys.exit(1)
accepted_extension = ('.cia', '.tik', '.cetk')
target_ip = sys.argv[1]
target_path = sys.argv[2]
hostPort = 8080 # Default value
if not os.path.exists(target_path):
print(target_path + ': No such file or directory.')
sys.exit(1)
if len(sys.argv) >= 4:
hostIp = sys.argv[3]
if len(sys.argv) == 5:
hostPort = int(sys.argv[4])
else:
print('Detecting host IP...')
hostIp = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
print('Preparing data...')
baseUrl = hostIp + ':' + str(hostPort) + '/'
if os.path.isfile(target_path):
if target_path.endswith(accepted_extension):
file_list_payload = baseUrl + quote(os.path.basename(target_path))
directory = os.path.dirname(target_path) # get file directory
else:
print('Unsupported file extension. Supported extensions are: ' + accepted_extension)
sys.exit(1)
else:
directory = target_path # it's a directory
file_list_payload = '' # init the payload before adding lines
for file in [file for file in next(os.walk(target_path))[2] if file.endswith(accepted_extension)]:
file_list_payload += baseUrl + quote(file) + '\n'
if len(file_list_payload) == 0:
print('No files to serve.')
sys.exit(1)
file_list_payloadBytes = file_list_payload.encode('ascii')
if directory and directory != '.': # doesn't need to move if it's already the current working directory
os.chdir(directory) # set working directory to the right folder to be able to serve files
print('\nURLs:')
print(file_list_payload + '\n')
print('Opening HTTP server on port ' + str(hostPort))
server = TCPServer(('', hostPort), SimpleHTTPRequestHandler)
thread = threading.Thread(target=server.serve_forever)
thread.start()
try:
print('Sending URL(s) to ' + target_ip + ' on port 5000...')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((target_ip, 5000))
sock.sendall(struct.pack('!L', len(file_list_payloadBytes)) + file_list_payloadBytes)
while len(sock.recv(1)) < 1:
time.sleep(0.05)
sock.close()
except Exception as e:
print('An error occurred: ' + str(e))
server.shutdown()
sys.exit(1)
print('Shutting down HTTP server...')
server.shutdown()

View File

@ -0,0 +1,286 @@
BasicInfo:
Title : $(APP_TITLE)
ProductCode : $(APP_PRODUCT_CODE)
Logo : Homebrew
RomFs:
RootPath: $(APP_ROMFS)
TitleInfo:
Category : $(APP_CATEGORY)
UniqueId : $(APP_UNIQUE_ID)
Option:
UseOnSD : $(APP_USE_ON_SD) # true if App is to be installed to SD
FreeProductCode : true # Removes limitations on ProductCode
MediaFootPadding : false # If true CCI files are created with padding
EnableCrypt : $(APP_ENCRYPTED) # Enables encryption for NCCH and CIA
EnableCompress : true # Compresses where applicable (currently only exefs:/.code)
AccessControlInfo:
CoreVersion : 2
# Exheader Format Version
DescVersion : 2
# Minimum Required Kernel Version (below is for 4.5.0)
ReleaseKernelMajor : "02"
ReleaseKernelMinor : "33"
# ExtData
UseExtSaveData : false # enables ExtData
#ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId
# FS:USER Archive Access Permissions
# Uncomment as required
FileSystemAccess:
- CategorySystemApplication
- CategoryHardwareCheck
- CategoryFileSystemTool
- Debug
- TwlCardBackup
- TwlNandData
- Boss
- DirectSdmc
- Core
- CtrNandRo
- CtrNandRw
- CtrNandRoWrite
- CategorySystemSettings
- CardBoard
- ExportImportIvs
- DirectSdmcWrite
- SwitchCleanup
- SaveDataMove
- Shop
- Shell
- CategoryHomeMenu
- SeedDB
IoAccessControl:
- FsMountNand
- FsMountNandRoWrite
- FsMountTwln
- FsMountWnand
- FsMountCardSpi
- UseSdif3
- CreateSeed
- UseCardSpi
# Process Settings
MemoryType : $(APP_MEMORY_TYPE) # Application/System/Base
SystemMode : $(APP_SYSTEM_MODE) # 64MB(Default)/96MB/80MB/72MB/32MB
IdealProcessor : 0
AffinityMask : 1
Priority : 16
MaxCpu : 0x9E # Default
HandleTableSize : 0x200
DisableDebug : false
EnableForceDebug : false
CanWriteSharedPage : true
CanUsePrivilegedPriority : false
CanUseNonAlphabetAndNumber : true
PermitMainFunctionArgument : true
CanShareDeviceMemory : true
RunnableOnSleep : false
SpecialMemoryArrange : true
# New3DS Exclusive Process Settings
SystemModeExt : $(APP_SYSTEM_MODE_EXT) # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
CpuSpeed : $(APP_CPU_SPEED) # 268MHz(Default)/804MHz
EnableL2Cache : $(APP_ENABLE_L2_CACHE) # false(default)/true
CanAccessCore2 : true
# Virtual Address Mappings
IORegisterMapping:
- 1ff00000-1ff7ffff # DSP memory
MemoryMapping:
- 1f000000-1f5fffff:r # VRAM
# Accessible SVCs, <Name>:<ID>
SystemCallAccess:
ControlMemory: 1
QueryMemory: 2
ExitProcess: 3
GetProcessAffinityMask: 4
SetProcessAffinityMask: 5
GetProcessIdealProcessor: 6
SetProcessIdealProcessor: 7
CreateThread: 8
ExitThread: 9
SleepThread: 10
GetThreadPriority: 11
SetThreadPriority: 12
GetThreadAffinityMask: 13
SetThreadAffinityMask: 14
GetThreadIdealProcessor: 15
SetThreadIdealProcessor: 16
GetCurrentProcessorNumber: 17
Run: 18
CreateMutex: 19
ReleaseMutex: 20
CreateSemaphore: 21
ReleaseSemaphore: 22
CreateEvent: 23
SignalEvent: 24
ClearEvent: 25
CreateTimer: 26
SetTimer: 27
CancelTimer: 28
ClearTimer: 29
CreateMemoryBlock: 30
MapMemoryBlock: 31
UnmapMemoryBlock: 32
CreateAddressArbiter: 33
ArbitrateAddress: 34
CloseHandle: 35
WaitSynchronization1: 36
WaitSynchronizationN: 37
SignalAndWait: 38
DuplicateHandle: 39
GetSystemTick: 40
GetHandleInfo: 41
GetSystemInfo: 42
GetProcessInfo: 43
GetThreadInfo: 44
ConnectToPort: 45
SendSyncRequest1: 46
SendSyncRequest2: 47
SendSyncRequest3: 48
SendSyncRequest4: 49
SendSyncRequest: 50
OpenProcess: 51
OpenThread: 52
GetProcessId: 53
GetProcessIdOfThread: 54
GetThreadId: 55
GetResourceLimit: 56
GetResourceLimitLimitValues: 57
GetResourceLimitCurrentValues: 58
GetThreadContext: 59
Break: 60
OutputDebugString: 61
ControlPerformanceCounter: 62
CreatePort: 71
CreateSessionToPort: 72
CreateSession: 73
AcceptSession: 74
ReplyAndReceive1: 75
ReplyAndReceive2: 76
ReplyAndReceive3: 77
ReplyAndReceive4: 78
ReplyAndReceive: 79
BindInterrupt: 80
UnbindInterrupt: 81
InvalidateProcessDataCache: 82
StoreProcessDataCache: 83
FlushProcessDataCache: 84
StartInterProcessDma: 85
StopDma: 86
GetDmaState: 87
RestartDma: 88
DebugActiveProcess: 96
BreakDebugProcess: 97
TerminateDebugProcess: 98
GetProcessDebugEvent: 99
ContinueDebugEvent: 100
GetProcessList: 101
GetThreadList: 102
GetDebugThreadContext: 103
SetDebugThreadContext: 104
QueryDebugProcessMemory: 105
ReadProcessMemory: 106
WriteProcessMemory: 107
SetHardwareBreakPoint: 108
GetDebugThreadParam: 109
ControlProcessMemory: 112
MapProcessMemory: 113
UnmapProcessMemory: 114
CreateCodeSet: 115
CreateProcess: 117
TerminateProcess: 118
SetProcessResourceLimits: 119
CreateResourceLimit: 120
SetResourceLimitValues: 121
AddCodeSegment: 122
Backdoor: 123
KernelSetState: 124
QueryProcessMemory: 125
# Service List
# Maximum 34 services (32 if firmware is prior to 9.6.0)
ServiceAccessControl:
- APT:U
- ac:u
- am:net
- boss:U
- cam:u
- cecd:u
- cfg:nor
- cfg:u
- csnd:SND
- dsp::DSP
- frd:u
- fs:USER
- gsp::Gpu
- gsp::Lcd
- hid:USER
- http:C
- ir:rst
- ir:u
- ir:USER
- mic:u
- mcu::HWC
- ndm:u
- news:s
- nwm::EXT
- nwm::UDS
- ptm:sysm
- ptm:u
- pxi:dev
- soc:U
- ssl:C
- y2r:u
SystemControlInfo:
SaveDataSize: 0KB # Change if the app uses savedata
RemasterVersion: $(APP_VERSION_MAJOR)
StackSize: 0x40000
# Modules that run services listed above should be included below
# Maximum 48 dependencies
# <module name>:<module titleid>
Dependency:
ac: 0x0004013000002402
#act: 0x0004013000003802
am: 0x0004013000001502
boss: 0x0004013000003402
camera: 0x0004013000001602
cecd: 0x0004013000002602
cfg: 0x0004013000001702
codec: 0x0004013000001802
csnd: 0x0004013000002702
dlp: 0x0004013000002802
dsp: 0x0004013000001a02
friends: 0x0004013000003202
gpio: 0x0004013000001b02
gsp: 0x0004013000001c02
hid: 0x0004013000001d02
http: 0x0004013000002902
i2c: 0x0004013000001e02
ir: 0x0004013000003302
mcu: 0x0004013000001f02
mic: 0x0004013000002002
ndm: 0x0004013000002b02
news: 0x0004013000003502
#nfc: 0x0004013000004002
nim: 0x0004013000002c02
nwm: 0x0004013000002d02
pdn: 0x0004013000002102
ps: 0x0004013000003102
ptm: 0x0004013000002202
#qtm: 0x0004013020004202
ro: 0x0004013000003702
socket: 0x0004013000002e02
spi: 0x0004013000002302
ssl: 0x0004013000002f02

13
app/buildtools/README.md Normal file
View File

@ -0,0 +1,13 @@
# buildtools
Common build tools for C/C++ projects. Supports PC and 3DS targets.
Dependencies:
* [makerom](https://github.com/profi200/Project_CTR/tree/master/makerom/) - Building 3DS/CIA files.
* [bannertool](https://github.com/Steveice10/bannertool/) - Building 3DS/CIA files.
* [citra](https://github.com/citra-emu/citra/) - Testing 3DS/CIA builds on a PC.
* zip - Zipping up output files.
* xxd - Embedding binary files in executables.
* python - Installing CIA builds to a 3DS over the network.
Credit for 3DS homebrew logo goes to [PabloMK7](http://gbatemp.net/members/pablomk7.345712/).

757
app/buildtools/make_base Normal file
View File

@ -0,0 +1,757 @@
# Make Variables
#
# TARGET: Optional; Platform to build for.
# - Supported values: NATIVE, NATIVE32, NATIVE64, WIN32, WIN64, MAC32, MAC64, LINUX32, LINUX64, 3DS, WIIU, SWITCH
# - Default value: NATIVE
# LIBRARY: Optional; Whether to output a library.
# - Supported values: 0, 1
# - Default value: 0
#
# All:
# - NAME: Project name.
# - INCLUDE_DIRS: Directories containing include headers.
# - SOURCE_DIRS: Directories containing source files to compile.
# - BUILD_DIR: Directory to store build files in.
# - OUTPUT_DIR: Directory to output the final results to.
# - LIBRARY_DIRS: Optional; Directories containing libraries to compile against.
# - LIBRARIES: Optional; Libraries to compile against.
# - EXTRA_OUTPUT_FILES: Optional; Extra files to copy to the output directory.
# - BUILD_FLAGS: Optional; Shared build flags.
# - BUILD_FLAGS_CC: Optional; C build flags.
# - BUILD_FLAGS_CXX: Optional; C++ build flags.
# - RUN_FLAGS: Optional; Flags to pass when running output executables.
# - VERSION_MAJOR: Optional; Major version number.
# - Default value: 0
# - VERSION_MINOR: Optional; Minor version number.
# - Default value: 0
# - VERSION_MICRO: Optional; Micro version number.
# - Default value: 0
#
# 3DS/Wii U/Switch:
# - TITLE: Optional; Formal application title, used in metadata.
# - Default value: NAME stripped of spaces.
# - AUTHOR: Optional; Application author.
# - Default value: "Unknown"
# - REMOTE_IP: Optional; IP to send executable to when running on hardware. Intended to be set in command line.
# - Default value: 127.0.0.1
#
# 3DS/Wii U:
# - DESCRIPTION: Optional; Application description.
# - Default value: "No description."
#
# 3DS:
# - PRODUCT_CODE: CIA/3DS product code.
# - UNIQUE_ID: CIA/3DS unique ID.
# - BANNER_AUDIO: Audio file to use in the CIA/3DS banner.
# - Supported file types: WAV, CWAV
# - BANNER_IMAGE: Graphics to use in the CIA/3DS banner.
# - Supported file types: 256x128 PNG, CGFX
# - ICON: Application icon.
# - Supported file types: 48x48 PNG
# - Category: Optional; CIA/3DS category.
# - Supported values: Application, SystemApplication, Applet, Firmware, Base, DlpChild, Demo, Contents, SystemContents, SharedContents, AddOnContents, Patch, AutoUpdateContents
# - Default value: Application
# - USE_ON_SD: Optional; Whether the CIA/3DS should be installed to the SD card.
# - Supported values: true, false
# - Default value: true
# - MEMORY_TYPE: Optional; CIA/3DS application memory layout.
# - Supported values: Application, System, Base
# - Default value: Application
# - SYSTEM_MODE: Optional; CIA/3DS legacy system mode.
# - Supported values: 32MB, 64MB, 72MB, 80MB, 96MB
# - Default value: 64MB
# - SYSTEM_MODE_EXT: Optional; CIA/3DS extended system mode.
# - Supported values: Legacy, 124MB, 178MB
# - Default value: Legacy
# - CPU_MODE: Optional; CIA/3DS CPU frequency. 804MHz is N3DS-only.
# - Supported values: 268MHz, 804MHz
# - Default value: 268MHz
# - ENABLE_L2_CACHE: Optional; Whether the CIA/3DS should use the N3DS L2 cache.
# - Supported values: true, false
# - Default value: false
# - ICON_FLAGS: Optional; Flags to pass to bannertool when making an SMDH icon.
# - ROMFS_DIR: Optional; Directory containing RomFS files.
# - LOGO: Optional; Logo animation to use when launching the CIA/3DS.
# - Supported file types: BCMA.LZ
#
# Wii U:
# - ICON: Application icon.
# - Supported file types: 256x96 PNG
# - LONG_DESCRIPTION: Optional; Long version of the description field.
# - Default value: Value of DESCRIPTION.
#
# Switch:
# - TITLE_ID: Optional; Application title ID.
# - ICON: Optional; Application icon.
# - Supported file types: 256x256 JPEG
# PROLOGUE #
TARGET ?= NATIVE
LIBRARY ?= 0
ALL_PC_TARGETS := WIN32 WIN64 MAC32 MAC64 LINUX32 LINUX64
ALL_SPECIFIC_TARGETS := $(ALL_PC_TARGETS) 3DS WIIU SWITCH
ALL_TARGETS := NATIVE NATIVE32 NATIVE64 $(ALL_SPECIFIC_TARGETS)
TARGETS :=
ifneq (1,$(words $(TARGET)))
TARGETS := $(TARGET)
else ifeq ($(TARGET),ALL)
TARGETS := $(ALL_SPECIFIC_TARGETS)
else ifeq ($(TARGET),PC)
TARGETS := $(ALL_PC_TARGETS)
endif
ifneq ($(TARGETS),)
.PHONY: all clean
all:
@$(foreach target,$(TARGETS),make --no-print-directory TARGET=$(target);)
else
ifneq ($(MAKECMDGOALS),clean)
$(info Building for $(TARGET)...)
endif
ifeq ($(TARGET),$(filter $(TARGET),3DS WIIU SWITCH))
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro")
endif
endif
# TOOLS #
BUILDTOOLS_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
define createdirrule
$(1): | $(dir $(1))
ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED
$(dir $(1)):
@mkdir -p $$@
$(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1
endif
endef
rwildcard=$(wildcard $1/$2) $(foreach d,$(wildcard $1/*),$(call rwildcard,$d,$2))
# INITIAL COMMON SETUP #
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
STRIPPED_NAME := $(subst $(SPACE),,$(NAME))
ifeq ($(OS),Windows_NT)
HOST_OS := windows
ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
HOST_ARCH := x86_64
else
HOST_ARCH := i686
endif
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
HOST_OS := mac
else ifeq ($(UNAME_S),Linux)
HOST_OS := linux
else
$(error "Unsupported host OS.")
endif
UNAME_M := $(shell uname -m)
ifeq ($(UNAME_M),$(filter $(UNAME_M),x86_64 amd64))
HOST_ARCH := x86_64
else ifeq ($(UNAME_M),$(filter $(UNAME_M),i386 i686))
HOST_ARCH := i686
else
$(error "Unsupported host architecture.")
endif
endif
ifeq ($(TARGET),NATIVE)
TARGET_OS := $(HOST_OS)
TARGET_ARCH := $(HOST_ARCH)
else ifeq ($(TARGET),NATIVE32)
TARGET_OS := $(HOST_OS)
TARGET_ARCH := i686
else ifeq ($(TARGET),NATIVE64)
TARGET_OS := $(HOST_OS)
TARGET_ARCH := x86_64
else ifeq ($(TARGET),WIN32)
TARGET_OS := windows
TARGET_ARCH := i686
else ifeq ($(TARGET),WIN64)
TARGET_OS := windows
TARGET_ARCH := x86_64
else ifeq ($(TARGET),LINUX32)
TARGET_OS := linux
TARGET_ARCH := i686
else ifeq ($(TARGET),LINUX64)
TARGET_OS := linux
TARGET_ARCH := x86_64
else ifeq ($(TARGET),MAC32)
TARGET_OS := mac
TARGET_ARCH := i686
else ifeq ($(TARGET),MAC64)
TARGET_OS := mac
TARGET_ARCH := x86_64
else ifeq ($(TARGET),3DS)
TARGET_OS := 3ds
TARGET_ARCH := arm
else ifeq ($(TARGET),WIIU)
TARGET_OS := wiiu
TARGET_ARCH := ppc
else ifeq ($(TARGET),SWITCH)
TARGET_OS := switch
TARGET_ARCH := aarch64
else
$(error "Unknown target. Supported targets: $(ALL_TARGETS)")
endif
TARGET_BUILD_DIR := $(BUILD_DIR)/$(TARGET_OS)-$(TARGET_ARCH)
TARGET_OUTPUT_DIR := $(OUTPUT_DIR)/$(TARGET_OS)-$(TARGET_ARCH)
BUILT_FILTER := $(patsubst %.bin,$(TARGET_BUILD_DIR)/%.bin.o,$(BUILD_FILTER)) \
$(patsubst %.c,$(TARGET_BUILD_DIR)/%.o,$(BUILD_FILTER)) \
$(patsubst %.cpp,$(TARGET_BUILD_DIR)/%.o,$(BUILD_FILTER)) \
$(patsubst %.s,$(TARGET_BUILD_DIR)/%.o,$(BUILD_FILTER))
OBJECT_FILES := $(foreach dir,$(SOURCE_DIRS), \
$(patsubst %.bin,$(TARGET_BUILD_DIR)/%.bin.o,$(call rwildcard,$(dir),*.bin)) \
$(patsubst %.c,$(TARGET_BUILD_DIR)/%.o,$(call rwildcard,$(dir),*.c)) \
$(patsubst %.cpp,$(TARGET_BUILD_DIR)/%.o,$(call rwildcard,$(dir),*.cpp)) \
$(patsubst %.s,$(TARGET_BUILD_DIR)/%.o,$(call rwildcard,$(dir),*.s)) \
)
OBJECT_FILES := $(filter-out $(BUILT_FILTER),$(OBJECT_FILES))
OUTPUT_ZIP_FILE ?= $(OUTPUT_DIR)/$(STRIPPED_NAME).zip
ifeq ($(strip $(VERSION_MAJOR)),)
VERSION_MAJOR := 0
endif
ifeq ($(strip $(VERSION_MINOR)),)
VERSION_MINOR := 0
endif
ifeq ($(strip $(VERSION_MICRO)),)
VERSION_MICRO := 0
endif
LD_FLAGS := $(patsubst %,-L%/lib,$(LIBRARY_DIRS)) $(patsubst %,-l%,$(LIBRARIES)) -Wl,-Map,MyApp.map
COMMON_CC_FLAGS := $(sort $(foreach dir,$(SOURCE_DIRS),$(patsubst %,-I$(TARGET_BUILD_DIR)/%,$(dir $(call rwildcard,$(dir),*))))) $(patsubst %,-I%,$(INCLUDE_DIRS)) $(patsubst %,-I%/include,$(LIBRARY_DIRS)) -g -Wall -DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) -DVERSION_MICRO=$(VERSION_MICRO) $(BUILD_FLAGS)
COMMON_CXX_FLAGS :=
ifeq ($(findstring -O,$(BUILD_FLAGS)),)
COMMON_CC_FLAGS += -O2
endif
# COMMON LIBRARY SETUP #
ifeq ($(LIBRARY),1)
STRIPPED_NAME := lib$(STRIPPED_NAME)
EXTRA_OUTPUT_FILES += $(INCLUDE_DIRS)
endif
# TARGET SETUP #
REMOTE_IP ?= 127.0.0.1 # User-defined
TITLE ?= $(NAME)
AUTHOR ?= "Unknown"
DESCRIPTION ?= "No description."
LONG_DESCRIPTION ?= $(DESCRIPTION)
ifeq ($(TARGET_OS),windows)
ifeq ($(HOST_OS),windows)
AR := ar
AS := as
CC := gcc
CXX := g++
else ifeq ($(TARGET_ARCH),i686)
AR := i686-w64-mingw32-ar
AS := i686-w64-mingw32-as
CC := i686-w64-mingw32-gcc
CXX := i686-w64-mingw32-g++
else ifeq ($(TARGET_ARCH),x86_64)
AR := x86_64-w64-mingw32-ar
AS := x86_64-w64-mingw32-as
CC := x86_64-w64-mingw32-gcc
CXX := x86_64-w64-mingw32-g++
endif
ifeq ($(TARGET_ARCH),i686)
COMMON_CC_FLAGS += -m32
else ifeq ($(TARGET_ARCH),x86_64)
COMMON_CC_FLAGS += -m64
endif
LD_FLAGS += -static-libstdc++ -static-libgcc -static
ifeq ($(LIBRARY),1)
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).dll
COMMON_CC_FLAGS += -fPIC
else
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).exe
endif
else ifeq ($(TARGET_OS),mac)
ifeq ($(HOST_OS),mac)
AR := ar
AS := as
CC := gcc
CXX := g++
else ifeq ($(TARGET_ARCH),i686)
AR := i386-apple-darwin15-ar
AS := i386-apple-darwin15-as
CC := i386-apple-darwin15-gcc
CXX := i386-apple-darwin15-g++
else ifeq ($(TARGET_ARCH),x86_64)
AR := x86_64-apple-darwin15-ar
AS := x86_64-apple-darwin15-as
CC := x86_64-apple-darwin15-gcc
CXX := x86_64-apple-darwin15-g++
endif
ifeq ($(TARGET_ARCH),i686)
COMMON_CC_FLAGS += -m32
else ifeq ($(TARGET_ARCH),x86_64)
COMMON_CC_FLAGS += -m64
endif
ifeq ($(LIBRARY),1)
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).so $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).dylib
COMMON_CC_FLAGS += -fPIC
else
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME)
endif
else ifeq ($(TARGET_OS),linux)
ifeq ($(HOST_OS),linux)
AR := ar
AS := as
CC := gcc
CXX := g++
else ifeq ($(TARGET_ARCH),i686)
AR := i686-pc-linux-gnu-ar
AS := i686-pc-linux-gnu-as
CC := i686-pc-linux-gnu-gcc
CXX := i686-pc-linux-gnu-g++
else ifeq ($(TARGET_ARCH),x86_64)
AR := x86_64-pc-linux-gnu-ar
AS := x86_64-pc-linux-gnu-as
CC := x86_64-pc-linux-gnu-gcc
CXX := x86_64-pc-linux-gnu-g++
endif
ifeq ($(TARGET_ARCH),i686)
COMMON_CC_FLAGS += -m32
else ifeq ($(TARGET_ARCH),x86_64)
COMMON_CC_FLAGS += -m64
endif
ifeq ($(LIBRARY),1)
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).so
COMMON_CC_FLAGS += -fPIC
else
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME)
endif
else ifeq ($(TARGET_OS),3ds)
BUILT_FILTER := $(patsubst %.v.pica,$(TARGET_BUILD_DIR)/%.shbin.o,$(BUILD_FILTER)) \
$(patsubst %.shlist,$(TARGET_BUILD_DIR)/%.shbin.o,$(BUILD_FILTER)) \
OBJECT_FILES := $(foreach dir,$(SOURCE_DIRS), \
$(patsubst %.v.pica,$(TARGET_BUILD_DIR)/%.shbin.o,$(call rwildcard,$(dir),*.v.pica)) \
$(patsubst %.shlist,$(TARGET_BUILD_DIR)/%.shbin.o,$(call rwildcard,$(dir),*.shlist)) \
) $(OBJECT_FILES)
OBJECT_FILES := $(filter-out $(BUILT_FILTER),$(OBJECT_FILES))
DEVKITARM := $(DEVKITPRO)/devkitARM
AR := $(DEVKITARM)/bin/arm-none-eabi-ar
AS := $(DEVKITARM)/bin/arm-none-eabi-as
CC := $(DEVKITARM)/bin/arm-none-eabi-gcc
CXX := $(DEVKITARM)/bin/arm-none-eabi-g++
ifeq ($(LIBRARY),1)
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a
else
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).elf $(TARGET_OUTPUT_DIR)/3ds/$(STRIPPED_NAME)/$(STRIPPED_NAME).3dsx $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).3ds $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).cia
endif
LD_FLAGS += -specs=3dsx.specs
COMMON_CC_FLAGS += -mword-relocations -ffast-math -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -D__3DS__
SERVEFILES := python $(BUILDTOOLS_DIR)/3ds/servefiles.py
CATEGORY ?= Application
USE_ON_SD ?= true
MEMORY_TYPE ?= Application
SYSTEM_MODE ?= 64MB
SYSTEM_MODE_EXT ?= Legacy
CPU_SPEED ?= 268MHz
ENABLE_L2_CACHE ?= false
_3DSXTOOL_FLAGS :=
COMMON_MAKEROM_FLAGS := -rsf $(BUILDTOOLS_DIR)/3ds/template.rsf -target t -exefslogo -icon $(TARGET_BUILD_DIR)/icon.icn -banner $(TARGET_BUILD_DIR)/banner.bnr -major $(VERSION_MAJOR) -minor $(VERSION_MINOR) -micro $(VERSION_MICRO) -DAPP_TITLE="$(TITLE)" -DAPP_PRODUCT_CODE="$(PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(UNIQUE_ID)" -DAPP_SYSTEM_MODE="$(SYSTEM_MODE)" -DAPP_SYSTEM_MODE_EXT="$(SYSTEM_MODE_EXT)" -DAPP_CATEGORY="$(CATEGORY)" -DAPP_USE_ON_SD="$(USE_ON_SD)" -DAPP_MEMORY_TYPE="$(MEMORY_TYPE)" -DAPP_CPU_SPEED="$(CPU_SPEED)" -DAPP_ENABLE_L2_CACHE="$(ENABLE_L2_CACHE)" -DAPP_VERSION_MAJOR="$(VERSION_MAJOR)"
ifneq ("$(wildcard $(ROMFS_DIR))","")
_3DSXTOOL_FLAGS += --romfs=$(ROMFS_DIR)
COMMON_MAKEROM_FLAGS += -DAPP_ROMFS="$(ROMFS_DIR)"
endif
ifneq ("$(wildcard $(LOGO))","")
COMMON_MAKEROM_FLAGS += -logo "$(LOGO)"
else ifneq ($(LOGO),plain)
COMMON_MAKEROM_FLAGS += -logo "$(BUILDTOOLS_DIR)/3ds/logo.bcma.lz"
endif
ifeq ($(suffix $(BANNER_IMAGE)),.cgfx)
BANNER_IMAGE_ARG := -ci
else
BANNER_IMAGE_ARG := -i
endif
ifeq ($(suffix $(BANNER_AUDIO)),.cwav)
BANNER_AUDIO_ARG := -ca
else
BANNER_AUDIO_ARG := -a
endif
else ifeq ($(TARGET_OS),wiiu)
DEVKITPPC := $(DEVKITPRO)/devkitPPC
AR := $(DEVKITPPC)/bin/powerpc-eabi-ar
AS := $(DEVKITPPC)/bin/powerpc-eabi-as
CC := $(DEVKITPPC)/bin/powerpc-eabi-gcc
CXX := $(DEVKITPPC)/bin/powerpc-eabi-g++
ifeq ($(LIBRARY),1)
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a
else
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/$(STRIPPED_NAME).elf $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/icon.png
endif
LD_FLAGS += -Wl,-d,--gc-sections
COMMON_CC_FLAGS += -mwup -mcpu=750 -meabi -mhard-float -ffast-math -DESPRESSO -DWIIU
else ifeq ($(TARGET_OS),switch)
DEVKITA64 := $(DEVKITPRO)/devkitA64
AR := $(DEVKITA64)/bin/aarch64-none-elf-ar
AS := $(DEVKITA64)/bin/aarch64-none-elf-as
CC := $(DEVKITA64)/bin/aarch64-none-elf-gcc
CXX := $(DEVKITA64)/bin/aarch64-none-elf-g++
ifeq ($(LIBRARY),1)
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a
else
OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).elf $(TARGET_OUTPUT_DIR)/switch/$(STRIPPED_NAME)/$(STRIPPED_NAME).nro
endif
LD_FLAGS += -specs=$(DEVKITPRO)/libnx/switch.specs
COMMON_CC_FLAGS += -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -D__SWITCH__
COMMON_CXX_FLAGS += -fno-rtti -fno-exceptions
NRO_FLAGS :=
ifneq ("$(wildcard $(ICON))","")
NRO_FLAGS += --icon=$(ICON)
endif
NACP_FLAGS :=
ifneq ($(TITLE_ID),)
NACP_FLAGS += --titleid=$(TITLE_ID)
endif
endif
# FINAL COMMON SETUP #
CLANG_CC := $(subst gcc,clang,$(CC))
CLANG_CC_EXISTS := $(shell which $(CLANG_CC) > /dev/null 2> /dev/null; echo $$?)
ifeq ($(CLANG_CC_EXISTS),0)
CC := $(CLANG_CC)
endif
CLANG_CXX := $(subst g++,clang++,$(CXX))
CLANG_CXX_EXISTS := $(shell which $(CLANG_CXX) > /dev/null 2> /dev/null; echo $$?)
ifeq ($(CLANG_CXX_EXISTS),0)
CXX := $(CLANG_CXX)
endif
CC_FLAGS := $(COMMON_CC_FLAGS) $(BUILD_FLAGS_CC)
CXX_FLAGS := $(COMMON_CC_FLAGS) $(COMMON_CXX_FLAGS) $(BUILD_FLAGS_CXX)
ifeq ($(findstring -std,$(BUILD_FLAGS_CC)),)
CC_FLAGS += -std=gnu11
endif
ifeq ($(findstring -std,$(BUILD_FLAGS_CXX)),)
CXX_FLAGS += -std=gnu++11
endif
ifneq ($(EXTRA_OUTPUT_FILES),)
EXTRA_OUTPUT_COPY_CMD := cp -r $(EXTRA_OUTPUT_FILES) $(OUTPUT_DIR)
endif
# MAIN RULES #
.PHONY: all run install clean
all: $(OUTPUT_ZIP_FILE)
# TARGET RULES #
ifeq ($(TARGET_OS),3ds)
ifeq ($(LIBRARY),1)
install: $(OUTPUT_ZIP_FILE)
@mkdir -p $(DEVKITPRO)/$(STRIPPED_NAME)
@cp -r $(TARGET_OUTPUT_DIR)/* $(DEVKITPRO)/$(STRIPPED_NAME)
@echo "Installed."
else
run: $(OUTPUT_ZIP_FILE)
@echo "Running..."
@citra $(RUN_FLAGS) $(TARGET_OUTPUT_DIR)/3ds/$(STRIPPED_NAME)/$(STRIPPED_NAME).3dsx
runhw: $(OUTPUT_ZIP_FILE)
@echo "Running..."
@3dslink --address $(REMOTE_IP) $(TARGET_OUTPUT_DIR)/3ds/$(STRIPPED_NAME)/$(STRIPPED_NAME).3dsx
install: $(OUTPUT_ZIP_FILE)
@echo "Installing..."
@$(SERVEFILES) $(REMOTE_IP) $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).cia
@echo "Installed."
endif
$(TARGET_BUILD_DIR)/%.shbin.o: $(TARGET_BUILD_DIR)/%.shbin.c
@echo $@
@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d $< -o $@
define shader-as
$(eval CURBIN := $(patsubst %.shbin.c,%.shbin,$@))
@picasso -o $(CURBIN) $1
@cd $(dir $(CURBIN)); \
xxd -i $(notdir $(CURBIN)) "$(CURDIR)/$@"
echo "extern const u8" `(echo $(notdir $(CURBIN)) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
echo "extern const u32" `(echo $(notdir $(CURBIN)) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_len";" >> `(echo $(CURBIN) | tr . _)`.h
endef
$(TARGET_BUILD_DIR)/%.shbin.c: %.v.pica %.g.pica
@echo $@
@$(call shader-as,$^)
$(TARGET_BUILD_DIR)/%.shbin.c: %.v.pica
@echo $@
@$(call shader-as,$<)
$(TARGET_BUILD_DIR)/%.shbin.c: %.shlist
@echo $@
@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
%.bnr: $(BANNER_IMAGE) $(BANNER_AUDIO)
@echo $@
@bannertool makebanner $(BANNER_IMAGE_ARG) $(BANNER_IMAGE) $(BANNER_AUDIO_ARG) $(BANNER_AUDIO) -o $@ > /dev/null
%.icn: $(ICON)
@echo $@
@bannertool makesmdh -s "$(TITLE)" -l "$(TITLE) - $(DESCRIPTION)" -p "$(AUTHOR)" -i $(ICON) $(ICON_FLAGS) -o $@ > /dev/null
%.smdh: $(ICON)
@echo $@
@smdhtool --create "$(TITLE)" "$(DESCRIPTION)" "$(AUTHOR)" $(ICON) $@
$(TARGET_OUTPUT_DIR)/3ds/$(STRIPPED_NAME)/%.3dsx: $(TARGET_OUTPUT_DIR)/%.elf $(TARGET_BUILD_DIR)/meta.smdh
@echo $@
@3dsxtool $< $@ --smdh=$(word 2,$^) $(_3DSXTOOL_FLAGS)
%.3ds: %.elf $(TARGET_BUILD_DIR)/banner.bnr $(TARGET_BUILD_DIR)/icon.icn
@echo $@
@makerom -f cci -o $@ -elf $< -DAPP_ENCRYPTED=true $(COMMON_MAKEROM_FLAGS)
%.cia: %.elf $(TARGET_BUILD_DIR)/banner.bnr $(TARGET_BUILD_DIR)/icon.icn
@echo $@
@makerom -f cia -o $@ -elf $< -DAPP_ENCRYPTED=false $(COMMON_MAKEROM_FLAGS)
else ifeq ($(TARGET_OS),wiiu)
ifeq ($(LIBRARY),1)
install: $(OUTPUT_ZIP_FILE)
@mkdir -p $(DEVKITPRO)/$(STRIPPED_NAME)
@cp -r $(TARGET_OUTPUT_DIR)/* $(DEVKITPRO)/$(STRIPPED_NAME)
@echo "Installed."
else
runhw: $(OUTPUT_ZIP_FILE)
@echo "Running..."
@WIILOAD=tcp:$(REMOTE_IP) wiiload $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/$(STRIPPED_NAME).elf $(RUN_FLAGS)
endif
$(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml:
@echo $@
@cp $(BUILDTOOLS_DIR)/wiiu/meta_template.xml $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
@sed -i -- 's/$$(TITLE)/$(subst /,\/,$(TITLE))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
@sed -i -- 's/$$(AUTHOR)/$(subst /,\/,$(AUTHOR))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
@sed -i -- 's/$$(VERSION)/$(subst /,\/,$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_MICRO))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
@sed -i -- 's/$$(RELEASE_DATE)/$(subst /,\/,$(shell date +'%Y%m%d%H%M%S'))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
@sed -i -- 's/$$(SHORT_DESCRIPTION)/$(subst /,\/,$(DESCRIPTION))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
@sed -i -- 's/$$(LONG_DESCRIPTION)/$(subst /,\/,$(LONG_DESCRIPTION))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
$(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/icon.png:
@echo $@
@cp $(ICON) $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/icon.png
else ifeq ($(TARGET_OS),switch)
ifeq ($(LIBRARY),1)
install: $(OUTPUT_ZIP_FILE)
@mkdir -p $(DEVKITPRO)/$(STRIPPED_NAME)
@cp -r $(TARGET_OUTPUT_DIR)/* $(DEVKITPRO)/$(STRIPPED_NAME)
@echo "Installed."
else
run: $(OUTPUT_ZIP_FILE)
@echo "Running..."
@yuzu-cmd $(RUN_FLAGS) $(TARGET_OUTPUT_DIR)/switch/$(STRIPPED_NAME)/$(STRIPPED_NAME).nro
runhw: $(OUTPUT_ZIP_FILE)
@echo "Running..."
@nxlink --address $(REMOTE_IP) $(TARGET_OUTPUT_DIR)/switch/$(STRIPPED_NAME)/$(STRIPPED_NAME).nro
endif
%.nacp:
@echo $@
@nacptool --create "$(TITLE)" "$(AUTHOR)" "$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_MICRO)" $@ $(NACP_FLAGS)
$(TARGET_OUTPUT_DIR)/switch/$(STRIPPED_NAME)/%.nro: $(TARGET_OUTPUT_DIR)/%.elf $(TARGET_BUILD_DIR)/meta.nacp
@echo $@
@elf2nro $< $@ --nacp=$(word 2,$^) $(NRO_FLAGS)
else ifeq ($(TARGET_OS),windows)
ifeq ($(HOST_OS),$(filter $(HOST_OS),linux mac))
ifneq ($(LIBRARY),1)
run: $(OUTPUT_FILES) $(OUTPUT_ZIP_FILE)
@echo "Running..."
@wine ./$< $(RUN_FLAGS)
endif
else ifeq ($(HOST_OS),windows)
ifneq ($(LIBRARY),1)
run: $(OUTPUT_FILES) $(OUTPUT_ZIP_FILE)
@echo "Running..."
@./$< $(RUN_FLAGS)
endif
endif
else ifeq ($(TARGET_OS),$(filter $(TARGET_OS),mac linux))
ifeq ($(HOST_OS),$(TARGET_OS))
ifeq ($(LIBRARY),1)
install: $(OUTPUT_ZIP_FILE)
@install -m 0755 $(OUTPUT_FILES) /usr/local/lib
@install -m 0644 $(foreach dir,$(INCLUDE_DIRS),$(wildcard $(dir)/*)) /usr/local/include
@echo "Installed."
else
run: $(OUTPUT_FILES) $(OUTPUT_ZIP_FILE)
@echo "Running..."
@./$< $(RUN_FLAGS)
install: $(OUTPUT_ZIP_FILE)
@install -m 0755 $(OUTPUT_FILES) /usr/local/bin
@echo "Installed."
endif
endif
endif
# COMMON RULES #
$(OUTPUT_ZIP_FILE): $(OUTPUT_FILES) $(EXTRA_OUTPUT_FILES)
$(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME): $(OBJECT_FILES)
@echo $@
@$(CXX) $(CXX_FLAGS) $^ -o $@ $(LD_FLAGS)
%.elf: $(OBJECT_FILES)
@echo $@
@$(CXX) $(CXX_FLAGS) $^ -o $@ $(LD_FLAGS)
%.a: $(OBJECT_FILES)
@echo $@
@$(AR) -rc $@ $^
%.so: $(OBJECT_FILES)
@echo $@
@$(CXX) $(CXX_FLAGS) -shared $^ -o $@ $(LD_FLAGS)
%.exe: $(OBJECT_FILES)
@echo $@
@$(CXX) $(CXX_FLAGS) $^ -o $@ $(LD_FLAGS)
%.dll: $(OBJECT_FILES)
@echo $@
@$(CXX) $(CXX_FLAGS) -shared $^ -o $@ $(LD_FLAGS)
%.dylib: $(OBJECT_FILES)
@echo $@
@$(CXX) $(CXX_FLAGS) -dynamiclib -undefined suppress -flat_namespace $^ -o $@ $(LD_FLAGS)
$(TARGET_BUILD_DIR)/%.o: %.c
@echo $@
@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d $< -o $@
$(TARGET_BUILD_DIR)/%.o: %.cpp
@echo $@
@$(CXX) -c $(CXX_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d $< -o $@
$(TARGET_BUILD_DIR)/%.o: %.s
@echo $@
@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d -x assembler-with-cpp $< -o $@
$(TARGET_BUILD_DIR)/%.o: %.S
@echo $@
@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d -x assembler-with-cpp $< -o $@
$(TARGET_BUILD_DIR)/%.bin.o: $(TARGET_BUILD_DIR)/%.bin.c
@echo $@
@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d $< -o $@
$(TARGET_BUILD_DIR)/%.bin.c: %.bin
@echo $@
@cd $(<D); \
xxd -i $(<F) "$(CURDIR)/$@"
@echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(TARGET_BUILD_DIR)/$< | tr . _)`.h
@echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_len";" >> `(echo $(TARGET_BUILD_DIR)/$< | tr . _)`.h
$(foreach file,$(OBJECT_FILES),$(eval $(call createdirrule,$(file))))
$(foreach file,$(OUTPUT_FILES),$(eval $(call createdirrule,$(file))))
# DEPENDS #
DEPENDS := $(OBJECT_FILES:.o=.d)
-include $(DEPENDS)
endif
# CLEAN #
clean:
@rm -rf $(BUILD_DIR) $(OUTPUT_DIR)
@echo "Cleaned."

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<app version="1">
<name>$(TITLE)</name>
<coder>$(AUTHOR)</coder>
<version>$(VERSION)</version>
<release_date>$(RELEASE_DATE)</release_date>
<short_description>$(SHORT_DESCRIPTION)</short_description>
<long_description>$(LONG_DESCRIPTION)</long_description>
</app>

BIN
app/images/logo.bclim Normal file

Binary file not shown.

68
app/includes/3gx.h Normal file
View File

@ -0,0 +1,68 @@
#include "3ds/types.h"
#define _3GX_MAGIC (0x3230303024584733) /* "3GX$0002" */
typedef struct CTR_PACKED
{
u32 authorLen;
const char* authorMsg;
u32 titleLen;
const char* titleMsg;
u32 summaryLen;
const char* summaryMsg;
u32 descriptionLen;
const char* descriptionMsg;
union {
u32 flags;
struct {
u32 embeddedExeLoadFunc : 1;
u32 embeddedSwapSaveLoadFunc : 1;
u32 memoryRegionSize : 2;
u32 compatibility : 2;
u32 eventsSelfManaged : 1;
u32 swapNotNeeded : 1;
u32 unused : 24;
};
};
u32 exeLoadChecksum;
u32 builtInLoadExeArgs[4];
u32 builtInSwapSaveLoadArgs[4];
} _3gx_Infos;
typedef struct CTR_PACKED
{
u32 count;
u32 * titles;
} _3gx_Targets;
typedef struct CTR_PACKED
{
u32 nbSymbols;
u32 symbolsOffset;
u32 nameTableOffset;
} _3gx_Symtable;
typedef struct CTR_PACKED
{
u32 codeOffset;
u32 rodataOffset;
u32 dataOffset;
u32 codeSize;
u32 rodataSize;
u32 dataSize;
u32 bssSize;
u32 exeLoadFuncOffset; // NOP terminated
u32 swapSaveFuncOffset; // NOP terminated
u32 swapLoadFuncOffset; // NOP terminated
} _3gx_Executable;
typedef struct CTR_PACKED
{
u64 magic;
u32 version;
u32 reserved;
_3gx_Infos infos;
_3gx_Executable executable;
_3gx_Targets targets;
_3gx_Symtable symtable;
} _3gx_Header;

74
app/includes/BCLIM.hpp Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include "3ds.h"
#include "tuple"
#include "CTRPluginFramework/Vector.hpp"
#include "CTRPluginFramework/Rect.hpp"
#include "CTRPluginFramework/Color.hpp"
namespace CTRPluginFramework {
class BCLIM {
public:
enum class TextureFormat : u32 {
L8 = 0,
A8 = 1,
LA4 = 2,
LA8 = 3,
HILO8 = 4,
RGB565 = 5,
RGB8 = 6,
RGBA5551 = 7,
RGBA4 = 8,
RGBA8 = 9,
ETC1 = 10,
ETC1A4 = 11,
L4 = 12,
A4 = 13,
};
struct Header {
u32 magic;
u16 endian;
u16 headerSize;
u32 version;
u32 fileSize;
u32 blockCount;
struct {
u32 magic;
u32 imagHeaderSize;
u16 width;
u16 height;
TextureFormat format;
} imag;
u32 dataLength;
};
BCLIM(void* bclimData, u32 bclimSize) : BCLIM(bclimData, (Header*)((u32)bclimData + bclimSize - 0x28)) {}
BCLIM(void* bclimData, Header* bclimHeader) : data(bclimData), header(bclimHeader) {}
using ColorBlendCallback = Color(*)(const Color &, const Color &);
static std::pair<bool, ColorBlendCallback> OpaqueBlend() {
return std::make_pair(false, OpaqueBlendFunc);
}
using RenderBackend = void(*)(void*, bool, Color*, int posX, int posY);
static std::pair<void*, RenderBackend> RenderInterface() {
return std::make_pair(nullptr, RenderInterfaceBackend);
}
void Render(const Rect<int>& position, std::pair<void*, RenderBackend> backend = RenderInterface(), const Rect<int>& crop = Rect<int>(0, 0, INT32_MAX, INT32_MAX), const Rect<int>& limits = Rect<int>(0, 0, 400, 240), std::pair<bool, ColorBlendCallback> colorBlend = OpaqueBlend());
void* data;
private:
Header* header;
static const u8 textureTileOrder[16];
static const u8 etc1Modifiers[8][2];
template<typename T>
inline T GetDataAt(int offset) {
return *(T*)(((u32)data) + offset);
}
static void RenderInterfaceBackend(void* usrData, bool isRead, Color* c, int posX, int posY);
static Color OpaqueBlendFunc(const Color& dst, const Color& src);
};
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "CTRPluginFramework/System/Mutex.hpp"
#include "CTRPluginFramework/System/Lock.hpp"
#include "CTRPluginFramework/Vector.hpp"
#include "CTRPluginFramework/Rect.hpp"
#include "CTRPluginFramework/Color.hpp"

View File

@ -0,0 +1,37 @@
#pragma once
#include "3ds.h"
#include "CTRPluginFramework/Time.hpp"
namespace CTRPluginFramework
{
class Clock
{
public:
Clock(void) : _startTime(GetCurrentTime()) {}
constexpr Clock(const Time& time) : _startTime(time) {}
__always_inline Time GetElapsedTime(void) const {
return (GetCurrentTime() - _startTime);
}
__always_inline bool HasTimePassed(const Time& time) const {
return (GetElapsedTime() >= time);
}
__always_inline Time Restart(void) {
const Time now = GetCurrentTime();
const Time ret = now - _startTime;
_startTime = now;
return (ret);
}
private:
Time _startTime;
static __always_inline Time GetCurrentTime(void)
{
return Ticks(svcGetSystemTick());
}
};
}

View File

@ -0,0 +1,67 @@
#pragma once
#include "3ds/types.h"
#include <algorithm>
#include <string>
namespace CTRPluginFramework
{
class Color
{
public:
enum class BlendMode
{
Alpha,
Add,
Sub,
Mul,
None
};
constexpr Color(void) : a(255), b(0), g(0), r(0) {}
constexpr Color(u32 color) : raw(color) {}
constexpr Color(u8 red, u8 green, u8 blue, u8 alpha = 255) : a(alpha), b(blue), g(green), r(red) {}
inline u32 ToU32(void) const { return raw; };
Color &Fade(float fading);
Color Blend(const Color &color, BlendMode mode) const;
inline bool operator == (const Color &right) const {return raw == right.raw;}
inline bool operator != (const Color &right) const {return raw != right.raw;}
bool operator < (const Color &right) const;
bool operator <= (const Color &right) const;
bool operator > (const Color &right) const;
bool operator >= (const Color &right) const;
Color operator + (const Color &right) const;
Color operator - (const Color &right) const;
Color operator * (const Color &right) const;
Color &operator += (const Color &right);
Color &operator -= (const Color &right);
Color &operator *= (const Color &right);
operator std::string() const
{
char strColor[5] = { 0 };
strColor[0] = 0x1B;
strColor[1] = std::max((u8)1, r);
strColor[2] = std::max((u8)1, g);
strColor[3] = std::max((u8)1, b);
return strColor;
}
union
{
u32 raw;
struct // Match raw byte order
{
u8 a;
u8 b;
u8 g;
u8 r;
};
};
};
}

View File

@ -0,0 +1,143 @@
#pragma once
#include "CTRPluginFramework/Vector.hpp"
#include <algorithm>
namespace CTRPluginFramework
{
template <typename T>
class Rect
{
public:
Rect();
Rect(const Vector<T>& leftTop, const Vector<T>& size);
Rect(const Vector<T>& leftTop, T width, T height);
Rect(T left, T top, const Vector<T>& size);
Rect(T left, T top, T width, T height);
template <typename U>
explicit Rect(const Rect<U>& rect);
bool Contains(T x, T y) const;
bool Contains(const Vector<T>& point) const;
bool Intersects(const Rect<T>& rect) const;
bool Intersects(const Rect<T>& rect, Rect<T>& intersect) const;
Vector<T> leftTop;
Vector<T> size;
};
template <typename T>
Rect<T>::Rect() : leftTop(0, 0), size(0, 0)
{
}
template <typename T>
Rect<T>::Rect(const Vector<T>& leftTopCorner, const Vector<T>& size)
{
leftTop = leftTopCorner;
this->size = size;
}
template <typename T>
Rect<T>::Rect(const Vector<T>& leftTopCorner, T width, T height)
{
leftTop = leftTopCorner;
size.x = width;
size.y = height;
}
template <typename T>
Rect<T>::Rect(T left, T top, const Vector<T>& size)
{
leftTop.x = left;
leftTop.y = top;
this->size = size;
}
template <typename T>
Rect<T>::Rect(T left, T top, T width, T height)
{
leftTop.x = left;
leftTop.y = top;
size.x = width;
size.y = height;
}
template <typename T>
template <typename U>
Rect<T>::Rect(const Rect<U>& rect)
{
leftTop = reinterpret_cast<T>(rect.leftTop);
size = reinterpret_cast<T>(rect.size);
}
template <typename T>
bool Rect<T>::Contains(T x, T y) const
{
T minX = std::min(leftTop.x, leftTop.x + size.x);
T maxX = std::max(leftTop.x, leftTop.x + size.x);
T minY = std::min(leftTop.y, leftTop.y + size.y);
T maxY = std::max(leftTop.y, leftTop.y + size.y);
return (x >= minX && x < maxX
&& y >= minY && y < maxY);
}
template <typename T>
bool Rect<T>::Contains(const Vector<T>& point) const
{
return (Contains(point.x, point.y));
}
template <typename T>
bool Rect<T>::Intersects(const Rect<T>& rect) const
{
Rect<T> intersect;
return (Intersects(rect, intersect));
}
template <typename T>
bool Rect<T>::Intersects(const Rect<T> &rect, Rect<T> &intersect) const
{
T thisMinX = std::min(leftTop.x, leftTop.x + size.x);
T thisMaxX = std::max(leftTop.x, leftTop.x + size.x);
T thisMinY = std::min(leftTop.y, leftTop.y + size.y);
T thisMaxY = std::max(leftTop.y, leftTop.y + size.y);
T rectMinX = std::min(rect.leftTop.x, rect.leftTop.x + rect.size.x);
T rectMaxX = std::max(rect.leftTop.x, rect.leftTop.x + rect.size.x);
T rectMinY = std::min(rect.leftTop.y, rect.leftTop.y + rect.size.y);
T rectMaxY = std::max(rect.leftTop.y, rect.leftTop.y + rect.size.y);
T intersectLeftX = std::max(thisMinX, rectMinX);
T intersectLeftY = std::max(thisMinY, rectMinY);
T intersectRightX = std::min(thisMaxX, rectMaxX);
T intersectRightY = std::min(thisMaxY, rectMaxY);
if (intersectLeftX < intersectRightX && intersectLeftY < intersectRightY)
{
intersect = Rect<T>(intersectLeftX, intersectLeftY, intersectRightX - intersectLeftX,
intersectRightY - intersectLeftY);
return (true);
}
intersect = Rect<T>(0, 0, 0, 0);
return (false);
}
template <typename T>
bool operator ==(Rect<T> &left, Rect<T> &right)
{
return (left.leftTop == right.leftTop
&& left.size == right.size);
}
template <typename T>
bool operator !=(Rect<T> &left, Rect<T> &right)
{
return (left.leftTop != right.leftTop
&& left.size != right.size);
}
typedef Rect<unsigned int> UIntRect;
typedef Rect<int> IntRect;
typedef Rect<float> FloatRect;
}

View File

@ -0,0 +1,54 @@
#ifndef CTRPLUGINFRAMEWORK_SYSTEM_LOCK_HPP
#define CTRPLUGINFRAMEWORK_SYSTEM_LOCK_HPP
#include "3ds.h"
namespace CTRPluginFramework
{
class Mutex;
class Lock
{
public:
inline explicit Lock(LightLock &llock) :
_type{LIGHTLOCK}, _llock{&llock}
{
LightLock_Lock(_llock);
}
inline explicit Lock(RecursiveLock &rlock) :
_type{RECLOCK}, _rlock{&rlock}
{
RecursiveLock_Lock(_rlock);
}
inline explicit Lock(Mutex &mutex) :
_type{MUTEX}, _mutex{&mutex}
{
mutex.Lock();
}
inline ~Lock(void)
{
if (_type == LIGHTLOCK)
LightLock_Unlock(_llock);
else if (_type == RECLOCK)
RecursiveLock_Unlock(_rlock);
else if (_type == MUTEX)
_mutex->Unlock();
}
private:
static const constexpr u32 LIGHTLOCK = 1;
static const constexpr u32 RECLOCK = 2;
static const constexpr u32 MUTEX = 3;
const u32 _type;
union
{
LightLock *_llock;
RecursiveLock *_rlock;
Mutex *_mutex;
};
};
}
#endif

View File

@ -0,0 +1,41 @@
#ifndef CTRPLUGINFRAMEWORK_SYSTEM_MUTEX_HPP
#define CTRPLUGINFRAMEWORK_SYSTEM_MUTEX_HPP
#include "3ds.h"
namespace CTRPluginFramework
{
class Mutex
{
public:
inline Mutex(void) {
RecursiveLock_Init(&_lock);
}
inline ~Mutex(void) {
// I suppose that we can "force" unlock the mutex
if (_lock.counter > 0)
{
_lock.counter = 1;
RecursiveLock_Unlock(&_lock);
}
}
inline void Lock(void) {
RecursiveLock_Lock(&_lock);
}
// Return true on failure
inline bool TryLock(void) {
return RecursiveLock_TryLock(&_lock) != 0;
}
inline void Unlock(void) {
RecursiveLock_Unlock(&_lock);
}
private:
RecursiveLock _lock;
};
}
#endif

View File

@ -0,0 +1,205 @@
#pragma once
#include "3ds/types.h"
namespace CTRPluginFramework
{
class Time
{
public :
constexpr Time(void) : _ticks(0) {}
float AsSeconds(void) const;
int AsMilliseconds(void) const;
s64 AsMicroseconds(void) const;
inline s64 AsTicks(void) const { return _ticks; }
static const Time Zero; ///< Predefined "zero" time value
static constexpr u32 TicksPerSecond = 268111856U;
private :
friend constexpr Time Seconds(float amount);
friend constexpr Time Milliseconds(int amount);
friend constexpr Time Microseconds(s64 amount);
friend constexpr Time Ticks(s64 amount);
constexpr Time(s64 ticks) : _ticks(ticks) {}
private :
s64 _ticks;
};
constexpr Time Seconds(float amount)
{
return (Time(static_cast<s64>(amount * Time::TicksPerSecond)));
}
constexpr Time Milliseconds(int amount)
{
return (Time(static_cast<s64>(amount * (Time::TicksPerSecond / 1000.f))));
}
constexpr Time Microseconds(s64 amount)
{
return (Time(static_cast<s64>(amount * (Time::TicksPerSecond / 1000000.f))));
}
constexpr Time Ticks(s64 amount)
{
return (Time(amount));
}
inline bool operator ==(const Time& left, const Time& right)
{
return (left.AsTicks() == right.AsTicks());
}
inline bool operator !=(const Time& left, const Time& right)
{
return (left.AsTicks() != right.AsTicks());
}
inline bool operator <(const Time& left, const Time& right)
{
return (left.AsTicks() < right.AsTicks());
}
inline bool operator >(const Time& left, const Time& right)
{
return (left.AsTicks() > right.AsTicks());
}
inline bool operator <=(const Time& left, const Time& right)
{
return (left.AsTicks() <= right.AsTicks());
}
inline bool operator >=(const Time& left, const Time& right)
{
return (left.AsTicks() >= right.AsTicks());
}
inline Time operator -(const Time& right)
{
return (Ticks(-right.AsTicks()));
}
inline Time operator +(const Time& left, const Time& right)
{
return (Ticks(left.AsTicks() + right.AsTicks()));
}
inline Time& operator +=(Time& left, const Time& right)
{
return (left = left + right);
}
inline Time operator -(const Time& left, const Time& right)
{
return (Ticks(left.AsTicks() - right.AsTicks()));
}
inline Time& operator -=(Time& left, const Time& right)
{
return left = left - right;
}
inline Time operator *(const Time& left, float right)
{
return (Seconds(left.AsSeconds() * right));
}
inline Time operator *(const Time& left, s64 right)
{
return (Microseconds(left.AsMicroseconds() * right));
}
inline Time operator *(float left, const Time& right)
{
return (right * left);
}
inline Time operator *(s64 left, const Time& right)
{
return (right * left);
}
inline Time& operator *=(Time& left, float right)
{
return (left = left * right);
}
inline Time& operator *=(Time& left, s64 right)
{
return (left = left * right);
}
inline Time operator /(const Time& left, float right)
{
return Seconds(left.AsSeconds() / right);
}
inline Time operator /(const Time& left, s64 right)
{
return (Microseconds(left.AsMicroseconds() / right));
}
inline Time& operator /=(Time& left, float right)
{
return (left = left / right);
}
inline Time& operator /=(Time& left, s64 right)
{
return (left = left / right);
}
inline float operator /(const Time& left, const Time& right)
{
return (left.AsSeconds() / right.AsSeconds());
}
inline Time operator %(const Time& left, const Time& right)
{
return (Ticks(left.AsTicks() % right.AsTicks()));
}
inline Time& operator %=(Time& left, const Time& right)
{
return (left = left % right);
}
}

View File

@ -0,0 +1,124 @@
#pragma once
#include "3ds/types.h"
namespace CTRPluginFramework
{
template <typename T>
class Vector
{
public:
Vector();
Vector(T x, T y);
template <typename U>
explicit Vector(const Vector<U> &vector);
T x;
T y;
};
template <typename T>
Vector<T>::Vector() : x(0), y(0)
{
}
template <typename T>
Vector<T>::Vector(T x, T y) : x(x), y(y)
{
}
template <typename T>
template <typename U>
Vector<T>::Vector(const Vector<U> &vector) : x(static_cast<T>(vector.x)), y(static_cast<T>(vector.y))
{
}
template <typename T>
Vector<T> operator - (const Vector<T> &vector)
{
return (Vector<T>(-vector.x, -vector.y));
}
template <typename T>
Vector<T> operator - (const Vector<T> &left, const Vector<T> &right)
{
return (Vector<T>(left.x - right.x, left.y - right.y));
}
template <typename T>
Vector<T> &operator -= (const Vector<T> &left, const Vector<T> &right)
{
left.x -= right.x;
left.y -= right.y;
return (left);
}
template <typename T>
Vector<T> operator + (const Vector<T> &left, const Vector<T> &right)
{
return (Vector<T>(left.x + right.x, left.y + right.y));
}
template <typename T>
Vector<T> &operator += (const Vector<T> &left, const Vector<T> &right)
{
left.x += right.x;
left.y += right.y;
return (left);
}
template <typename T>
Vector<T> operator * (const Vector<T> &left, T right)
{
return (Vector<T>(left.x * right, left.y * right));
}
template <typename T>
Vector<T> operator * (const T left, const Vector<T> &right)
{
return (Vector<T>(right.x * left, right.y * left));
}
template <typename T>
Vector<T> &operator *= (Vector<T> &left, const T right)
{
left.x *= right;
left.y *= right;
return (left);
}
template <typename T>
Vector<T> operator / (const Vector<T> &left, const T right)
{
return (Vector<T>(left.x / right, left.y / right));
}
template <typename T>
Vector<T> &operator /= (Vector<T> &left, const T right)
{
left.x /= right;
left.y /= right;
return (left);
}
template <typename T>
bool operator <= (const Vector<T> &left, const Vector<T> &right)
{
return (left.x <= right.x
&& left.y <= right.y);
}
template <typename T>
bool operator >= (const Vector<T> &left, const Vector<T> &right)
{
return (left.x >= right.x
&& left.y >= right.y);
}
typedef Vector<unsigned int> UIntVector;
typedef Vector<int> IntVector;
typedef Vector<float> FloatVector;
typedef Vector<signed short> shortVector;
}

View File

@ -0,0 +1,6 @@
// Only used by Visual Studio Intellisense
#define VERSION_MAJOR 0
#define VERSION_MINOR 0
#define VERSION_REVISION 1
#define SERVER_PORT 5543

47
app/includes/Logger.hpp Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "3ds.h"
#include "string"
#include "queue"
#include "CTRPluginFramework/System/Mutex.hpp"
#include "CTRPluginFramework/System/Lock.hpp"
class Logger {
public:
Logger();
void Start();
void End();
~Logger();
void Raw(bool isTopScr, const char* fmt, ...);
void Info(const char* fmt, ...);
void Debug(const char* fmt, ...);
void Warning(const char* fmt, ...);
void Error(const char* fmt, ...);
void Traffic(const char* fmt, ...);
void Wait();
bool debug_enable = false;
private:
struct PendingLog {
enum class Type : u8 {
RAW,
DEBUG,
INFO,
WARNING,
ERROR,
TRAFFIC,
};
Type type;
bool isTopScr = true;
std::string string;
};
static void LoggerThread(void* arg);
void Handler();
Thread thread;
LightEvent event;
std::queue<PendingLog> pendingLogs;
CTRPluginFramework::Mutex pendingLogsMutex;
bool run = true;
};

5
app/includes/Main.hpp Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include "3ds.h"
#include "Logger.hpp"
extern Logger logger;

154
app/includes/csvc.h Normal file
View File

@ -0,0 +1,154 @@
/* This paricular file is licensed under the following terms: */
/*
* This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
* for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
* and redistribute it freely, subject to the following restrictions:
*
* The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
* If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
*
* Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
* This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <3ds/types.h>
/// Operations for svcControlService
typedef enum ServiceOp
{
SERVICEOP_STEAL_CLIENT_SESSION = 0, ///< Steal a client session given a service or global port name
SERVICEOP_GET_NAME, ///< Get the name of a service or global port given a client or session handle
} ServiceOp;
/**
* @brief Executes a function in supervisor mode, using the supervisor-mode stack.
* @param func Function to execute.
* @param ... Function parameters, up to 3 registers.
*/
Result svcCustomBackdoor(void *func, ...);
///@name I/O
///@{
/**
* @brief Gives the physical address corresponding to a virtual address.
* @param VA Virtual address.
* @param writeCheck whether to check if the VA is writable in supervisor mode
* @return The corresponding physical address, or NULL.
*/
u32 svcConvertVAToPA(const void *VA, bool writeCheck);
/**
* @brief Flushes a range of the data cache (L2C included).
* @param addr Start address.
* @param len Length of the range.
*/
void svcFlushDataCacheRange(void *addr, u32 len);
/**
* @brief Flushes the data cache entirely (L2C included).
*/
void svcFlushEntireDataCache(void);
/**
* @brief Invalidates a range of the instruction cache.
* @param addr Start address.
* @param len Length of the range.
*/
void svcInvalidateInstructionCacheRange(void *addr, u32 len);
/**
* @brief Invalidates the data cache entirely.
*/
void svcInvalidateEntireInstructionCache(void);
///@}
///@name Memory management
///@{
/**
* @brief Maps a block of process memory.
* @param dstProcessHandle Handle of the process to map the memory in (destination)
* @param destAddress Address of the mapped block in the current process.
* @param srcProcessHandle Handle of the process to map the memory from (source)
* @param srcAddress Address of the mapped block in the source process.
* @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes).
*/
Result svcMapProcessMemoryEx(Handle dstProcessHandle, u32 destAddress, Handle srcProcessHandle, u32 vaSrc, u32 size);
/**
* @brief Unmaps a block of process memory.
* @param process Handle of the process to unmap the memory from
* @param destAddress Address of the block of memory to unmap
* @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes).
* This function should only be used to unmap memory mapped with svcMapProcessMemoryEx
*/
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
/**
* @brief Controls memory mapping, with the choice to use region attributes or not.
* @param[out] addr_out The virtual address resulting from the operation. Usually the same as addr0.
* @param addr0 The virtual address to be used for the operation.
* @param addr1 The virtual address to be (un)mirrored by @p addr0 when using @ref MEMOP_MAP or @ref MEMOP_UNMAP.
* It has to be pointing to a RW memory.
* Use NULL if the operation is @ref MEMOP_FREE or @ref MEMOP_ALLOC.
* @param size The requested size for @ref MEMOP_ALLOC and @ref MEMOP_ALLOC_LINEAR.
* @param op Operation flags. See @ref MemOp.
* @param perm A combination of @ref MEMPERM_READ and @ref MEMPERM_WRITE. Using MEMPERM_EXECUTE will return an error.
* Value 0 is used when unmapping memory.
* @param isLoader Whether to use the region attributes
* If a memory is mapped for two or more addresses, you have to use MEMOP_UNMAP before being able to MEMOP_FREE it.
* MEMOP_MAP will fail if @p addr1 was already mapped to another address.
*
* @sa svcControlMemory
*/
Result svcControlMemoryEx(u32* addr_out, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
///@}
///@name System
///@{
/**
* @brief Performs actions related to services or global handles.
* @param op The operation to perform, see @ref ServiceOp.
*
* Examples:
* svcControlService(SERVICEOP_GET_NAME, (char [12])outName, (Handle)clientOrSessionHandle);
* svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, (Handle *)&outHandle, (const char *)name);
*/
Result svcControlService(ServiceOp op, ...);
/**
* @brief Copy a handle from a process to another one.
* @param[out] out The output handle.
* @param outProcess Handle of the process of the output handle.
* @param in The input handle. Pseudo-handles are not accepted.
* @param inProcess Handle of the process of the input handle.
*/
Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess);
/**
* @brief Get the address and class name of the underlying kernel object corresponding to a handle.
* @param[out] outKAddr The output kernel address.
* @param[out] outName Output class name. The buffer should be large enough to contain it.
* @param in The input handle.
*/
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in);
/// Operations for svcControlProcess
typedef enum ProcessOp
{
PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch
///< svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0)
PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access
///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0)
PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT,
PROCESSOP_GET_ON_EXIT_EVENT,
PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process
///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va)
} ProcessOp;
Result svcControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3);
///@}

9
app/includes/logo.h Normal file
View File

@ -0,0 +1,9 @@
/* Generated by bin2c, do not edit manually */
#ifndef ____includes_logo_h_included
#define ____includes_logo_h_included
/* Contents of file .\data\logo.bin */
extern const long int __data_logo_bin_size;
extern const unsigned char __data_logo_bin[32808];
#endif /* ____includes_logo_h_included */

36
app/includes/plgldr.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <3ds/types.h>
typedef struct
{
bool noFlash;
u8 pluginMemoryStrategy;
u8 persistent;
u32 lowTitleId;
char path[256];
u32 config[32];
} PluginLoadParameters;
typedef enum
{
PLG_STRATEGY_NONE = 2,
PLG_STRATEGY_SWAP = 0,
PLG_STRATEGY_MODE3 = 1
} PluginMemoryStrategy;
Result plgLdrInit(void);
void plgLdrExit(void);
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled);
Result PLGLDR__SetPluginLoaderState(bool enabled);
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters);
Result PLGLDR__GetVersion(u32* version);
Result PLGLDR__SetExeDecSettings(void* decFunc, void* args);
Result PLGLDR__ClearPluginLoadParameters();
#ifdef __cplusplus
}
#endif

BIN
app/resources/audio.cwav Normal file

Binary file not shown.

BIN
app/resources/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
app/resources/icon.pdn Normal file

Binary file not shown.

BIN
app/resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
app/resources/logo.bcma.lz Normal file

Binary file not shown.

150
app/sources/BCLIM.cpp Normal file
View File

@ -0,0 +1,150 @@
#include "BCLIM.hpp"
namespace CTRPluginFramework {
static inline int ColorClamp(int Color)
{
if (Color > 255) Color = 255;
if (Color < 0) Color = 0;
return Color;
}
const u8 BCLIM::textureTileOrder[] =
{
0, 1, 4, 5,
2, 3, 6, 7,
8, 9, 12, 13,
10, 11, 14, 15
};
const u8 BCLIM::etc1Modifiers[][2] =
{
{ 2, 8 },
{ 5, 17 },
{ 9, 29 },
{ 13, 42 },
{ 18, 60 },
{ 24, 80 },
{ 33, 106 },
{ 47, 183 }
};
Color BCLIM::OpaqueBlendFunc(const Color& dst, const Color& src) {
return dst;
}
static Color ReadPixel(void* fb, int posX, int posY) {
constexpr u32 _bytesPerPixel = 2;
constexpr u32 _rowSize = 240;
u32 offset = (_rowSize - 1 - posY + posX * _rowSize) * _bytesPerPixel;
union
{
u16 u;
u8 b[2];
} half;
half.u = *reinterpret_cast<u16 *>((u32)fb + offset);
Color c;
c.r = (half.u >> 8) & 0xF8;
c.g = (half.u >> 3) & 0xFC;
c.b = (half.u << 3) & 0xF8;
c.a = 255;
return c;
}
static void WritePixel(void* fb, int posX, int posY, const Color& c) {
constexpr u32 _bytesPerPixel = 2;
constexpr u32 _rowSize = 240;
u32 offset = (_rowSize - 1 - posY + posX * _rowSize) * _bytesPerPixel;
union
{
u16 u;
u8 b[2];
} half;
half.u = (c.r & 0xF8) << 8;
half.u |= (c.g & 0xFC) << 3;
half.u |= (c.b & 0xF8) >> 3;
*reinterpret_cast<u16 *>((u32)fb + offset) = half.u;
}
void BCLIM::RenderInterfaceBackend(void* usrData, bool isRead, Color* c, int posX, int posY) {
if (isRead) {
*c = ReadPixel(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), posX, posY);
} else {
WritePixel(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), posX, posY, *c);
}
}
static bool FastContains(const Rect<int>& rect, Vector<int> point) {
return point.x >= rect.leftTop.x && point.x < (rect.leftTop.x + rect.size.x) &&
point.y >= rect.leftTop.y && point.y < (rect.leftTop.y + rect.size.y);
}
void BCLIM::Render(const Rect<int>& position, std::pair<void*, RenderBackend> backend, const Rect<int>& crop, const Rect<int>& limits, std::pair<bool, ColorBlendCallback> colorBlender) {
auto mappingScale = [](const Rect<int>& position, int x, int y, int w, int h) {
int posX = position.leftTop.x;
int posY = position.leftTop.y;
float progX = x/(float)w;
float progY = y/(float)h;
return Vector<int>(posX + position.size.x * progX, posY + position.size.y * progY);
};
auto mappingDirect = [](const Rect<int>& position, int x, int y, int w, int h) {
return Vector<int>(position.leftTop.x + x, position.leftTop.y + y);
};
Vector<int>(*mapping)(const Rect<int>& position, int x, int y, int w, int h);
Color current;
if (position.size.x == header->imag.width && position.size.y == header->imag.height)
mapping = mappingDirect;
else
mapping = mappingScale;
switch (header->imag.format) {
case TextureFormat::L8:
case TextureFormat::A8:
case TextureFormat::LA4:
case TextureFormat::LA8:
case TextureFormat::HILO8:
case TextureFormat::RGB8:
case TextureFormat::RGBA5551:
case TextureFormat::RGBA8:
case TextureFormat::ETC1:
case TextureFormat::L4:
case TextureFormat::A4:
case TextureFormat::RGBA4:
case TextureFormat::ETC1A4:
break;
case TextureFormat::RGB565:
{
int offs = 0;
Vector<int> prevPos(-1, -1);
for (int y = 0; y < header->imag.height; y+=8) {
for (int x = 0; x < header->imag.width; x+=8) {
for (int i = 0; i < 64; i++) {
int x2 = i % 8;
if (x + x2 >= crop.size.x || x + x2 >= header->imag.width) continue;
int y2 = i / 8;
if (y + y2 >= crop.size.y || y + y2 >= header->imag.height) continue;
auto drawPos = mapping(position, x + x2, y + y2, header->imag.width, header->imag.height);
if (!FastContains(limits, drawPos)) continue;
if (drawPos.x != prevPos.x || drawPos.y != prevPos.y) {
prevPos = drawPos;
int pos = textureTileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4);
u16 pixel = GetDataAt<u16>(offs + pos * 2);
u8 b = (u8)((pixel & 0x1F) << 3);
u8 g = (u8)((pixel & 0x7E0) >> 3);
u8 r = (u8)((pixel & 0xF800) >> 8);
if (colorBlender.first)
backend.second(backend.first, true, &current, drawPos.x, drawPos.y);
Color finalcolor = colorBlender.second(Color(r, g, b), current);
backend.second(backend.first, false, &finalcolor, drawPos.x, drawPos.y);
}
}
offs += 64 * 2;
}
}
}
break;
}
}
}

View File

@ -0,0 +1,158 @@
#include "CTRPluginFramework/Color.hpp"
#include <algorithm>
namespace CTRPluginFramework
{
Color& Color::Fade(float fading)
{
if (fading > 1.0f || fading < -1.0f)
return *this;
if (fading > 0.0f)
{
float tint = 1.f - fading;
r = std::min((int)(255 - (255 - r) * tint), 255);
g = std::min((int)(255 - (255 - g) * tint), 255);
b = std::min((int)(255 - (255 - b) * tint), 255);
}
else
{
float shade = 1.f + fading;
r *= shade;
g *= shade;
b *= shade;
}
return *this;
}
Color Color::Blend(const Color &color, BlendMode mode) const
{
// This is background, color is foreground
Color ret;
unsigned int _r;
unsigned int _g;
unsigned int _b;
unsigned int _a;
float forealpha = (float)color.a / 255.f;
float minusForeAlpha = 1.f - forealpha;
switch (mode)
{
case BlendMode::Alpha:
_r = (forealpha * (float)color.r) + ((float)r * minusForeAlpha);
_g = (forealpha * (float)color.g) + ((float)g * minusForeAlpha);
_b = (forealpha * (float)color.b) + ((float)b * minusForeAlpha);
_a = color.a * a;
ret.r = std::min(_r, 255u);
ret.g = std::min(_g, 255u);
ret.b = std::min(_b, 255u);
ret.a = std::min(_a, 255u);
break;
case BlendMode::Add:
_r = a * r / 255 + 255 * color.r / 255;
_g = a * g / 255 + 255 * color.g / 255;
_b = a * b / 255 + 255 * color.b / 255;
_a = a + color.a;
ret.r = std::min(_r, 255u);
ret.g = std::min(_g, 255u);
ret.b = std::min(_b, 255u);
ret.a = std::min(_a, 255u);
break;
case BlendMode::Sub:
_r = a * r / 255 - 255 * color.r / 255;
_g = a * g / 255 - 255 * color.g / 255;
_b = a * b / 255 - 255 * color.b / 255;
_a = a - color.a;
ret.r = std::max(_r, 0u);
ret.g = std::max(_g, 0u);
ret.b = std::max(_b, 0u);
ret.a = std::max(_a, 0u);
break;
case BlendMode::Mul:
ret = *this * color;
break;
default:
ret = color;
break;
}
return (ret);
}
bool Color::operator < (const Color &right) const
{
return (( r < right.r)
&& (b < right.b)
&& (g < right.g));
}
bool Color::operator <= (const Color &right) const
{
return (( r <= right.r)
&& (b <= right.b)
&& (g <= right.g));
}
bool Color::operator > (const Color &right) const
{
return (( r > right.r)
&& (b > right.b)
&& (g > right.g));
}
bool Color::operator >= (const Color &right) const
{
return (( r >= right.r)
&& (b >= right.b)
&& (g >= right.g));
}
Color Color::operator+(const Color& right) const
{
u8 _r(std::min(r + right.r, 255));
u8 _g(std::min(g + right.g, 255));
u8 _b(std::min(b + right.b, 255));
u8 _a(std::min(a + right.a, 255));
return (Color(_r, _g, _b, _a));
}
Color Color::operator-(const Color& right) const
{
u8 _r(std::max(r - right.r, 0));
u8 _g(std::max(g - right.g, 0));
u8 _b(std::max(b - right.b, 0));
u8 _a(std::max(a - right.a, 0));
return (Color(_r, _g, _b, _a));
}
Color Color::operator*(const Color& right) const
{
u8 _r(r * right.r / 255);
u8 _g(g * right.g / 255);
u8 _b(b * right.b / 255);
u8 _a(a * right.a / 255);
return (Color(_r, _g, _b, _a));
}
Color &Color::operator+=(const Color& right)
{
*this = *this + right;
return (*this);
}
Color &Color::operator-=(const Color& right)
{
*this = *this - right;
return (*this);
}
Color &Color::operator*=(const Color& right)
{
*this = *this * right;
return (*this);
}
}

View File

@ -0,0 +1,26 @@
#include "3ds/types.h"
#include "CTRPluginFramework/Time.hpp"
namespace CTRPluginFramework
{
const Time Time::Zero;
float Time::AsSeconds(void) const
{
return (_ticks / (float)TicksPerSecond);
}
int Time::AsMilliseconds(void) const
{
return static_cast<int>(_ticks / (TicksPerSecond / 1000.f));
}
s64 Time::AsMicroseconds(void) const
{
return static_cast<s64>(_ticks / (TicksPerSecond / 1000000.f));
}
}

178
app/sources/Logger.cpp Normal file
View File

@ -0,0 +1,178 @@
#include "Logger.hpp"
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include "3ds.h"
Logger::Logger() {}
void Logger::Start() {
LightEvent_Init(&event, ResetType::RESET_ONESHOT);
s32 prio = 0;
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
thread = threadCreate(LoggerThread, this, 0x300, prio + 1, -2, false);
}
void Logger::End() {
run = false;
LightEvent_Signal(&event);
threadJoin(thread, U64_MAX);
}
Logger::~Logger() {
if (run) End();
threadFree(thread);
}
extern PrintConsole topScreenConsole, bottomScreenConsole;
void Logger::Raw(bool isTopScr, const char* fmt, ...) {
va_list valist;
char buffer[256];
va_start(valist, fmt);
int ret = vsnprintf(buffer, 255, fmt, valist);
va_end(valist);
if (ret >= 0) buffer[ret] = '\0';
{
CTRPluginFramework::Lock l(pendingLogsMutex);
pendingLogs.push(PendingLog{.type = PendingLog::Type::RAW, .isTopScr = isTopScr, .string{buffer}});
}
LightEvent_Signal(&event);
}
void Logger::Info(const char* fmt, ...) {
va_list valist;
char buffer[256];
va_start(valist, fmt);
int ret = vsnprintf(buffer, 255, fmt, valist);
va_end(valist);
if (ret >= 0) buffer[ret] = '\0';
{
CTRPluginFramework::Lock l(pendingLogsMutex);
pendingLogs.push(PendingLog{.type = PendingLog::Type::INFO, .isTopScr = true, .string{buffer}});
}
LightEvent_Signal(&event);
}
void Logger::Debug(const char* fmt, ...) {
if (!debug_enable) return;
va_list valist;
char buffer[256];
va_start(valist, fmt);
int ret = vsnprintf(buffer, 255, fmt, valist);
va_end(valist);
if (ret >= 0) buffer[ret] = '\0';
{
CTRPluginFramework::Lock l(pendingLogsMutex);
pendingLogs.push(PendingLog{.type = PendingLog::Type::DEBUG, .isTopScr = true, .string{buffer}});
}
LightEvent_Signal(&event);
}
void Logger::Warning(const char* fmt, ...) {
va_list valist;
char buffer[256];
va_start(valist, fmt);
int ret = vsnprintf(buffer, 255, fmt, valist);
va_end(valist);
if (ret >= 0) buffer[ret] = '\0';
{
CTRPluginFramework::Lock l(pendingLogsMutex);
pendingLogs.push(PendingLog{.type = PendingLog::Type::WARNING, .isTopScr = true, .string{buffer}});
}
LightEvent_Signal(&event);
}
void Logger::Error(const char* fmt, ...) {
va_list valist;
char buffer[256];
va_start(valist, fmt);
int ret = vsnprintf(buffer, 255, fmt, valist);
va_end(valist);
if (ret >= 0) buffer[ret] = '\0';
{
CTRPluginFramework::Lock l(pendingLogsMutex);
pendingLogs.push(PendingLog{.type = PendingLog::Type::ERROR, .isTopScr = true, .string{buffer}});
}
LightEvent_Signal(&event);
}
void Logger::Traffic(const char* fmt, ...) {
va_list valist;
char buffer[256];
va_start(valist, fmt);
int ret = vsnprintf(buffer, 255, fmt, valist);
va_end(valist);
if (ret >= 0) buffer[ret] = '\0';
{
CTRPluginFramework::Lock l(pendingLogsMutex);
pendingLogs.push(PendingLog{.type = PendingLog::Type::TRAFFIC, .isTopScr = false, .string{buffer}});
}
LightEvent_Signal(&event);
}
void Logger::Handler() {
bool currentIsTop = true;
int back;
while (true) {
LightEvent_Wait(&event);
if (!run) {
break;
}
while (run) {
PendingLog log;
{
CTRPluginFramework::Lock l(pendingLogsMutex);
if (!pendingLogs.size())
break;
log = pendingLogs.front();
pendingLogs.pop();
}
if (log.isTopScr && !currentIsTop) {
currentIsTop = true;
consoleSelect(&topScreenConsole);
}
if (!log.isTopScr && currentIsTop) {
currentIsTop = false;
consoleSelect(&bottomScreenConsole);
}
switch (log.type)
{
case PendingLog::Type::RAW:
printf("%s\n", log.string.c_str());
break;
case PendingLog::Type::DEBUG:
printf("[D] %s\n", log.string.c_str());
break;
case PendingLog::Type::INFO:
printf("[I] %s\n", log.string.c_str());
break;
case PendingLog::Type::WARNING:
topScreenConsole.fg = 19;
printf("[W] %s\n", log.string.c_str());
topScreenConsole.fg = 0;
break;
case PendingLog::Type::ERROR:
topScreenConsole.fg = 17;
printf("[E] %s\n", log.string.c_str());
topScreenConsole.fg = 0;
break;
case PendingLog::Type::TRAFFIC:
back = bottomScreenConsole.cursorY;
bottomScreenConsole.cursorY = 25;
printf(log.string.c_str());
bottomScreenConsole.cursorY = back;
break;
default:
break;
}
}
}
}
void Logger::LoggerThread(void* arg) {
Logger* l = (Logger*)arg;
l->Handler();
}

122
app/sources/csvc.s Normal file
View File

@ -0,0 +1,122 @@
@ This paricular file is licensed under the following terms:
@ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
@ for any damages arising from the use of this software.
@
@ Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
@ and redistribute it freely, subject to the following restrictions:
@
@ The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
@ If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
@
@ Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
@ This notice may not be removed or altered from any source distribution.
.arm
.balign 4
.macro SVC_BEGIN name
.section .text.\name, "ax", %progbits
.global \name
.type \name, %function
.align 2
.cfi_startproc
\name:
.endm
.macro SVC_END
.cfi_endproc
.endm
SVC_BEGIN svcCustomBackdoor
svc 0x80
bx lr
SVC_END
SVC_BEGIN svcConvertVAToPA
svc 0x90
bx lr
SVC_END
SVC_BEGIN svcFlushDataCacheRange
svc 0x91
bx lr
SVC_END
SVC_BEGIN svcFlushEntireDataCache
svc 0x92
bx lr
SVC_END
SVC_BEGIN svcInvalidateInstructionCacheRange
svc 0x93
bx lr
SVC_END
SVC_BEGIN svcInvalidateEntireInstructionCache
svc 0x94
bx lr
SVC_END
SVC_BEGIN svcMapProcessMemoryEx
str r4, [sp, #-4]!
ldr r4, [sp, #4]
svc 0xA0
ldr r4, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcUnmapProcessMemoryEx
svc 0xA1
bx lr
SVC_END
SVC_BEGIN svcControlMemoryEx
push {r0, r4, r5}
ldr r0, [sp, #0xC]
ldr r4, [sp, #0xC+0x4]
ldr r5, [sp, #0xC+0x8]
svc 0xA2
pop {r2, r4, r5}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcControlMemoryUnsafe
str r4, [sp, #-4]!
ldr r4, [sp, #4]
svc 0xA3
ldr r4, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcFreeMemory
svc 0xA3
bx lr
SVC_END
SVC_BEGIN svcControlService
svc 0xB0
bx lr
SVC_END
SVC_BEGIN svcCopyHandle
str r0, [sp, #-4]!
svc 0xB1
ldr r2, [sp], #4
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcTranslateHandle
str r0, [sp, #-4]!
svc 0xB2
ldr r2, [sp], #4
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcControlProcess
svc 0xB3
bx lr
SVC_END

2057
app/sources/logo.c Normal file

File diff suppressed because it is too large Load Diff

236
app/sources/main.cpp Normal file
View File

@ -0,0 +1,236 @@
#include <stdio.h>
#include <stdlib.h>
#include "sys/stat.h"
#include <string.h>
#include <memory.h>
#include "Main.hpp"
#include "plgldr.h"
#include "BCLIM.hpp"
#include "logo.h"
#include "plugin.h"
#include "3gx.h"
Logger logger;
const char* artic_setup_plugin = "/3ds/AzaharArticSetup/AzaharArticSetup.3gx";
char *strdup(const char *s) {
char *d = (char*)malloc(strlen(s) + 1);
if (d == NULL) return NULL;
strcpy(d, s);
return d;
}
FILE* fopen_mkdir(const char* name, const char* mode, bool actuallyOpen = true)
{
char* _path = strdup(name);
char *p;
FILE* retfile = NULL;
errno = 0;
for (p = _path + 1; *p; p++)
{
if (*p == '/')
{
*p = '\0';
if (mkdir(_path, 777) != 0)
if (errno != EEXIST) goto error;
*p = '/';
}
}
if (actuallyOpen) retfile = fopen(name, mode);
error:
free(_path);
return retfile;
}
bool extractPlugin() {
u32 expectedVersion = SYSTEM_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
bool plugin_needs_update = true;
FILE* f = fopen(artic_setup_plugin, "r");
if (f) {
_3gx_Header header;
int read = fread(&header, 1, sizeof(header), f);
if (read == sizeof(header) && header.magic == _3GX_MAGIC) {
plugin_needs_update = header.version != expectedVersion;
}
}
if (f) fclose(f);
if (plugin_needs_update) {
logger.Info("Updating Azahar Artic Setup plugin file");
f = fopen_mkdir(artic_setup_plugin, "w");
if (!f) {
logger.Error("Cannot open plugin file");
return false;
}
int written = fwrite(plugin_AzaharArticSetup_3gx, 1, plugin_AzaharArticSetup_3gx_size, f);
fclose(f);
if (written != plugin_AzaharArticSetup_3gx_size) {
logger.Error("Cannot write plugin file");
return false;
}
}
return true;
}
bool launchPlugin() {
u32 ret = 0;
PluginLoadParameters plgparam = { 0 };
u8 isPlgEnabled = 0;
plgparam.noFlash = true;
plgparam.pluginMemoryStrategy = PLG_STRATEGY_NONE;
plgparam.persistent = 0;
plgparam.lowTitleId = 0;
strcpy(plgparam.path, artic_setup_plugin);
ret = plgLdrInit();
if (R_FAILED(ret)) {
logger.Error("Cannot start plugin loader");
return false;
}
u32 version;
ret = PLGLDR__GetVersion(&version);
if (R_FAILED(ret)) {
logger.Error("Plugin loader error");
plgLdrExit();
return false;
}
if (version < SYSTEM_VERSION(1,0,2)) {
logger.Error("Unsupported plugin loader version,");
logger.Error("please update Luma3DS");
plgLdrExit();
return false;
}
ret = PLGLDR__IsPluginLoaderEnabled((bool*)&isPlgEnabled);
if (R_FAILED(ret)) {
logger.Error("Plugin loader error");
plgLdrExit();
return false;
}
plgparam.config[0] = isPlgEnabled;
ret = PLGLDR__SetPluginLoaderState(true);
if (R_FAILED(ret)) {
logger.Error("Cannot enable plugin loader");
plgLdrExit();
return false;
}
ret = PLGLDR__SetPluginLoadParameters(&plgparam);
plgLdrExit();
if (R_FAILED(ret)) {
logger.Error("Plugin loader error");
return false;
}
return true;
}
PrintConsole topScreenConsole, bottomScreenConsole;
int transferedBytes = 0;
void Main() {
logger.Start();
logger.debug_enable = true;
gfxInitDefault();
consoleInit(GFX_TOP, &topScreenConsole);
consoleInit(GFX_BOTTOM, &bottomScreenConsole);
topScreenConsole.bg = 15; topScreenConsole.fg = 0;
bottomScreenConsole.bg = 15; bottomScreenConsole.fg = 0;
gfxSetDoubleBuffering(GFX_BOTTOM, false);
aptSetHomeAllowed(false);
consoleSelect(&bottomScreenConsole);
consoleClear();
consoleSelect(&topScreenConsole);
consoleClear();
{
CTRPluginFramework::BCLIM((void*)__data_logo_bin, __data_logo_bin_size).Render(CTRPluginFramework::Rect<int>((320 - 128) / 2, (240 - 128) / 2, 128, 128));
}
logger.Raw(false, "\n Azahar Artic Setup v%d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
logger.Raw(false, " Press A to launch setup tool.");
logger.Raw(false, " Press B or START to exit.");
logger.Raw(true, "");
logger.Info("Welcome to Azahar Artic Setup Tool!\n Only use this tool with Azahar Emulator\n\n Check bottom screen for controls.");
bool do_jump = false;
while (aptMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
u32 kDown = hidKeysDown();
if (kDown & (KEY_B | KEY_START)) {
break;
}
if (kDown & KEY_A) {
logger.Info("Launching Azahar Artic Setup");
bool done = extractPlugin() && launchPlugin();
if (done) {
do_jump = true;
logger.Raw(true, "");
logger.Info("Done! Please wait...");
svcSleepThread(3000000000);
break;
} else {
logger.Error("Failed to launch Azahar Artic Setup");
}
}
// Flush and swap framebuffers
gfxFlushBuffers();
gfxSwapBuffers();
//Wait for VBlank
gspWaitForVBlank();
}
// Flush and swap framebuffers
gfxFlushBuffers();
gfxSwapBuffers();
//Wait for VBlank
gspWaitForVBlank();
gfxExit();
gspLcdExit();
logger.End();
if (do_jump) {
OS_VersionBin cver = {0};
OS_VersionBin nver = {0};
osGetSystemVersionData(&nver, &cver);
u64 program_id = 0;
if (cver.region == 'E') {
program_id = 0x0004001000022000;
} else if (cver.region == 'U') {
program_id = 0x0004001000021000;
} else if (cver.region == 'J') {
program_id = 0x0004001000020000;
} else if (cver.region == 'C') {
program_id = 0x0004001000026000;
} else if (cver.region == 'K') {
program_id = 0x0004001000027000;
} else if (cver.region == 'T') {
program_id = 0x0004001000028000;
}
aptSetChainloader(program_id, MEDIATYPE_NAND);
}
}
extern "C" {
int main(int argc, char* argv[]);
}
// Entrypoint, game will starts when you exit this function
int main(int argc, char* argv[])
{
Main();
return 0;
}

134
app/sources/plgldr.c Normal file
View File

@ -0,0 +1,134 @@
#include <3ds.h>
#include "plgldr.h"
#include "csvc.h"
static Handle plgLdrHandle = 0;
static int plgLdrRefCount;
Result plgLdrInit(void)
{
Result res = 0;
if (AtomicPostIncrement(&plgLdrRefCount) == 0)
res = svcConnectToPort(&plgLdrHandle, "plg:ldr");
if (R_FAILED(res)) AtomicDecrement(&plgLdrRefCount);
return res;
}
void plgLdrExit(void)
{
if (AtomicDecrement(&plgLdrRefCount))
return;
svcCloseHandle(plgLdrHandle);
}
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(2, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
*isEnabled = cmdbuf[2];
}
return res;
}
Result PLGLDR__SetPluginLoaderState(bool enabled)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
cmdbuf[1] = (u32)enabled;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(4, 2, 4);
cmdbuf[1] = (u32)parameters->noFlash | (((u32)parameters->pluginMemoryStrategy) << 8) | (((u32)parameters->persistent) << 16);
cmdbuf[2] = parameters->lowTitleId;
cmdbuf[3] = IPC_Desc_Buffer(256, IPC_BUFFER_R);
cmdbuf[4] = (u32)parameters->path;
cmdbuf[5] = IPC_Desc_Buffer(32 * sizeof(u32), IPC_BUFFER_R);
cmdbuf[6] = (u32)parameters->config;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__GetVersion(u32* version)
{
Result res = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(8, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
if (cmdbuf[0] != IPC_MakeHeader(8, 2, 0))
return 0xD900182F;
res = cmdbuf[1];
if (version)
*version = cmdbuf[2];
}
return res;
}
Result PLGLDR__SetExeDecSettings(void* decFunc, void* args)
{
Result res = 0;
u32 buf[0x10] = { 0 };
u32* trueArgs;
if (args) trueArgs = args;
else trueArgs = buf;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(13, 1, 2);
cmdbuf[1] = (decFunc) ? svcConvertVAToPA(decFunc, false) | (1 << 31) : 0;
cmdbuf[2] = IPC_Desc_Buffer(16 * sizeof(u32), IPC_BUFFER_R);
cmdbuf[3] = (u32)trueArgs;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__ClearPluginLoadParameters()
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(14, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}

BIN
images/logo_small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

3
plugin/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/build
*.3gx
.vscode/

202
plugin/3gx.ld Normal file
View File

@ -0,0 +1,202 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
PHDRS
{
code PT_LOAD FLAGS(5) /* Read | Execute */;
rodata PT_LOAD FLAGS(4) /* Read */;
data PT_LOAD FLAGS(6) /* Read | Write */;
}
SECTIONS
{
/* =========== CODE section =========== */
PROVIDE(__start__ = 0x07000100);
. = __start__;
.text ALIGN(4) :
{
/* .init */
KEEP( *(.crt0) )
KEEP( *(.init) )
. = ALIGN(4);
/* .text */
*(.text)
*(.text.*)
*(.glue_7)
*(.glue_7t)
*(.stub)
*(.gnu.warning)
*(.gnu.linkonce.t*)
. = ALIGN(4);
/* .fini */
KEEP( *(.fini) )
. = ALIGN(4);
} : code
__text_end__ = .;
/* =========== RODATA section =========== */
.rodata ALIGN(0x4) :
{
*(.rodata)
*(.roda)
*(.rodata.*)
*all.rodata*(*)
*(.gnu.linkonce.r*)
SORT(CONSTRUCTORS)
. = ALIGN(4);
__tdata_align = .;
LONG (ALIGNOF(.tdata));
. = ALIGN(4);
} : rodata
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } : rodata
__exidx_start = .;
ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } : rodata
__exidx_end = .;
/* =========== DATA section =========== */
.data ALIGN(4):
{
*(.data)
*(.data.*)
*(.gnu.linkonce.d*)
CONSTRUCTORS
. = ALIGN(4);
} : data
.tdata : ALIGN(4)
{
__tdata_lma = .;
*(.tdata)
*(.tdata.*)
*(.gnu.linkonce.td.*)
. = ALIGN(4);
__tdata_lma_end = .;
} : data
.tbss : ALIGN(4)
{
*(.tbss)
*(.tbss.*)
*(.gnu.linkonce.tb.*)
*(.tcommon)
. = ALIGN(4);
} : data
.preinit_array ALIGN(4) :
{
PROVIDE (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
} : data
.init_array ALIGN(4) :
{
PROVIDE (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE (__init_array_end = .);
} : data
.fini_array ALIGN(4) :
{
PROVIDE (__fini_array_start = .);
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE (__fini_array_end = .);
} : data
.ctors ALIGN(4) :
{
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
} : data
.dtors ALIGN(4) :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
} : data
__bss_start__ = .;
.bss ALIGN(4) :
{
*(.dynbss)
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b*)
*(COMMON)
/* Reserve space for the TLS segment of the main thread.
We need (__tls_start - 8) to be aligned the same as .tdata, to account for
the 8-byte ARM TLS header. Since the header is not actually used for
ARM_TLS_LE32 relocation, we just fake it by subtracting 8 from the data
offset.
*/
. = 8 + ABSOLUTE(ALIGN(ABSOLUTE(. - 8), ALIGNOF(.tdata)));
__tls_start = .;
. += SIZEOF(.tdata);
/* Ensure the alignment of .tbss is accounted for in the total size,
since SIZEOF(.tdata) doesn't include any padding before .tbss.
*/
. = ALIGN(ALIGNOF(.tbss));
. += SIZEOF(.tbss);
__tls_end = .;
} : data
__bss_end__ = .;
__end__ = ABSOLUTE(.) ;
/* ==================
==== Metadata ====
================== */
/* Discard sections that difficult post-processing */
/DISCARD/ : { *(.group .comment .note) }
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
}

1
plugin/ArticProtocol Submodule

@ -0,0 +1 @@
Subproject commit 842ab851afb6e08419d1f0a94bf3450a2080a21e

View File

@ -0,0 +1,27 @@
Author: Nanquitas
Version: # Plugin version
Major: VERSION_MAJOR
Minor: VERSION_MINOR
Revision: VERSION_REVISION
Targets: # Low TitleId of games which are compatibles with this plugin (empty for all)
- 0x00020000
- 0x00021000
- 0x00022000
- 0x00026000
- 0x00027000
- 0x00028000
Title: Azahar Artic Setup Tool
Summary: Setup tool for Azahar Emulator
Description: |
Provides system files to the Azahar 3DS Emulator
Compatibility: Console
MemorySize: 2MiB
EventsSelfManaged: true
SwapNotNeeded: true

121
plugin/Makefile Normal file
View File

@ -0,0 +1,121 @@
.SUFFIXES:
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITARM)/3ds_rules
TARGET := AzaharArticSetup
PLGINFO := build/AzaharArticSetup.plgInfo
BUILD := build
INCLUDES := ArticProtocol/includes includes
SOURCES := ArticProtocol/sources sources sources/CTRPluginFramework
VERSION_MAJOR := 1
VERSION_MINOR := 2
VERSION_REVISION := 0
SERVER_PORT := 5543
IP := 19
FTP_HOST := 192.168.1.
FTP_PORT := "5000"
FTP_PATH := "luma/plugins"
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := $(ARCH) -Os -mword-relocations \
-fomit-frame-pointer -ffunction-sections -fno-strict-aliasing
CFLAGS += $(INCLUDE) -D__3DS__ -DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) \
-DVERSION_REVISION=$(VERSION_REVISION) -DSERVER_PORT=$(SERVER_PORT)
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++20
ASFLAGS := $(ARCH)
LDFLAGS := -T $(TOPDIR)/3gx.ld $(ARCH) -Os -Wl,--gc-sections,--strip-discarded
LIBS := -lctru
LIBDIRS := $(CTRULIB) $(PORTLIBS)
#---------------------------------------------------------------------------------
# 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)))
export LD := $(CXX)
export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I $(CURDIR)/$(dir) ) \
$(foreach dir,$(LIBDIRS),-I $(dir)/include) \
-I $(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L $(dir)/lib)
.PHONY: $(BUILD) clean all send
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTPUT).3gx $(OUTPUT).elf
send:
@echo "Sending plugin over FTP"
@$(CURDIR)/sendfile.py $(TARGET).3gx $(FTP_PATH) "$(FTP_HOST)$(IP)" $(FTP_PORT) default.3gx
re: clean all
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).3gx : $(OFILES)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
%.3gx: %.elf
#---------------------------------------------------------------------------------
@echo creating $(notdir $@)
@3gxtool -s $(word 1, $^) $(TOPDIR)/$(PLGINFO) $@
@mv $(OUTPUT).elf AzaharArticSetup.elf
-include $(DEPENDS)
#---------------------------------------------------------------------------------
endif

BIN
plugin/images/logo.bclim Normal file

Binary file not shown.

74
plugin/includes/BCLIM.hpp Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include "3ds.h"
#include "tuple"
#include "CTRPluginFramework/Vector.hpp"
#include "CTRPluginFramework/Rect.hpp"
#include "CTRPluginFramework/Color.hpp"
namespace CTRPluginFramework {
class BCLIM {
public:
enum class TextureFormat : u32 {
L8 = 0,
A8 = 1,
LA4 = 2,
LA8 = 3,
HILO8 = 4,
RGB565 = 5,
RGB8 = 6,
RGBA5551 = 7,
RGBA4 = 8,
RGBA8 = 9,
ETC1 = 10,
ETC1A4 = 11,
L4 = 12,
A4 = 13,
};
struct Header {
u32 magic;
u16 endian;
u16 headerSize;
u32 version;
u32 fileSize;
u32 blockCount;
struct {
u32 magic;
u32 imagHeaderSize;
u16 width;
u16 height;
TextureFormat format;
} imag;
u32 dataLength;
};
BCLIM(void* bclimData, u32 bclimSize) : BCLIM(bclimData, (Header*)((u32)bclimData + bclimSize - 0x28)) {}
BCLIM(void* bclimData, Header* bclimHeader) : data(bclimData), header(bclimHeader) {}
using ColorBlendCallback = Color(*)(const Color &, const Color &);
static std::pair<bool, ColorBlendCallback> OpaqueBlend() {
return std::make_pair(false, OpaqueBlendFunc);
}
using RenderBackend = void(*)(void*, bool, Color*, int posX, int posY);
static std::pair<void*, RenderBackend> RenderInterface() {
return std::make_pair(nullptr, RenderInterfaceBackend);
}
void Render(const Rect<int>& position, std::pair<void*, RenderBackend> backend = RenderInterface(), const Rect<int>& crop = Rect<int>(0, 0, INT32_MAX, INT32_MAX), const Rect<int>& limits = Rect<int>(0, 0, 400, 240), std::pair<bool, ColorBlendCallback> colorBlend = OpaqueBlend());
void* data;
private:
Header* header;
static const u8 textureTileOrder[16];
static const u8 etc1Modifiers[8][2];
template<typename T>
inline T GetDataAt(int offset) {
return *(T*)(((u32)data) + offset);
}
static void RenderInterfaceBackend(void* usrData, bool isRead, Color* c, int posX, int posY);
static Color OpaqueBlendFunc(const Color& dst, const Color& src);
};
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "CTRPluginFramework/System/Mutex.hpp"
#include "CTRPluginFramework/System/Lock.hpp"
#include "CTRPluginFramework/Vector.hpp"
#include "CTRPluginFramework/Rect.hpp"
#include "CTRPluginFramework/Color.hpp"

View File

@ -0,0 +1,37 @@
#pragma once
#include "3ds.h"
#include "CTRPluginFramework/Time.hpp"
namespace CTRPluginFramework
{
class Clock
{
public:
Clock(void) : _startTime(GetCurrentTime()) {}
constexpr Clock(const Time& time) : _startTime(time) {}
__always_inline Time GetElapsedTime(void) const {
return (GetCurrentTime() - _startTime);
}
__always_inline bool HasTimePassed(const Time& time) const {
return (GetElapsedTime() >= time);
}
__always_inline Time Restart(void) {
const Time now = GetCurrentTime();
const Time ret = now - _startTime;
_startTime = now;
return (ret);
}
private:
Time _startTime;
static __always_inline Time GetCurrentTime(void)
{
return Ticks(svcGetSystemTick());
}
};
}

View File

@ -0,0 +1,67 @@
#pragma once
#include "3ds/types.h"
#include <algorithm>
#include <string>
namespace CTRPluginFramework
{
class Color
{
public:
enum class BlendMode
{
Alpha,
Add,
Sub,
Mul,
None
};
constexpr Color(void) : a(255), b(0), g(0), r(0) {}
constexpr Color(u32 color) : raw(color) {}
constexpr Color(u8 red, u8 green, u8 blue, u8 alpha = 255) : a(alpha), b(blue), g(green), r(red) {}
inline u32 ToU32(void) const { return raw; };
Color &Fade(float fading);
Color Blend(const Color &color, BlendMode mode) const;
inline bool operator == (const Color &right) const {return raw == right.raw;}
inline bool operator != (const Color &right) const {return raw != right.raw;}
bool operator < (const Color &right) const;
bool operator <= (const Color &right) const;
bool operator > (const Color &right) const;
bool operator >= (const Color &right) const;
Color operator + (const Color &right) const;
Color operator - (const Color &right) const;
Color operator * (const Color &right) const;
Color &operator += (const Color &right);
Color &operator -= (const Color &right);
Color &operator *= (const Color &right);
operator std::string() const
{
char strColor[5] = { 0 };
strColor[0] = 0x1B;
strColor[1] = std::max((u8)1, r);
strColor[2] = std::max((u8)1, g);
strColor[3] = std::max((u8)1, b);
return strColor;
}
union
{
u32 raw;
struct // Match raw byte order
{
u8 a;
u8 b;
u8 g;
u8 r;
};
};
};
}

View File

@ -0,0 +1,143 @@
#pragma once
#include "CTRPluginFramework/Vector.hpp"
#include <algorithm>
namespace CTRPluginFramework
{
template <typename T>
class Rect
{
public:
Rect();
Rect(const Vector<T>& leftTop, const Vector<T>& size);
Rect(const Vector<T>& leftTop, T width, T height);
Rect(T left, T top, const Vector<T>& size);
Rect(T left, T top, T width, T height);
template <typename U>
explicit Rect(const Rect<U>& rect);
bool Contains(T x, T y) const;
bool Contains(const Vector<T>& point) const;
bool Intersects(const Rect<T>& rect) const;
bool Intersects(const Rect<T>& rect, Rect<T>& intersect) const;
Vector<T> leftTop;
Vector<T> size;
};
template <typename T>
Rect<T>::Rect() : leftTop(0, 0), size(0, 0)
{
}
template <typename T>
Rect<T>::Rect(const Vector<T>& leftTopCorner, const Vector<T>& size)
{
leftTop = leftTopCorner;
this->size = size;
}
template <typename T>
Rect<T>::Rect(const Vector<T>& leftTopCorner, T width, T height)
{
leftTop = leftTopCorner;
size.x = width;
size.y = height;
}
template <typename T>
Rect<T>::Rect(T left, T top, const Vector<T>& size)
{
leftTop.x = left;
leftTop.y = top;
this->size = size;
}
template <typename T>
Rect<T>::Rect(T left, T top, T width, T height)
{
leftTop.x = left;
leftTop.y = top;
size.x = width;
size.y = height;
}
template <typename T>
template <typename U>
Rect<T>::Rect(const Rect<U>& rect)
{
leftTop = reinterpret_cast<T>(rect.leftTop);
size = reinterpret_cast<T>(rect.size);
}
template <typename T>
bool Rect<T>::Contains(T x, T y) const
{
T minX = std::min(leftTop.x, leftTop.x + size.x);
T maxX = std::max(leftTop.x, leftTop.x + size.x);
T minY = std::min(leftTop.y, leftTop.y + size.y);
T maxY = std::max(leftTop.y, leftTop.y + size.y);
return (x >= minX && x < maxX
&& y >= minY && y < maxY);
}
template <typename T>
bool Rect<T>::Contains(const Vector<T>& point) const
{
return (Contains(point.x, point.y));
}
template <typename T>
bool Rect<T>::Intersects(const Rect<T>& rect) const
{
Rect<T> intersect;
return (Intersects(rect, intersect));
}
template <typename T>
bool Rect<T>::Intersects(const Rect<T> &rect, Rect<T> &intersect) const
{
T thisMinX = std::min(leftTop.x, leftTop.x + size.x);
T thisMaxX = std::max(leftTop.x, leftTop.x + size.x);
T thisMinY = std::min(leftTop.y, leftTop.y + size.y);
T thisMaxY = std::max(leftTop.y, leftTop.y + size.y);
T rectMinX = std::min(rect.leftTop.x, rect.leftTop.x + rect.size.x);
T rectMaxX = std::max(rect.leftTop.x, rect.leftTop.x + rect.size.x);
T rectMinY = std::min(rect.leftTop.y, rect.leftTop.y + rect.size.y);
T rectMaxY = std::max(rect.leftTop.y, rect.leftTop.y + rect.size.y);
T intersectLeftX = std::max(thisMinX, rectMinX);
T intersectLeftY = std::max(thisMinY, rectMinY);
T intersectRightX = std::min(thisMaxX, rectMaxX);
T intersectRightY = std::min(thisMaxY, rectMaxY);
if (intersectLeftX < intersectRightX && intersectLeftY < intersectRightY)
{
intersect = Rect<T>(intersectLeftX, intersectLeftY, intersectRightX - intersectLeftX,
intersectRightY - intersectLeftY);
return (true);
}
intersect = Rect<T>(0, 0, 0, 0);
return (false);
}
template <typename T>
bool operator ==(Rect<T> &left, Rect<T> &right)
{
return (left.leftTop == right.leftTop
&& left.size == right.size);
}
template <typename T>
bool operator !=(Rect<T> &left, Rect<T> &right)
{
return (left.leftTop != right.leftTop
&& left.size != right.size);
}
typedef Rect<unsigned int> UIntRect;
typedef Rect<int> IntRect;
typedef Rect<float> FloatRect;
}

View File

@ -0,0 +1,54 @@
#ifndef CTRPLUGINFRAMEWORK_SYSTEM_LOCK_HPP
#define CTRPLUGINFRAMEWORK_SYSTEM_LOCK_HPP
#include "3ds.h"
namespace CTRPluginFramework
{
class Mutex;
class Lock
{
public:
inline explicit Lock(LightLock &llock) :
_type{LIGHTLOCK}, _llock{&llock}
{
LightLock_Lock(_llock);
}
inline explicit Lock(RecursiveLock &rlock) :
_type{RECLOCK}, _rlock{&rlock}
{
RecursiveLock_Lock(_rlock);
}
inline explicit Lock(Mutex &mutex) :
_type{MUTEX}, _mutex{&mutex}
{
mutex.Lock();
}
inline ~Lock(void)
{
if (_type == LIGHTLOCK)
LightLock_Unlock(_llock);
else if (_type == RECLOCK)
RecursiveLock_Unlock(_rlock);
else if (_type == MUTEX)
_mutex->Unlock();
}
private:
static const constexpr u32 LIGHTLOCK = 1;
static const constexpr u32 RECLOCK = 2;
static const constexpr u32 MUTEX = 3;
const u32 _type;
union
{
LightLock *_llock;
RecursiveLock *_rlock;
Mutex *_mutex;
};
};
}
#endif

View File

@ -0,0 +1,41 @@
#ifndef CTRPLUGINFRAMEWORK_SYSTEM_MUTEX_HPP
#define CTRPLUGINFRAMEWORK_SYSTEM_MUTEX_HPP
#include "3ds.h"
namespace CTRPluginFramework
{
class Mutex
{
public:
inline Mutex(void) {
RecursiveLock_Init(&_lock);
}
inline ~Mutex(void) {
// I suppose that we can "force" unlock the mutex
if (_lock.counter > 0)
{
_lock.counter = 1;
RecursiveLock_Unlock(&_lock);
}
}
inline void Lock(void) {
RecursiveLock_Lock(&_lock);
}
// Return true on failure
inline bool TryLock(void) {
return RecursiveLock_TryLock(&_lock) != 0;
}
inline void Unlock(void) {
RecursiveLock_Unlock(&_lock);
}
private:
RecursiveLock _lock;
};
}
#endif

View File

@ -0,0 +1,205 @@
#pragma once
#include "3ds/types.h"
namespace CTRPluginFramework
{
class Time
{
public :
constexpr Time(void) : _ticks(0) {}
float AsSeconds(void) const;
int AsMilliseconds(void) const;
s64 AsMicroseconds(void) const;
inline s64 AsTicks(void) const { return _ticks; }
static const Time Zero; ///< Predefined "zero" time value
static constexpr u32 TicksPerSecond = 268111856U;
private :
friend constexpr Time Seconds(float amount);
friend constexpr Time Milliseconds(int amount);
friend constexpr Time Microseconds(s64 amount);
friend constexpr Time Ticks(s64 amount);
constexpr Time(s64 ticks) : _ticks(ticks) {}
private :
s64 _ticks;
};
constexpr Time Seconds(float amount)
{
return (Time(static_cast<s64>(amount * Time::TicksPerSecond)));
}
constexpr Time Milliseconds(int amount)
{
return (Time(static_cast<s64>(amount * (Time::TicksPerSecond / 1000.f))));
}
constexpr Time Microseconds(s64 amount)
{
return (Time(static_cast<s64>(amount * (Time::TicksPerSecond / 1000000.f))));
}
constexpr Time Ticks(s64 amount)
{
return (Time(amount));
}
inline bool operator ==(const Time& left, const Time& right)
{
return (left.AsTicks() == right.AsTicks());
}
inline bool operator !=(const Time& left, const Time& right)
{
return (left.AsTicks() != right.AsTicks());
}
inline bool operator <(const Time& left, const Time& right)
{
return (left.AsTicks() < right.AsTicks());
}
inline bool operator >(const Time& left, const Time& right)
{
return (left.AsTicks() > right.AsTicks());
}
inline bool operator <=(const Time& left, const Time& right)
{
return (left.AsTicks() <= right.AsTicks());
}
inline bool operator >=(const Time& left, const Time& right)
{
return (left.AsTicks() >= right.AsTicks());
}
inline Time operator -(const Time& right)
{
return (Ticks(-right.AsTicks()));
}
inline Time operator +(const Time& left, const Time& right)
{
return (Ticks(left.AsTicks() + right.AsTicks()));
}
inline Time& operator +=(Time& left, const Time& right)
{
return (left = left + right);
}
inline Time operator -(const Time& left, const Time& right)
{
return (Ticks(left.AsTicks() - right.AsTicks()));
}
inline Time& operator -=(Time& left, const Time& right)
{
return left = left - right;
}
inline Time operator *(const Time& left, float right)
{
return (Seconds(left.AsSeconds() * right));
}
inline Time operator *(const Time& left, s64 right)
{
return (Microseconds(left.AsMicroseconds() * right));
}
inline Time operator *(float left, const Time& right)
{
return (right * left);
}
inline Time operator *(s64 left, const Time& right)
{
return (right * left);
}
inline Time& operator *=(Time& left, float right)
{
return (left = left * right);
}
inline Time& operator *=(Time& left, s64 right)
{
return (left = left * right);
}
inline Time operator /(const Time& left, float right)
{
return Seconds(left.AsSeconds() / right);
}
inline Time operator /(const Time& left, s64 right)
{
return (Microseconds(left.AsMicroseconds() / right));
}
inline Time& operator /=(Time& left, float right)
{
return (left = left / right);
}
inline Time& operator /=(Time& left, s64 right)
{
return (left = left / right);
}
inline float operator /(const Time& left, const Time& right)
{
return (left.AsSeconds() / right.AsSeconds());
}
inline Time operator %(const Time& left, const Time& right)
{
return (Ticks(left.AsTicks() % right.AsTicks()));
}
inline Time& operator %=(Time& left, const Time& right)
{
return (left = left % right);
}
}

View File

@ -0,0 +1,124 @@
#pragma once
#include "3ds/types.h"
namespace CTRPluginFramework
{
template <typename T>
class Vector
{
public:
Vector();
Vector(T x, T y);
template <typename U>
explicit Vector(const Vector<U> &vector);
T x;
T y;
};
template <typename T>
Vector<T>::Vector() : x(0), y(0)
{
}
template <typename T>
Vector<T>::Vector(T x, T y) : x(x), y(y)
{
}
template <typename T>
template <typename U>
Vector<T>::Vector(const Vector<U> &vector) : x(static_cast<T>(vector.x)), y(static_cast<T>(vector.y))
{
}
template <typename T>
Vector<T> operator - (const Vector<T> &vector)
{
return (Vector<T>(-vector.x, -vector.y));
}
template <typename T>
Vector<T> operator - (const Vector<T> &left, const Vector<T> &right)
{
return (Vector<T>(left.x - right.x, left.y - right.y));
}
template <typename T>
Vector<T> &operator -= (const Vector<T> &left, const Vector<T> &right)
{
left.x -= right.x;
left.y -= right.y;
return (left);
}
template <typename T>
Vector<T> operator + (const Vector<T> &left, const Vector<T> &right)
{
return (Vector<T>(left.x + right.x, left.y + right.y));
}
template <typename T>
Vector<T> &operator += (const Vector<T> &left, const Vector<T> &right)
{
left.x += right.x;
left.y += right.y;
return (left);
}
template <typename T>
Vector<T> operator * (const Vector<T> &left, T right)
{
return (Vector<T>(left.x * right, left.y * right));
}
template <typename T>
Vector<T> operator * (const T left, const Vector<T> &right)
{
return (Vector<T>(right.x * left, right.y * left));
}
template <typename T>
Vector<T> &operator *= (Vector<T> &left, const T right)
{
left.x *= right;
left.y *= right;
return (left);
}
template <typename T>
Vector<T> operator / (const Vector<T> &left, const T right)
{
return (Vector<T>(left.x / right, left.y / right));
}
template <typename T>
Vector<T> &operator /= (Vector<T> &left, const T right)
{
left.x /= right;
left.y /= right;
return (left);
}
template <typename T>
bool operator <= (const Vector<T> &left, const Vector<T> &right)
{
return (left.x <= right.x
&& left.y <= right.y);
}
template <typename T>
bool operator >= (const Vector<T> &left, const Vector<T> &right)
{
return (left.x >= right.x
&& left.y >= right.y);
}
typedef Vector<unsigned int> UIntVector;
typedef Vector<int> IntVector;
typedef Vector<float> FloatVector;
typedef Vector<signed short> shortVector;
}

View File

@ -0,0 +1,6 @@
// Only used by Visual Studio Intellisense
#define VERSION_MAJOR 0
#define VERSION_MINOR 0
#define VERSION_REVISION 1
#define SERVER_PORT 5543

5
plugin/includes/Main.hpp Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include "3ds.h"
#include "Logger.hpp"
extern Logger logger;

154
plugin/includes/csvc.h Normal file
View File

@ -0,0 +1,154 @@
/* This paricular file is licensed under the following terms: */
/*
* This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
* for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
* and redistribute it freely, subject to the following restrictions:
*
* The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
* If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
*
* Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
* This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <3ds/types.h>
/// Operations for svcControlService
typedef enum ServiceOp
{
SERVICEOP_STEAL_CLIENT_SESSION = 0, ///< Steal a client session given a service or global port name
SERVICEOP_GET_NAME, ///< Get the name of a service or global port given a client or session handle
} ServiceOp;
/**
* @brief Executes a function in supervisor mode, using the supervisor-mode stack.
* @param func Function to execute.
* @param ... Function parameters, up to 3 registers.
*/
Result svcCustomBackdoor(void *func, ...);
///@name I/O
///@{
/**
* @brief Gives the physical address corresponding to a virtual address.
* @param VA Virtual address.
* @param writeCheck whether to check if the VA is writable in supervisor mode
* @return The corresponding physical address, or NULL.
*/
u32 svcConvertVAToPA(const void *VA, bool writeCheck);
/**
* @brief Flushes a range of the data cache (L2C included).
* @param addr Start address.
* @param len Length of the range.
*/
void svcFlushDataCacheRange(void *addr, u32 len);
/**
* @brief Flushes the data cache entirely (L2C included).
*/
void svcFlushEntireDataCache(void);
/**
* @brief Invalidates a range of the instruction cache.
* @param addr Start address.
* @param len Length of the range.
*/
void svcInvalidateInstructionCacheRange(void *addr, u32 len);
/**
* @brief Invalidates the data cache entirely.
*/
void svcInvalidateEntireInstructionCache(void);
///@}
///@name Memory management
///@{
/**
* @brief Maps a block of process memory.
* @param dstProcessHandle Handle of the process to map the memory in (destination)
* @param destAddress Address of the mapped block in the current process.
* @param srcProcessHandle Handle of the process to map the memory from (source)
* @param srcAddress Address of the mapped block in the source process.
* @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes).
*/
Result svcMapProcessMemoryEx(Handle dstProcessHandle, u32 destAddress, Handle srcProcessHandle, u32 vaSrc, u32 size);
/**
* @brief Unmaps a block of process memory.
* @param process Handle of the process to unmap the memory from
* @param destAddress Address of the block of memory to unmap
* @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes).
* This function should only be used to unmap memory mapped with svcMapProcessMemoryEx
*/
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
/**
* @brief Controls memory mapping, with the choice to use region attributes or not.
* @param[out] addr_out The virtual address resulting from the operation. Usually the same as addr0.
* @param addr0 The virtual address to be used for the operation.
* @param addr1 The virtual address to be (un)mirrored by @p addr0 when using @ref MEMOP_MAP or @ref MEMOP_UNMAP.
* It has to be pointing to a RW memory.
* Use NULL if the operation is @ref MEMOP_FREE or @ref MEMOP_ALLOC.
* @param size The requested size for @ref MEMOP_ALLOC and @ref MEMOP_ALLOC_LINEAR.
* @param op Operation flags. See @ref MemOp.
* @param perm A combination of @ref MEMPERM_READ and @ref MEMPERM_WRITE. Using MEMPERM_EXECUTE will return an error.
* Value 0 is used when unmapping memory.
* @param isLoader Whether to use the region attributes
* If a memory is mapped for two or more addresses, you have to use MEMOP_UNMAP before being able to MEMOP_FREE it.
* MEMOP_MAP will fail if @p addr1 was already mapped to another address.
*
* @sa svcControlMemory
*/
Result svcControlMemoryEx(u32* addr_out, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
///@}
///@name System
///@{
/**
* @brief Performs actions related to services or global handles.
* @param op The operation to perform, see @ref ServiceOp.
*
* Examples:
* svcControlService(SERVICEOP_GET_NAME, (char [12])outName, (Handle)clientOrSessionHandle);
* svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, (Handle *)&outHandle, (const char *)name);
*/
Result svcControlService(ServiceOp op, ...);
/**
* @brief Copy a handle from a process to another one.
* @param[out] out The output handle.
* @param outProcess Handle of the process of the output handle.
* @param in The input handle. Pseudo-handles are not accepted.
* @param inProcess Handle of the process of the input handle.
*/
Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess);
/**
* @brief Get the address and class name of the underlying kernel object corresponding to a handle.
* @param[out] outKAddr The output kernel address.
* @param[out] outName Output class name. The buffer should be large enough to contain it.
* @param in The input handle.
*/
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in);
/// Operations for svcControlProcess
typedef enum ProcessOp
{
PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch
///< svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0)
PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access
///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0)
PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT,
PROCESSOP_GET_ON_EXIT_EVENT,
PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process
///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va)
} ProcessOp;
Result svcControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3);
///@}

9
plugin/includes/logo.h Normal file
View File

@ -0,0 +1,9 @@
/* Generated by bin2c, do not edit manually */
#ifndef ____includes_logo_h_included
#define ____includes_logo_h_included
/* Contents of file .\data\logo.bin */
extern const long int __data_logo_bin_size;
extern const unsigned char __data_logo_bin[32808];
#endif /* ____includes_logo_h_included */

View File

@ -0,0 +1,134 @@
/* Generated by bin2c, do not edit manually */
/* Contents of file extheader.bin */
const long int nim_extheader_bin_size = 2048;
const unsigned char nim_extheader_bin[2048] = {
0x6E, 0x69, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00,
0x00, 0x00, 0x10, 0x00, 0x51, 0x00, 0x00, 0x00, 0x7C, 0x01, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x10, 0x15, 0x00, 0x05, 0x00, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x60, 0x15, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA8, 0x09, 0x00, 0x00, 0xC8, 0x02, 0x13, 0x00,
0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00,
0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00,
0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x3D,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01,
0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x61, 0x6D, 0x3A, 0x6E, 0x65, 0x74, 0x00, 0x00,
0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00,
0x63, 0x66, 0x67, 0x3A, 0x73, 0x00, 0x00, 0x00, 0x70, 0x74, 0x6D, 0x3A, 0x73, 0x00, 0x00, 0x00,
0x70, 0x74, 0x6D, 0x3A, 0x67, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x80, 0xF2, 0xFD, 0x00, 0x00, 0xF3,
0x80, 0x03, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x02, 0x20, 0xE3, 0x4B, 0xE4, 0x26, 0xA3, 0x5F, 0x83, 0x17, 0x88, 0xB5, 0x0F, 0xDA, 0x7A, 0x77,
0xDB, 0xC7, 0x03, 0x50, 0xB0, 0xEC, 0xE5, 0x9B, 0x47, 0x0B, 0x2F, 0x21, 0x97, 0xF7, 0x81, 0xF5,
0x57, 0xAB, 0xBF, 0x1D, 0xA0, 0x96, 0x72, 0x2A, 0x6F, 0x6F, 0x06, 0x51, 0xC3, 0x86, 0x4C, 0x31,
0x34, 0x10, 0x81, 0xDC, 0x61, 0x10, 0x73, 0x57, 0xEF, 0xCC, 0x49, 0x09, 0x46, 0x84, 0x9C, 0xEA,
0xFA, 0xDB, 0xC6, 0xF7, 0x8F, 0xCE, 0x40, 0x9F, 0xD0, 0x38, 0x90, 0xE4, 0x2D, 0xA1, 0xAC, 0x51,
0xF0, 0x9D, 0xF8, 0xBC, 0xC1, 0xDC, 0xD2, 0xCB, 0xDE, 0x60, 0x25, 0x3A, 0xB5, 0x12, 0xC5, 0x21,
0x1E, 0x41, 0x2C, 0x56, 0x8F, 0xB0, 0x8A, 0x8B, 0x0E, 0xF5, 0x40, 0x73, 0xD1, 0x17, 0x3F, 0x2B,
0x8E, 0x34, 0x3C, 0x55, 0xC2, 0xC4, 0x74, 0x74, 0x84, 0x2E, 0xDC, 0xC6, 0xB5, 0x95, 0x71, 0x7C,
0xBA, 0xF8, 0x3B, 0xC8, 0x31, 0x4A, 0x85, 0x7A, 0xD1, 0x76, 0xAD, 0x41, 0xBD, 0xAA, 0x9A, 0xE8,
0x81, 0x0A, 0x64, 0x99, 0x86, 0x41, 0x3A, 0xB2, 0xEB, 0x2F, 0xB7, 0x52, 0x3F, 0xDA, 0x2C, 0x60,
0x70, 0x45, 0xAB, 0x0F, 0x2C, 0x31, 0xD3, 0x35, 0x6E, 0x02, 0x40, 0x3C, 0x63, 0x58, 0x8F, 0x28,
0x5F, 0xD2, 0x7C, 0xDF, 0x42, 0xAA, 0x5E, 0x5D, 0x4D, 0x50, 0x61, 0xA9, 0x2A, 0x81, 0x49, 0xA7,
0xA3, 0x76, 0xE6, 0xA4, 0x33, 0x08, 0xAD, 0x93, 0xF5, 0x18, 0x54, 0x50, 0xFC, 0xAE, 0x66, 0x44,
0x92, 0x23, 0xB5, 0xF6, 0x2F, 0xAE, 0x57, 0xCB, 0x42, 0x92, 0x56, 0xAC, 0xE4, 0xBE, 0x83, 0xDB,
0x05, 0x5E, 0x18, 0x2E, 0x1D, 0xB4, 0x35, 0x8D, 0xE5, 0x3C, 0xDC, 0xB3, 0x90, 0x9D, 0x6C, 0xDB,
0x0F, 0xC0, 0xC3, 0xD8, 0x4F, 0xA5, 0xF3, 0x79, 0x8A, 0xC4, 0xD9, 0x13, 0x07, 0x06, 0x9F, 0xC1,
0xA6, 0x92, 0x3F, 0xDE, 0x62, 0x6C, 0x2B, 0xBC, 0x70, 0x94, 0x81, 0x79, 0x80, 0xF6, 0x6E, 0xF9,
0xBD, 0x6D, 0xC0, 0xDD, 0xA8, 0x7D, 0x54, 0xE3, 0xC8, 0x0D, 0x0E, 0xDA, 0x5F, 0x4C, 0x23, 0x75,
0x11, 0xE2, 0x0F, 0x16, 0x0A, 0x48, 0x5D, 0xE9, 0xD3, 0x5F, 0x7E, 0x8B, 0x55, 0x9B, 0xC1, 0xC9,
0xA4, 0xED, 0xA6, 0x1A, 0x3A, 0xFD, 0xA7, 0x82, 0x3B, 0x1C, 0xE3, 0x73, 0xE3, 0xBA, 0xC7, 0x86,
0x05, 0x93, 0xE4, 0x0A, 0xC1, 0x6C, 0x3E, 0xC8, 0x87, 0x11, 0x5F, 0x98, 0xCC, 0x76, 0x23, 0xA3,
0xA5, 0x79, 0x95, 0x0A, 0xDD, 0x7D, 0x27, 0x0F, 0x26, 0x85, 0x0A, 0xE9, 0x4F, 0x2D, 0x23, 0x44,
0xF7, 0xA5, 0xE8, 0x7F, 0x17, 0x7F, 0x65, 0x5F, 0x4D, 0xCD, 0x6D, 0xF2, 0xDB, 0x64, 0xF4, 0x30,
0xD1, 0x57, 0x2B, 0x92, 0x34, 0xDA, 0x60, 0xB5, 0xF7, 0x26, 0x4A, 0xC3, 0x66, 0xC0, 0x27, 0x35,
0xD5, 0xF2, 0xB5, 0xB0, 0xB0, 0xE6, 0xB3, 0xC9, 0x92, 0xEB, 0x8F, 0x40, 0x38, 0x9C, 0xE4, 0x30,
0x9C, 0x13, 0xF9, 0x2F, 0x5B, 0x90, 0x67, 0x30, 0xE9, 0x44, 0xD9, 0x7F, 0x8C, 0x09, 0x34, 0xEE,
0x89, 0x67, 0xD6, 0x00, 0xA1, 0x54, 0xB1, 0xD5, 0x10, 0x72, 0xD5, 0xDA, 0x15, 0x1E, 0x50, 0xB4,
0x25, 0xD7, 0x82, 0xD3, 0xAA, 0x0B, 0xD0, 0x65, 0x77, 0x8D, 0x5D, 0x07, 0xFF, 0x9B, 0x8E, 0xF0,
0x8C, 0x37, 0x23, 0x13, 0x6C, 0xFD, 0xF8, 0xE6, 0xB6, 0x8E, 0xB2, 0x89, 0xAB, 0x44, 0x69, 0x30,
0x96, 0x50, 0x8B, 0x88, 0xCB, 0xF4, 0x47, 0x60, 0x7D, 0xC5, 0xA7, 0xAB, 0x3B, 0x45, 0x7E, 0xDF,
0x8C, 0x6D, 0x0C, 0x2B, 0x3F, 0x40, 0x3A, 0x55, 0xB2, 0xF7, 0xDF, 0x26, 0x21, 0xBE, 0xC5, 0x5F,
0x82, 0xCF, 0xE2, 0x51, 0x74, 0x43, 0xEC, 0x57, 0xA1, 0x04, 0x8C, 0x19, 0x4F, 0x86, 0xB9, 0x49,
0xFF, 0x2C, 0x00, 0xF0, 0x30, 0x01, 0x04, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x61, 0x6D, 0x3A, 0x6E, 0x65, 0x74, 0x00, 0x00,
0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00,
0x63, 0x66, 0x67, 0x3A, 0x73, 0x00, 0x00, 0x00, 0x70, 0x74, 0x6D, 0x3A, 0x73, 0x00, 0x00, 0x00,
0x70, 0x74, 0x6D, 0x3A, 0x67, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x80, 0xF2, 0xFD, 0x00, 0x00, 0xF3,
0x80, 0x03, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
};

79
plugin/includes/plgldr.h Normal file
View File

@ -0,0 +1,79 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <3ds/types.h>
#define MAX_BUFFER (50)
#define MAX_ITEMS_COUNT (64)
#define HeaderMagic (0x24584733) /* "3GX$" */
typedef struct
{
bool noFlash;
u8 pluginMemoryStrategy;
u8 persistent;
u32 lowTitleId;
char path[256];
u32 config[32];
} PluginLoadParameters;
typedef struct
{
u32 nbItems;
u8 states[MAX_ITEMS_COUNT];
char title[MAX_BUFFER];
char items[MAX_ITEMS_COUNT][MAX_BUFFER];
char hints[MAX_ITEMS_COUNT][MAX_BUFFER];
} PluginMenu;
typedef enum
{
PLG_WAIT = -1,
PLG_OK = 0,
PLG_SLEEP_ENTRY = 1,
PLG_SLEEP_EXIT = 2,
PLG_ABOUT_TO_SWAP = 3,
PLG_ABOUT_TO_EXIT = 4,
PLG_HOME_ENTER = 5,
PLG_HOME_EXIT = 6,
} PLG_Event;
typedef struct ///< Defined by Luma3DS plugin loader
{
u32 magic;
u32 version;
u32 heapVA;
u32 heapSize;
u32 exeSize; // Include sizeof(PluginHeader) + .text + .rodata + .data + .bss (0x1000 aligned too)
u32 isDefaultPlugin;
s32* plgldrEvent; ///< Used for synchronization
s32* plgldrReply; ///< Used for synchronization
u8 notifyHomeEvent;
u8 padding[3];
u32 reserved[23];
u32 config[32];
} PluginHeader;
typedef void (*OnPlgLdrEventCb_t)(s32 eventType);
Result plgLdrInit(void);
void plgLdrExit(void);
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled);
Result PLGLDR__SetPluginLoaderState(bool enabled);
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters);
Result PLGLDR__DisplayMenu(PluginMenu *menu);
Result PLGLDR__DisplayMessage(const char *title, const char *body);
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error);
Result PLGLDR__GetVersion(u32 *version);
Result PLGLDR__GetPluginPath(char *path);
Result PLGLDR__SetRosalinaMenuBlock(bool shouldBlock);
Result PLGLDR__SetSwapSettings(const char* swapPath, void* encFunc, void* decFunc, void* args);
void PLGLDR__SetEventCallback(OnPlgLdrEventCb_t cb);
void PLGLDR__Status(void);
Result PLGLDR__ClearPluginLoadParameters();
#ifdef __cplusplus
}
#endif

57
plugin/sendfile.py Normal file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env Python
import sys
import ftplib
import datetime
from ftplib import FTP
# Usage:
# sendfile.py "filename" "ftppath" "host" "ip"
def printf(string):
print(datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S') + " : " + string);
if __name__ == '__main__':
print("");
printf("FTP File Sender\n")
try:
filename = sys.argv[1]
path = sys.argv[2]
host = sys.argv[3]
port = sys.argv[4]
destfile = sys.argv[5]
ftp = FTP()
printf("Connecting to " + host + ":" + port);
ftp.connect(host, int(port), timeout=3);
printf("Connected");
printf("Opening " + filename);
file = open(sys.argv[1], "rb");
printf("Success");
except IOError as e:
printf("/!\ An error occured. /!\ ");
printf("Moving to: ftp:/" + path);
try:
ftp.cwd(path);
except IOError:
try:
ftp.mkd(path);
ftp.cwd(path);
except Exception:
pass
try:
printf("Sending file");
ftp.storbinary('STOR '+ destfile, file);
printf("Done")
file.close();
ftp.quit();
printf("Disconnected");
except Exception:
printf("/!\ An error occured. /!\ ");

86
plugin/sources/3gx_crt0.s Normal file
View File

@ -0,0 +1,86 @@
/*--------------------------------------------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/.
--------------------------------------------------------------------------------*/
@---------------------------------------------------------------------------------
@ 3DS processor selection
@---------------------------------------------------------------------------------
.cpu mpcore
@---------------------------------------------------------------------------------
@---------------------------------------------------------------------------------
.section ".crt0","ax"
.global _start, __service_ptr, __apt_appid, __ctru_heap_size, __ctru_linear_heap_size, __system_arglist, __system_runflags
@---------------------------------------------------------------------------------
.align 2
.arm
@---------------------------------------------------------------------------------
_start:
@---------------------------------------------------------------------------------
b startup
.ascii "_prm"
__service_ptr:
.word 0 @ Pointer to service handle override list -- if non-NULL it is assumed that we have been launched from a homebrew launcher
__apt_appid:
.word 0x300 @ Program APPID
__ctru_heap_size:
.word 24*1024*1024 @ Default heap size (24 MiB)
__ctru_linear_heap_size:
.word 24*1024*1024 @ Default linear heap size (24 MiB)
__system_arglist:
.word 0 @ Pointer to argument list (argc (u32) followed by that many NULL terminated strings)
__system_runflags:
.word 0 @ Flags to signal runtime restrictions to ctrulib
startup:
@ Restore plugin loader state, including the pushed stack registers
add sp, sp, #4
ldmfd sp!, {r0}
msr cpsr, r0
ldmfd sp!, {r0-r12}
mov lr, #0
@ Save return address
mov r4, lr
@ Clear the BSS section
ldr r0, =__bss_start__
ldr r1, =__bss_end__
sub r1, r1, r0
bl ClearMem
@ System initialization
mov r0, r4
bl initSystem
@ Set up argc/argv arguments for main()
ldr r0, =__system_argc
ldr r1, =__system_argv
ldr r0, [r0]
ldr r1, [r1]
@ Jump to user code
ldr r3, =main
ldr lr, =exit
bx r3
@---------------------------------------------------------------------------------
@ Clear memory to 0x00 if length != 0
@ r0 = Start Address
@ r1 = Length
@---------------------------------------------------------------------------------
ClearMem:
@---------------------------------------------------------------------------------
mov r2, #3 @ Round down to nearest word boundary
add r1, r1, r2 @ Shouldn't be needed
bics r1, r1, r2 @ Clear 2 LSB (and set Z)
bxeq lr @ Quit if copy size is 0
mov r2, #0
ClrLoop:
stmia r0!, {r2}
subs r1, r1, #4
bne ClrLoop
bx lr

View File

@ -0,0 +1,906 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ArticFunctions.hpp"
#include "Main.hpp"
#include "CTRPluginFramework/CTRPluginFramework.hpp"
#include "CTRPluginFramework/Clock.hpp"
#include "nim_extheader.h"
extern "C" {
#include "csvc.h"
}
extern bool isControllerMode;
constexpr u32 INITIAL_SETUP_APP_VERSION = 0;
enum class HandleType {
FILE,
DIR,
ARCHIVE
};
namespace ArticBaseFunctions {
ExHeader_Info lastAppExheader;
std::map<u64, HandleType> openHandles;
CTRPluginFramework::Mutex amMutex;
CTRPluginFramework::Mutex cfgMutex;
void Process_GetTitleID(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
if (good) good = mi.FinishInputParameters();
if (!good) return;
ArticBaseCommon::Buffer* tid_buffer = mi.ReserveResultBuffer(0, sizeof(u64));
if (!tid_buffer) {
return;
}
s64 out;
svcGetProcessInfo(&out, CUR_PROCESS_HANDLE, 0x10001);
memcpy(tid_buffer->data, &out, sizeof(s64));
mi.FinishGood(0);
}
void Process_GetProductInfo(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
if (good) good = mi.FinishInputParameters();
if (!good) return;
ArticBaseCommon::Buffer* prod_code_buffer = mi.ReserveResultBuffer(0, sizeof(FS_ProductInfo));
if (!prod_code_buffer) {
return;
}
u32 pid;
Result res = svcGetProcessId(&pid, CUR_PROCESS_HANDLE);
if (R_SUCCEEDED(res)) res = FSUSER_GetProductInfo((FS_ProductInfo*)prod_code_buffer->data, pid);
if (R_FAILED(res)) {
logger.Error("Process_GetProductInfo: 0x%08X", res);
mi.FinishInternalError();
return;
}
mi.FinishGood(0);
}
void Process_GetExheader(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
if (good) good = mi.FinishInputParameters();
if (!good) return;
ArticBaseCommon::Buffer* exheader_buf = mi.ReserveResultBuffer(0, sizeof(lastAppExheader));
if (!exheader_buf) {
return;
}
memcpy(exheader_buf->data, &lastAppExheader, exheader_buf->bufferSize);
mi.FinishGood(0);
}
void Process_ReadCode(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 offset, size;
if (good) good = mi.GetParameterS32(offset);
if (good) good = mi.GetParameterS32(size);
if (good) good = mi.FinishInputParameters();
if (!good) return;
s64 out;
if (R_FAILED(svcGetProcessInfo(&out, CUR_PROCESS_HANDLE, 0x10005))) {
mi.FinishInternalError();
return;
}
u8* start_addr = reinterpret_cast<u8*>(out);
ArticBaseCommon::Buffer* code_buf = mi.ReserveResultBuffer(0, size);
if (!code_buf) {
return;
}
memcpy(code_buf->data, start_addr + offset, size);
mi.FinishGood(0);
}
static void _Process_ReadExefs(ArticProtocolServer::MethodInterface& mi, const char* section) {
bool good = true;
if (good) good = mi.FinishInputParameters();
if (!good) return;
// Set up FS_Path structures
u8 path[0xC] = {0};
u32* type = (u32*)path;
char* name = (char*)(path + sizeof(u32));
*type = 0x2; // ExeFS
strcpy(name, section); // Icon
FS_Path archPath = { PATH_EMPTY, 1, "" };
FS_Path filePath = { PATH_BINARY, sizeof(path), path };
// Open the RomFS file and mount it
Handle fd = 0;
Result rc = FSUSER_OpenFileDirectly(&fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0);
if (R_FAILED(rc)) {
mi.FinishGood(rc);
return;
}
u64 file_size;
rc = FSFILE_GetSize(fd, &file_size);
if (R_FAILED(rc)) {
FSFILE_Close(fd);
mi.FinishGood(rc);
return;
}
ArticBaseCommon::Buffer* icon_buf = mi.ReserveResultBuffer(0, static_cast<size_t>(file_size));
if (!icon_buf) {
FSFILE_Close(fd);
return;
}
u32 bytes_read;
rc = FSFILE_Read(fd, &bytes_read, 0, icon_buf->data, icon_buf->bufferSize);
if (R_FAILED(rc)) {
FSFILE_Close(fd);
mi.ResizeLastResultBuffer(icon_buf, 0);
mi.FinishGood(rc);
return;
}
mi.ResizeLastResultBuffer(icon_buf, bytes_read);
FSFILE_Close(fd);
mi.FinishGood(0);
}
void Process_ReadIcon(ArticProtocolServer::MethodInterface& mi) {
_Process_ReadExefs(mi, "icon");
}
void Process_ReadBanner(ArticProtocolServer::MethodInterface& mi) {
_Process_ReadExefs(mi, "banner");
}
void Process_ReadLogo(ArticProtocolServer::MethodInterface& mi) {
_Process_ReadExefs(mi, "logo");
}
bool GetFSPath(ArticProtocolServer::MethodInterface& mi, FS_Path& path) {
void* pathPtr; size_t pathSize;
if (!mi.GetParameterBuffer(pathPtr, pathSize))
return false;
path.type = reinterpret_cast<FS_Path*>(pathPtr)->type;
path.size = reinterpret_cast<FS_Path*>(pathPtr)->size;
if (pathSize < 8 || path.size != pathSize - 0x8) {
mi.FinishInternalError();
return false;
}
path.data = (u8*)pathPtr + 0x8;
return true;
}
void FSUSER_OpenFileDirectly_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 archiveID;
FS_Path archPath;
FS_Path filePath;
s32 openFlags;
s32 attributes;
if (good) good = mi.GetParameterS32(archiveID);
if (good) good = GetFSPath(mi, archPath);
if (good) good = GetFSPath(mi, filePath);
if (good) good = mi.GetParameterS32(openFlags);
if (good) good = mi.GetParameterS32(attributes);
if (good) good = mi.FinishInputParameters();
if (!good) return;
Handle out;
Result res = FSUSER_OpenFileDirectly(&out, (FS_ArchiveID)archiveID, archPath, filePath, openFlags, attributes);
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* handle_buf = mi.ReserveResultBuffer(0, sizeof(Handle));
if (!handle_buf) {
FSFILE_Close(out);
return;
}
*reinterpret_cast<Handle*>(handle_buf->data) = out;
openHandles[(u64)out] = HandleType::FILE;
mi.FinishGood(res);
}
void FSUSER_OpenArchive_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 archiveID;
FS_Path archPath;
if (good) good = mi.GetParameterS32(archiveID);
if (good) good = GetFSPath(mi, archPath);
if (good) good = mi.FinishInputParameters();
if (!good) return;
FS_Archive out;
Result res = FSUSER_OpenArchive(&out, (FS_ArchiveID)archiveID, archPath);
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* handle_buf = mi.ReserveResultBuffer(0, sizeof(FS_Archive));
if (!handle_buf) {
FSUSER_CloseArchive(out);
return;
}
*reinterpret_cast<FS_Archive*>(handle_buf->data) = out;
openHandles[(u64)out] = HandleType::ARCHIVE;
mi.FinishGood(res);
}
void FSUSER_CloseArchive_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
FS_Archive archive;
if (good) good = mi.GetParameterS64(*reinterpret_cast<s64*>(&archive));
if (good) good = mi.FinishInputParameters();
if (!good) return;
Result res = FSUSER_CloseArchive(archive);
openHandles.erase((u64)archive);
mi.FinishGood(res);
}
void FSUSER_OpenFile_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
FS_Archive archive;
FS_Path filePath;
s32 openFlags;
s32 attributes;
if (good) good = mi.GetParameterS64(*reinterpret_cast<s64*>(&archive));
if (good) good = GetFSPath(mi, filePath);
if (good) good = mi.GetParameterS32(openFlags);
if (good) good = mi.GetParameterS32(attributes);
if (good) good = mi.FinishInputParameters();
if (!good) return;
Handle out;
Result res = FSUSER_OpenFile(&out, archive, filePath, openFlags, attributes);
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* handle_buf = mi.ReserveResultBuffer(0, sizeof(Handle));
if (!handle_buf) {
FSFILE_Close(out);
return;
}
*reinterpret_cast<Handle*>(handle_buf->data) = out;
// Citra always asks for the size after opening a file, provided it here.
u64 fileSize;
Result res2 = FSFILE_GetSize(out, &fileSize);
if (R_SUCCEEDED(res2)) {
ArticBaseCommon::Buffer* size_buf = mi.ReserveResultBuffer(1, sizeof(u64));
if (!size_buf) {
FSFILE_Close(out);
return;
}
*reinterpret_cast<u64*>(size_buf->data) = fileSize;
}
openHandles[(u64)out] = HandleType::FILE;
mi.FinishGood(res);
}
void FSUSER_OpenDirectory_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
FS_Archive archive;
FS_Path dirPath;
if (good) good = mi.GetParameterS64(*reinterpret_cast<s64*>(&archive));
if (good) good = GetFSPath(mi, dirPath);
if (good) good = mi.FinishInputParameters();
if (!good) return;
Handle out;
Result res = FSUSER_OpenDirectory(&out, archive, dirPath);
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* handle_buf = mi.ReserveResultBuffer(0, sizeof(Handle));
if (!handle_buf) {
FSDIR_Close(out);
return;
}
*reinterpret_cast<Handle*>(handle_buf->data) = out;
openHandles[(u64)out] = HandleType::DIR;
mi.FinishGood(res);
}
void FSFILE_Close_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 handle;
if (good) good = mi.GetParameterS32(handle);
if (good) good = mi.FinishInputParameters();
if (!good) return;
Result res = FSFILE_Close(handle);
openHandles.erase((u64)handle);
mi.FinishGood(res);
}
void FSFILE_GetSize_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 handle;
if (good) good = mi.GetParameterS32(handle);
if (good) good = mi.FinishInputParameters();
if (!good) return;
u64 fileSize;
Result res = FSFILE_GetSize(handle, &fileSize);
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* size_buf = mi.ReserveResultBuffer(0, sizeof(u64));
if (!size_buf) {
return;
}
*reinterpret_cast<u64*>(size_buf->data) = fileSize;
mi.FinishGood(res);
}
void FSFILE_GetAttributes_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 handle;
if (good) good = mi.GetParameterS32(handle);
if (good) good = mi.FinishInputParameters();
if (!good) return;
u32 attributes;
Result res = FSFILE_GetAttributes(handle, &attributes);
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* size_buf = mi.ReserveResultBuffer(0, sizeof(u32));
if (!size_buf) {
return;
}
*reinterpret_cast<u32*>(size_buf->data) = attributes;
mi.FinishGood(res);
}
void FSFILE_Read_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 handle, size;
s64 offset;
u32 bytes_read;
if (good) good = mi.GetParameterS32(handle);
if (good) good = mi.GetParameterS64(offset);
if (good) good = mi.GetParameterS32(size);
if (good) good = mi.FinishInputParameters();
if (!good) return;
logger.Debug("Read o=0x%08X, l=0x%08X", (u32)offset, (u32)size);
ArticBaseCommon::Buffer* read_buf = mi.ReserveResultBuffer(0, size);
if (!read_buf) {
return;
}
Result res = FSFILE_Read(handle, &bytes_read, offset, read_buf->data, read_buf->bufferSize);
if (R_FAILED(res)) {
mi.ResizeLastResultBuffer(read_buf, 0);
mi.FinishGood(res);
return;
}
mi.ResizeLastResultBuffer(read_buf, bytes_read);
mi.FinishGood(res);
}
void FSDIR_Read_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 handle;
s32 entryCount;
if (good) good = mi.GetParameterS32(handle);
if (good) good = mi.GetParameterS32(entryCount);
if (good) good = mi.FinishInputParameters();
if (!good) return;
ArticBaseCommon::Buffer* read_dir_buf = mi.ReserveResultBuffer(0, entryCount * sizeof(FS_DirectoryEntry));
if (!read_dir_buf) {
return;
}
u32 entries_read;
Result res = FSDIR_Read(handle, &entries_read, entryCount, reinterpret_cast<FS_DirectoryEntry*>(read_dir_buf->data));
if (R_FAILED(res)) {
mi.ResizeLastResultBuffer(read_dir_buf, 0);
mi.FinishGood(res);
return;
}
mi.ResizeLastResultBuffer(read_dir_buf, entries_read * sizeof(FS_DirectoryEntry));
mi.FinishGood(res);
}
void FSDIR_Close_(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s32 handle;
if (good) good = mi.GetParameterS32(handle);
if (good) good = mi.FinishInputParameters();
if (!good) return;
Result res = FSDIR_Close(handle);
openHandles.erase((u64)handle);
mi.FinishGood(res);
}
void System_IsAzaharInitialSetup(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
if (good) mi.FinishInputParameters();
ArticBaseCommon::Buffer* ret_buf = mi.ReserveResultBuffer(0, 4);
if (!ret_buf) {
return;
}
ret_buf->data[0] = INITIAL_SETUP_APP_VERSION;
mi.FinishGood(0);
}
void System_GetSystemFile(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
s8 type;
if (good) good = mi.GetParameterS8(type);
if (good) good = mi.FinishInputParameters();
if (!good) return;
if (type < 0 || type > 3) {
mi.FinishGood(-1);
return;
}
// SecureInfo_A
if (type >= 0 && type <= 2) {
Handle fspxiHandle;
Result res = svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, &fspxiHandle, "PxiFS0");
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
FSPXI_Archive archive;
res = FSPXI_OpenArchive(fspxiHandle, &archive, ARCHIVE_NAND_CTR_FS, fsMakePath(PATH_EMPTY, ""));
if (R_FAILED(res)) {
svcCloseHandle(fspxiHandle);
mi.FinishGood(res);
return;
}
const char* files[] = {"/rw/sys/SecureInfo_", "/rw/sys/LocalFriendCodeSeed_", "/private/movable.sed"};
char file_name[0x20] = {0};
strcpy(file_name, files[type]);
char* end = file_name + strlen(files[type]);
if (type == 0 || type == 1) {
*end = 'A';
}
FSPXI_File file;
res = FSPXI_OpenFile(fspxiHandle, &file, archive, fsMakePath(PATH_ASCII, file_name), FS_OPEN_READ, 0);
if (R_FAILED(res)) {
*end = 'B';
res = FSPXI_OpenFile(fspxiHandle, &file, archive, fsMakePath(PATH_ASCII, file_name), FS_OPEN_READ, 0);
if (R_FAILED(res)) {
FSPXI_CloseArchive(fspxiHandle, archive);
svcCloseHandle(fspxiHandle);
mi.FinishGood(res);
return;
}
}
u64 size = 0;
res = FSPXI_GetFileSize(fspxiHandle, file, &size);
if (R_FAILED(res)) {
FSPXI_CloseFile(fspxiHandle, file);
FSPXI_CloseArchive(fspxiHandle, archive);
svcCloseHandle(fspxiHandle);
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* ret_buf = mi.ReserveResultBuffer(0, (int)size);
if (!ret_buf) {
FSPXI_CloseFile(fspxiHandle, file);
FSPXI_CloseArchive(fspxiHandle, archive);
svcCloseHandle(fspxiHandle);
return;
}
u32 bytes_read = 0;
res = FSPXI_ReadFile(fspxiHandle, file, &bytes_read, 0, ret_buf->data, (u32)size);
FSPXI_CloseFile(fspxiHandle, file);
FSPXI_CloseArchive(fspxiHandle, archive);
svcCloseHandle(fspxiHandle);
mi.FinishGood(bytes_read != size ? -2 : res);
} else if (type == 3) {
Result res = amInit();
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
u32 deviceID;
res = AM_GetDeviceId(&deviceID);
amExit();
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
char filePath[0x50];
sprintf(filePath, "/luma/backups/%08lX/otp.bin", deviceID);
Handle file;
res = FSUSER_OpenFileDirectly(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filePath), FS_OPEN_READ, 0);
if (R_FAILED(res)) {
logger.Error("Missing OTP backup on SD card, please update your luma version and/or remove the console battery.");
mi.FinishGood(res);
return;
}
u64 size = 0;
res = FSFILE_GetSize(file, &size);
if (R_FAILED(res)) {
FSFILE_Close(file);
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* ret_buf = mi.ReserveResultBuffer(0, (int)size);
if (!ret_buf) {
FSFILE_Close(file);
return;
}
u32 bytes_read = 0;
res = FSFILE_Read(file, &bytes_read, 0, ret_buf->data, (u32)size);
FSFILE_Close(file);
mi.FinishGood(bytes_read != size ? -2 : res);
}
}
static u32 getle32(const u8* p)
{
return (p[0]<<0) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
}
static u32 lzss_get_decompressed_size(u8* compressed, u32 compressedsize)
{
u8* footer = compressed + compressedsize - 8;
u32 originalbottom = getle32(footer+4);
return originalbottom + compressedsize;
}
static int lzss_decompress(u8* compressed, u32 compressedsize, u8* decompressed, u32 decompressedsize)
{
u8* footer = compressed + compressedsize - 8;
u32 buffertopandbottom = getle32(footer+0);
//u32 originalbottom = getle32(footer+4);
u32 i, j;
u32 out = decompressedsize;
u32 index = compressedsize - ((buffertopandbottom>>24)&0xFF);
u32 segmentoffset;
u32 segmentsize;
u8 control;
u32 stopindex = compressedsize - (buffertopandbottom&0xFFFFFF);
memset(decompressed, 0, decompressedsize);
memcpy(decompressed, compressed, compressedsize);
while(index > stopindex)
{
control = compressed[--index];
for(i=0; i<8; i++)
{
if (index <= stopindex)
break;
if (index <= 0)
break;
if (out <= 0)
break;
if (control & 0x80)
{
if (index < 2)
{
// fprintf(stderr, "Error, compression out of bounds\n");
goto clean;
}
index -= 2;
segmentoffset = compressed[index] | (compressed[index+1]<<8);
segmentsize = ((segmentoffset >> 12)&15)+3;
segmentoffset &= 0x0FFF;
segmentoffset += 2;
if (out < segmentsize)
{
// fprintf(stderr, "Error, compression out of bounds\n");
goto clean;
}
for(j=0; j<segmentsize; j++)
{
u8 data;
if (out+segmentoffset >= decompressedsize)
{
// fprintf(stderr, "Error, compression out of bounds\n");
goto clean;
}
data = decompressed[out+segmentoffset];
decompressed[--out] = data;
}
}
else
{
if (out < 1)
{
// fprintf(stderr, "Error, compression out of bounds\n");
goto clean;
}
decompressed[--out] = compressed[--index];
}
control <<= 1;
}
}
return 0;
clean:
return -1;
}
void System_GetNIM(ArticProtocolServer::MethodInterface& mi) {
bool good = true;
if (good) good = mi.FinishInputParameters();
if (!good) return;
Handle file;
u32 archive_path[4] = {0x00002C02, 0x00040130, MEDIATYPE_NAND, 0x0};
u32 file_path[5] = {0x0, 0x0, 0x2, 0x646F632E, 0x00000065};
FS_Path archive_path_bin = {PATH_BINARY, 0x10, archive_path};
FS_Path file_path_bin = {PATH_BINARY, 0x14, file_path};
Result res = FSUSER_OpenFileDirectly(&file, ARCHIVE_SAVEDATA_AND_CONTENT, archive_path_bin, file_path_bin, FS_OPEN_READ, 0);
if (R_FAILED(res)) {
mi.FinishGood(res);
return;
}
u64 size = 0;
res = FSFILE_GetSize(file, &size);
if (R_FAILED(res)) {
FSFILE_Close(file);
mi.FinishGood(res);
return;
}
u8* buffer = (u8*)malloc((size_t)size);
u32 bytes_read = 0;
res = FSFILE_Read(file, &bytes_read, 0, buffer, (u32)size);
FSFILE_Close(file);
if (R_FAILED(res) || bytes_read != size) {
if (bytes_read != size) res = -2;
free(buffer);
mi.FinishGood(res);
return;
}
ArticBaseCommon::Buffer* ret_buf = mi.ReserveResultBuffer(0, nim_extheader_bin_size);
if (!ret_buf) {
free(buffer);
return;
}
memcpy(ret_buf->data, nim_extheader_bin, nim_extheader_bin_size);
ret_buf = mi.ReserveResultBuffer(1, lzss_get_decompressed_size(buffer, (u32)size));
if (!ret_buf) {
free(buffer);
return;
}
lzss_decompress(buffer, (u32)size, (u8*)ret_buf->data, ret_buf->bufferSize);
free(buffer);
u64 checksum = 0;
u64* start = (u64*)(ret_buf->data), *end = (u64*)((uintptr_t)(ret_buf->data + ret_buf->bufferSize) & ~7);
while (start != end) checksum = (checksum + *start++) * 0x6500000065ULL;
if (checksum != 0x50F9D326AB2239E9ULL) {
logger.Error("Invalid NIM checksum. Please ensure your console is on the latest version");
mi.ResizeLastResultBuffer(ret_buf, 0);
mi.FinishGood(-3);
return;
}
mi.FinishGood(0);
}
template<std::size_t N>
constexpr auto& METHOD_NAME(char const (&s)[N]) {
static_assert(N < sizeof(ArticBaseCommon::RequestPacket::method), "String exceeds 32 bytes!");
return s;
}
std::map<std::string, void(*)(ArticProtocolServer::MethodInterface& mi)> functionHandlers = {
{METHOD_NAME("Process_GetTitleID"), Process_GetTitleID},
{METHOD_NAME("Process_GetProductInfo"), Process_GetProductInfo},
{METHOD_NAME("Process_GetExheader"), Process_GetExheader},
{METHOD_NAME("Process_ReadCode"), Process_ReadCode},
{METHOD_NAME("Process_ReadIcon"), Process_ReadIcon},
{METHOD_NAME("Process_ReadBanner"), Process_ReadBanner},
{METHOD_NAME("Process_ReadLogo"), Process_ReadLogo},
{METHOD_NAME("FSUSER_OpenFileDirectly"), FSUSER_OpenFileDirectly_},
{METHOD_NAME("FSUSER_OpenArchive"), FSUSER_OpenArchive_},
{METHOD_NAME("FSUSER_CloseArchive"), FSUSER_CloseArchive_},
{METHOD_NAME("FSUSER_OpenFile"), FSUSER_OpenFile_},
{METHOD_NAME("FSUSER_OpenDirectory"), FSUSER_OpenDirectory_},
{METHOD_NAME("FSFILE_Close"), FSFILE_Close_},
{METHOD_NAME("FSFILE_GetAttributes"), FSFILE_GetAttributes_},
{METHOD_NAME("FSFILE_GetSize"), FSFILE_GetSize_},
{METHOD_NAME("FSFILE_Read"), FSFILE_Read_},
{METHOD_NAME("FSDIR_Read"), FSDIR_Read_},
{METHOD_NAME("FSDIR_Close"), FSDIR_Close_},
{METHOD_NAME("System_IsAzaharInitialSetup"), System_IsAzaharInitialSetup},
{METHOD_NAME("System_GetSystemFile"), System_GetSystemFile},
{METHOD_NAME("System_GetNIM"), System_GetNIM},
};
bool obtainExheader() {
Result loaderInitCustom(void);
void loaderExitCustom(void);
Result LOADER_GetLastApplicationProgramInfo(ExHeader_Info* exheaderInfo);
Result res = loaderInitCustom();
if (R_SUCCEEDED(res)) res = LOADER_GetLastApplicationProgramInfo(&lastAppExheader);
loaderExitCustom();
if (R_FAILED(res)) {
logger.Error("Failed to get ExHeader. Luma3DS may be outdated.");
return false;
}
return true;
}
static bool closeHandles() {
auto CloseHandle = [](u64 handle, HandleType type) {
switch (type)
{
case HandleType::FILE:
logger.Debug("Call pending FSFILE_Close");
FSFILE_Close((Handle)handle);
break;
case HandleType::DIR:
logger.Debug("Call pending FSDIR_Close");
FSDIR_Close((Handle)handle);
break;
case HandleType::ARCHIVE:
logger.Debug("Call pending FSUSER_CloseArchive");
FSUSER_CloseArchive((FS_Archive)handle);
break;
default:
break;
}
};
for (auto it = openHandles.begin(); it != openHandles.end(); it++) {
CloseHandle(it->first, it->second);
}
openHandles.clear();
return true;
}
std::vector<bool(*)()> setupFunctions {
obtainExheader,
};
std::vector<bool(*)()> destructFunctions {
closeHandles,
};
}

150
plugin/sources/BCLIM.cpp Normal file
View File

@ -0,0 +1,150 @@
#include "BCLIM.hpp"
namespace CTRPluginFramework {
static inline int ColorClamp(int Color)
{
if (Color > 255) Color = 255;
if (Color < 0) Color = 0;
return Color;
}
const u8 BCLIM::textureTileOrder[] =
{
0, 1, 4, 5,
2, 3, 6, 7,
8, 9, 12, 13,
10, 11, 14, 15
};
const u8 BCLIM::etc1Modifiers[][2] =
{
{ 2, 8 },
{ 5, 17 },
{ 9, 29 },
{ 13, 42 },
{ 18, 60 },
{ 24, 80 },
{ 33, 106 },
{ 47, 183 }
};
Color BCLIM::OpaqueBlendFunc(const Color& dst, const Color& src) {
return dst;
}
static Color ReadPixel(void* fb, int posX, int posY) {
constexpr u32 _bytesPerPixel = 2;
constexpr u32 _rowSize = 240;
u32 offset = (_rowSize - 1 - posY + posX * _rowSize) * _bytesPerPixel;
union
{
u16 u;
u8 b[2];
} half;
half.u = *reinterpret_cast<u16 *>((u32)fb + offset);
Color c;
c.r = (half.u >> 8) & 0xF8;
c.g = (half.u >> 3) & 0xFC;
c.b = (half.u << 3) & 0xF8;
c.a = 255;
return c;
}
static void WritePixel(void* fb, int posX, int posY, const Color& c) {
constexpr u32 _bytesPerPixel = 2;
constexpr u32 _rowSize = 240;
u32 offset = (_rowSize - 1 - posY + posX * _rowSize) * _bytesPerPixel;
union
{
u16 u;
u8 b[2];
} half;
half.u = (c.r & 0xF8) << 8;
half.u |= (c.g & 0xFC) << 3;
half.u |= (c.b & 0xF8) >> 3;
*reinterpret_cast<u16 *>((u32)fb + offset) = half.u;
}
void BCLIM::RenderInterfaceBackend(void* usrData, bool isRead, Color* c, int posX, int posY) {
if (isRead) {
*c = ReadPixel(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), posX, posY);
} else {
WritePixel(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), posX, posY, *c);
}
}
static bool FastContains(const Rect<int>& rect, Vector<int> point) {
return point.x >= rect.leftTop.x && point.x < (rect.leftTop.x + rect.size.x) &&
point.y >= rect.leftTop.y && point.y < (rect.leftTop.y + rect.size.y);
}
void BCLIM::Render(const Rect<int>& position, std::pair<void*, RenderBackend> backend, const Rect<int>& crop, const Rect<int>& limits, std::pair<bool, ColorBlendCallback> colorBlender) {
auto mappingScale = [](const Rect<int>& position, int x, int y, int w, int h) {
int posX = position.leftTop.x;
int posY = position.leftTop.y;
float progX = x/(float)w;
float progY = y/(float)h;
return Vector<int>(posX + position.size.x * progX, posY + position.size.y * progY);
};
auto mappingDirect = [](const Rect<int>& position, int x, int y, int w, int h) {
return Vector<int>(position.leftTop.x + x, position.leftTop.y + y);
};
Vector<int>(*mapping)(const Rect<int>& position, int x, int y, int w, int h);
Color current;
if (position.size.x == header->imag.width && position.size.y == header->imag.height)
mapping = mappingDirect;
else
mapping = mappingScale;
switch (header->imag.format) {
case TextureFormat::L8:
case TextureFormat::A8:
case TextureFormat::LA4:
case TextureFormat::LA8:
case TextureFormat::HILO8:
case TextureFormat::RGB8:
case TextureFormat::RGBA5551:
case TextureFormat::RGBA8:
case TextureFormat::ETC1:
case TextureFormat::L4:
case TextureFormat::A4:
case TextureFormat::RGBA4:
case TextureFormat::ETC1A4:
break;
case TextureFormat::RGB565:
{
int offs = 0;
Vector<int> prevPos(-1, -1);
for (int y = 0; y < header->imag.height; y+=8) {
for (int x = 0; x < header->imag.width; x+=8) {
for (int i = 0; i < 64; i++) {
int x2 = i % 8;
if (x + x2 >= crop.size.x || x + x2 >= header->imag.width) continue;
int y2 = i / 8;
if (y + y2 >= crop.size.y || y + y2 >= header->imag.height) continue;
auto drawPos = mapping(position, x + x2, y + y2, header->imag.width, header->imag.height);
if (!FastContains(limits, drawPos)) continue;
if (drawPos.x != prevPos.x || drawPos.y != prevPos.y) {
prevPos = drawPos;
int pos = textureTileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4);
u16 pixel = GetDataAt<u16>(offs + pos * 2);
u8 b = (u8)((pixel & 0x1F) << 3);
u8 g = (u8)((pixel & 0x7E0) >> 3);
u8 r = (u8)((pixel & 0xF800) >> 8);
if (colorBlender.first)
backend.second(backend.first, true, &current, drawPos.x, drawPos.y);
Color finalcolor = colorBlender.second(Color(r, g, b), current);
backend.second(backend.first, false, &finalcolor, drawPos.x, drawPos.y);
}
}
offs += 64 * 2;
}
}
}
break;
}
}
}

View File

@ -0,0 +1,158 @@
#include "CTRPluginFramework/Color.hpp"
#include <algorithm>
namespace CTRPluginFramework
{
Color& Color::Fade(float fading)
{
if (fading > 1.0f || fading < -1.0f)
return *this;
if (fading > 0.0f)
{
float tint = 1.f - fading;
r = std::min((int)(255 - (255 - r) * tint), 255);
g = std::min((int)(255 - (255 - g) * tint), 255);
b = std::min((int)(255 - (255 - b) * tint), 255);
}
else
{
float shade = 1.f + fading;
r *= shade;
g *= shade;
b *= shade;
}
return *this;
}
Color Color::Blend(const Color &color, BlendMode mode) const
{
// This is background, color is foreground
Color ret;
unsigned int _r;
unsigned int _g;
unsigned int _b;
unsigned int _a;
float forealpha = (float)color.a / 255.f;
float minusForeAlpha = 1.f - forealpha;
switch (mode)
{
case BlendMode::Alpha:
_r = (forealpha * (float)color.r) + ((float)r * minusForeAlpha);
_g = (forealpha * (float)color.g) + ((float)g * minusForeAlpha);
_b = (forealpha * (float)color.b) + ((float)b * minusForeAlpha);
_a = color.a * a;
ret.r = std::min(_r, 255u);
ret.g = std::min(_g, 255u);
ret.b = std::min(_b, 255u);
ret.a = std::min(_a, 255u);
break;
case BlendMode::Add:
_r = a * r / 255 + 255 * color.r / 255;
_g = a * g / 255 + 255 * color.g / 255;
_b = a * b / 255 + 255 * color.b / 255;
_a = a + color.a;
ret.r = std::min(_r, 255u);
ret.g = std::min(_g, 255u);
ret.b = std::min(_b, 255u);
ret.a = std::min(_a, 255u);
break;
case BlendMode::Sub:
_r = a * r / 255 - 255 * color.r / 255;
_g = a * g / 255 - 255 * color.g / 255;
_b = a * b / 255 - 255 * color.b / 255;
_a = a - color.a;
ret.r = std::max(_r, 0u);
ret.g = std::max(_g, 0u);
ret.b = std::max(_b, 0u);
ret.a = std::max(_a, 0u);
break;
case BlendMode::Mul:
ret = *this * color;
break;
default:
ret = color;
break;
}
return (ret);
}
bool Color::operator < (const Color &right) const
{
return (( r < right.r)
&& (b < right.b)
&& (g < right.g));
}
bool Color::operator <= (const Color &right) const
{
return (( r <= right.r)
&& (b <= right.b)
&& (g <= right.g));
}
bool Color::operator > (const Color &right) const
{
return (( r > right.r)
&& (b > right.b)
&& (g > right.g));
}
bool Color::operator >= (const Color &right) const
{
return (( r >= right.r)
&& (b >= right.b)
&& (g >= right.g));
}
Color Color::operator+(const Color& right) const
{
u8 _r(std::min(r + right.r, 255));
u8 _g(std::min(g + right.g, 255));
u8 _b(std::min(b + right.b, 255));
u8 _a(std::min(a + right.a, 255));
return (Color(_r, _g, _b, _a));
}
Color Color::operator-(const Color& right) const
{
u8 _r(std::max(r - right.r, 0));
u8 _g(std::max(g - right.g, 0));
u8 _b(std::max(b - right.b, 0));
u8 _a(std::max(a - right.a, 0));
return (Color(_r, _g, _b, _a));
}
Color Color::operator*(const Color& right) const
{
u8 _r(r * right.r / 255);
u8 _g(g * right.g / 255);
u8 _b(b * right.b / 255);
u8 _a(a * right.a / 255);
return (Color(_r, _g, _b, _a));
}
Color &Color::operator+=(const Color& right)
{
*this = *this + right;
return (*this);
}
Color &Color::operator-=(const Color& right)
{
*this = *this - right;
return (*this);
}
Color &Color::operator*=(const Color& right)
{
*this = *this * right;
return (*this);
}
}

View File

@ -0,0 +1,26 @@
#include "3ds/types.h"
#include "CTRPluginFramework/Time.hpp"
namespace CTRPluginFramework
{
const Time Time::Zero;
float Time::AsSeconds(void) const
{
return (_ticks / (float)TicksPerSecond);
}
int Time::AsMilliseconds(void) const
{
return static_cast<int>(_ticks / (TicksPerSecond / 1000.f));
}
s64 Time::AsMicroseconds(void) const
{
return static_cast<s64>(_ticks / (TicksPerSecond / 1000000.f));
}
}

122
plugin/sources/csvc.s Normal file
View File

@ -0,0 +1,122 @@
@ This paricular file is licensed under the following terms:
@ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
@ for any damages arising from the use of this software.
@
@ Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
@ and redistribute it freely, subject to the following restrictions:
@
@ The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
@ If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
@
@ Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
@ This notice may not be removed or altered from any source distribution.
.arm
.balign 4
.macro SVC_BEGIN name
.section .text.\name, "ax", %progbits
.global \name
.type \name, %function
.align 2
.cfi_startproc
\name:
.endm
.macro SVC_END
.cfi_endproc
.endm
SVC_BEGIN svcCustomBackdoor
svc 0x80
bx lr
SVC_END
SVC_BEGIN svcConvertVAToPA
svc 0x90
bx lr
SVC_END
SVC_BEGIN svcFlushDataCacheRange
svc 0x91
bx lr
SVC_END
SVC_BEGIN svcFlushEntireDataCache
svc 0x92
bx lr
SVC_END
SVC_BEGIN svcInvalidateInstructionCacheRange
svc 0x93
bx lr
SVC_END
SVC_BEGIN svcInvalidateEntireInstructionCache
svc 0x94
bx lr
SVC_END
SVC_BEGIN svcMapProcessMemoryEx
str r4, [sp, #-4]!
ldr r4, [sp, #4]
svc 0xA0
ldr r4, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcUnmapProcessMemoryEx
svc 0xA1
bx lr
SVC_END
SVC_BEGIN svcControlMemoryEx
push {r0, r4, r5}
ldr r0, [sp, #0xC]
ldr r4, [sp, #0xC+0x4]
ldr r5, [sp, #0xC+0x8]
svc 0xA2
pop {r2, r4, r5}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcControlMemoryUnsafe
str r4, [sp, #-4]!
ldr r4, [sp, #4]
svc 0xA3
ldr r4, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcFreeMemory
svc 0xA3
bx lr
SVC_END
SVC_BEGIN svcControlService
svc 0xB0
bx lr
SVC_END
SVC_BEGIN svcCopyHandle
str r0, [sp, #-4]!
svc 0xB1
ldr r2, [sp], #4
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcTranslateHandle
str r0, [sp, #-4]!
svc 0xB2
ldr r2, [sp], #4
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcControlProcess
svc 0xB3
bx lr
SVC_END

View File

@ -0,0 +1,41 @@
#include "3ds.h"
namespace ArticBaseFunctions {
static Handle loaderHandleCustom;
static int loaderRefCountCustom;
Result loaderInitCustom(void)
{
Result res;
if (AtomicPostIncrement(&loaderRefCountCustom)) return 0;
res = srvGetServiceHandle(&loaderHandleCustom, "Loader");
if (R_FAILED(res)) AtomicDecrement(&loaderRefCountCustom);
return res;
}
void loaderExitCustom(void)
{
if (AtomicDecrement(&loaderRefCountCustom)) return;
svcCloseHandle(loaderHandleCustom);
}
Result LOADER_GetLastApplicationProgramInfo(ExHeader_Info* exheaderInfo)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 *staticbufs = getThreadStaticBuffers();
cmdbuf[0] = IPC_MakeHeader(0x102, 0, 0);
u32 staticbufscpy[2] = {staticbufs[0], staticbufs[1]};
staticbufs[0] = IPC_Desc_StaticBuffer(0x400, 0);
staticbufs[1] = (u32)exheaderInfo;
if(R_FAILED(ret = svcSendSyncRequest(loaderHandleCustom))) return ret;
staticbufs[0] = staticbufscpy[0];
staticbufs[1] = staticbufscpy[1];
return (Result)cmdbuf[1];
}
}

2057
plugin/sources/logo.c Normal file

File diff suppressed because it is too large Load Diff

340
plugin/sources/main.cpp Normal file
View File

@ -0,0 +1,340 @@
#include "Main.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ArticProtocolServer.hpp"
#include "ArticFunctions.hpp"
#include "CTRPluginFramework/Clock.hpp"
#include "plgldr.h"
#include "BCLIM.hpp"
#include "logo.h"
#define SOC_ALIGN 0x1000
#define SOC_BUFFERSIZE 0x400000
Logger logger;
static bool should_run = true;
static int listen_fd = -1;
static int accept_fd = -1;
static ArticProtocolServer* articBase = nullptr;
static bool reloadBottomText = false;
int transferedBytes = 0;
extern "C" {
#include "csvc.h"
}
void DisableSleep(void)
{
u8 reg;
if (R_FAILED(mcuHwcInit()))
return;
if (R_FAILED(MCUHWC_ReadRegister(0x18, &reg, 1)))
return;
reg |= 0x60; ///< Disable home btn & shell state events
MCUHWC_WriteRegister(0x18, &reg, 1);
mcuHwcExit();
}
void EnableSleep(void)
{
u8 reg;
if (R_FAILED(mcuHwcInit()))
return;
if (R_FAILED(MCUHWC_ReadRegister(0x18, &reg, 1)))
return;
reg &= ~0x60; ///< Enable home btn & shell state events
MCUHWC_WriteRegister(0x18, &reg, 1);
mcuHwcExit();
}
void Start(void* arg) {
int res;
void* SOC_buffer = memalign(SOC_ALIGN, SOC_BUFFERSIZE);
if ((res = socInit((u32*)SOC_buffer, SOC_BUFFERSIZE)) != 0) {
free(SOC_buffer);
logger.Error("Server: Cannot initialize sockets");
return;
}
while (should_run) {
svcSleepThread(500000000);
struct sockaddr_in servaddr = {0};
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
logger.Error("Server: Cannot create socket");
continue;
}
if (!ArticProtocolServer::SetNonBlock(listen_fd, true)) {
logger.Error("Server:: Failed to set non-block");
close(listen_fd);
listen_fd = -1;
continue;
}
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERVER_PORT);
res = bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (res < 0) {
logger.Error("Server: Failed to bind() to port %d", SERVER_PORT);
close(listen_fd);
listen_fd = -1;
continue;
}
res = listen(listen_fd, 1);
if (res < 0) {
if (should_run) {
logger.Error("Server: Failed to listen()");
}
close(listen_fd);
listen_fd = -1;
continue;
}
struct in_addr host_id;
host_id.s_addr = gethostid();
logger.Info("Server: Listening on: %s:%d", inet_ntoa(host_id), SERVER_PORT);
struct sockaddr_in peeraddr = {0};
socklen_t peeraddr_len = sizeof(peeraddr);
bool error = false;
while (true) {
accept_fd = accept(listen_fd, (struct sockaddr *) &peeraddr, &peeraddr_len);
if (accept_fd < 0 || peeraddr_len == 0) {
if (errno == EWOULDBLOCK && should_run) {
svcSleepThread(10000000);
continue;
}
if (should_run) {
logger.Error("Server: Failed to accept()");
}
error = true;
break;
}
break;
}
if (listen_fd >= 0) {
close(listen_fd);
listen_fd = -1;
}
if (error)
continue;
logger.Info("Server: Connected: %s:%d", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
if (!ArticProtocolServer::SetNonBlock(accept_fd, true)) {
logger.Error("Server: Failed to set non-block");
shutdown(accept_fd, SHUT_RDWR);
close(accept_fd);
accept_fd = -1;
continue;
}
articBase = new ArticProtocolServer(accept_fd);
articBase->Serve();
accept_fd = -1;
delete articBase;
articBase = nullptr;
logger.Info("Server: Disconnected");
for (auto it = ArticBaseFunctions::destructFunctions.begin(); it != ArticBaseFunctions::destructFunctions.end(); it++) {
(*it)();
}
}
socExit();
free(SOC_buffer);
}
PrintConsole topScreenConsole, bottomScreenConsole;
void Main() {
logger.Start();
plgLdrInit();
bool prevPluginState = ((PluginHeader*)0x07000000)->config[0] != 0;
PLGLDR__SetPluginLoaderState(prevPluginState);
PLGLDR__ClearPluginLoadParameters();
plgLdrExit();
gspLcdInit();
gfxInitDefault();
consoleInit(GFX_TOP, &topScreenConsole);
consoleInit(GFX_BOTTOM, &bottomScreenConsole);
topScreenConsole.bg = 15; topScreenConsole.fg = 0;
bottomScreenConsole.bg = 15; bottomScreenConsole.fg = 0;
gfxSetDoubleBuffering(GFX_BOTTOM, false);
aptSetHomeAllowed(false);
consoleSelect(&bottomScreenConsole);
consoleClear();
consoleSelect(&topScreenConsole);
consoleClear();
{
CTRPluginFramework::BCLIM((void*)__data_logo_bin, __data_logo_bin_size).Render(CTRPluginFramework::Rect<int>((320 - 128) / 2, (240 - 128) / 2, 128, 128));
}
auto print_bottom_info = []() {
logger.Raw(false, "\n Azahar Artic Setup v%d.%d.%d\n\n"
" - A: Enter sleep \n"
" - X: Show debug log \n"
" - Y: Restart server \n"
" - START: Exit \n"
, VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
};
print_bottom_info();
logger.Raw(true, "");
bool setupCorrect = true;
for (auto it = ArticBaseFunctions::setupFunctions.begin(); it != ArticBaseFunctions::setupFunctions.end(); it++) {
setupCorrect = (*it)() && setupCorrect;
}
Thread serverThread = nullptr;
if (!setupCorrect) {
logger.Error("Server: Setup failed");
} else {
s32 prio = 0;
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
serverThread = threadCreate(Start, nullptr, 0x1000, prio-1, -2, false);
}
CTRPluginFramework::Clock clock;
bool sleeping = false;
DisableSleep();
while (aptMainLoop())
{
if (reloadBottomText) {
reloadBottomText = false;
print_bottom_info();
}
hidScanInput();
u32 kDown = hidKeysDown();
u32 kHeld = hidKeysHeld();
if (((kDown & ~KEY_SELECT) != 0) && sleeping) {
sleeping = false;
GSPLCD_PowerOnBacklight(3);
continue;
}
if ((kDown & KEY_A) && !sleeping) {
sleeping = true;
GSPLCD_PowerOffBacklight(3);
}
if ((kDown & KEY_START)) {
logger.Info("Server: Exiting");
break;
}
if ((kDown & KEY_Y)) {
if (articBase) {
logger.Info("Server: Restarting");
articBase->QueryStop();
} else {
logger.Debug("Server: Not started yet.");
}
}
if ((kDown & KEY_X)) {
if (logger.debug_enable) {
logger.Info("Server: Debug log disabled");
logger.debug_enable = false;
} else {
logger.Info("Server: Debug log enabled");
logger.debug_enable = true;
}
}
if (clock.HasTimePassed(CTRPluginFramework::Seconds(1)))
{
if (articBase) {
CTRPluginFramework::Time t = clock.GetElapsedTime();
float bytes = transferedBytes / t.AsSeconds();
transferedBytes = 0;
float value = (bytes >= 1000 * 1000) ? (bytes / 1000.f * 1000.f) : (bytes / 1000.f);
const char* unit = (bytes >= 1000 * 1000) ? "MB/s" : "KB/s";
logger.Traffic(" Traffic: %.02f %s \n", value, unit);
clock.Restart();
} else {
logger.Traffic(" \n");
}
}
// Flush and swap framebuffers
gfxFlushBuffers();
gfxSwapBuffers();
//Wait for VBlank
gspWaitForVBlank();
}
should_run = false;
if (listen_fd >= 0) {
close(listen_fd);
listen_fd = -1;
}
if (articBase) {
articBase->QueryStop();
}
if (serverThread) {
threadJoin(serverThread, U64_MAX);
threadFree(serverThread);
}
EnableSleep();
// Flush and swap framebuffers
gfxFlushBuffers();
gfxSwapBuffers();
//Wait for VBlank
gspWaitForVBlank();
gfxExit();
gspLcdExit();
logger.End();
}
extern "C" {
int main(int argc, char* argv[]);
}
// Entrypoint, game will starts when you exit this function
int main(int argc, char* argv[])
{
Main();
return 0;
}

347
plugin/sources/plgldr.c Normal file
View File

@ -0,0 +1,347 @@
#include <3ds.h>
#include "plgldr.h"
#include <string.h>
#include "csvc.h"
static Handle plgLdrHandle;
static Handle plgLdrArbiter = 0;
static s32* plgEvent;
static s32* plgReply;
static int plgLdrRefCount;
static bool isN3DS;
static OnPlgLdrEventCb_t onPlgLdrEventCb = NULL;
static Result PLGLDR__GetArbiter(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(9, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
plgLdrArbiter = cmdbuf[3];
}
return res;
}
Result plgLdrInit(void)
{
s64 out = 0;
Result res = 0;
svcGetSystemInfo(&out, 0x10000, 0x201);
isN3DS = out != 0;
if (AtomicPostIncrement(&plgLdrRefCount) == 0)
res = svcConnectToPort(&plgLdrHandle, "plg:ldr");
if (R_SUCCEEDED(res))
{
u32 res2 = PLGLDR__GetArbiter();
if (R_SUCCEEDED(res2) || res2 == 0xE0E01BF4) { // Succeeded or not implemented (citra)
PluginHeader *header = (PluginHeader *)0x07000000;
plgEvent = header->plgldrEvent;
plgReply = header->plgldrReply;
} else
plgLdrExit();
}
else
plgLdrExit();
return res;
}
void plgLdrExit(void)
{
if (AtomicDecrement(&plgLdrRefCount))
return;
if (plgLdrHandle)
svcCloseHandle(plgLdrHandle);
if (plgLdrArbiter)
svcCloseHandle(plgLdrArbiter);
plgLdrHandle = plgLdrArbiter = 0;
}
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(2, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
*isEnabled = cmdbuf[2];
}
return res;
}
Result PLGLDR__SetPluginLoaderState(bool enabled)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
cmdbuf[1] = (u32)enabled;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(4, 2, 4);
cmdbuf[1] = (u32)parameters->noFlash | (((u32)parameters->pluginMemoryStrategy) << 8) | (((u32)parameters->persistent) << 16);
cmdbuf[2] = parameters->lowTitleId;
cmdbuf[3] = IPC_Desc_Buffer(256, IPC_BUFFER_R);
cmdbuf[4] = (u32)parameters->path;
cmdbuf[5] = IPC_Desc_Buffer(32 * sizeof(u32), IPC_BUFFER_R);
cmdbuf[6] = (u32)parameters->config;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
void Flash(u32 color);
Result PLGLDR__DisplayMenu(PluginMenu *menu)
{
Result res = 0;
u32 nbItems = menu->nbItems;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(5, 1, 8);
cmdbuf[1] = nbItems;
cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW);
cmdbuf[3] = (u32)menu->states;
cmdbuf[4] = IPC_Desc_Buffer(MAX_BUFFER, IPC_BUFFER_R);
cmdbuf[5] = (u32)menu->title;
cmdbuf[6] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
cmdbuf[7] = (u32)menu->items;
cmdbuf[8] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
cmdbuf[9] = (u32)menu->hints;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
else Flash(0xFF0000);
return res;
}
Result PLGLDR__DisplayMessage(const char *title, const char *body)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(6, 0, 4);
cmdbuf[1] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
cmdbuf[2] = (u32)title;
cmdbuf[3] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
cmdbuf[4] = (u32)body;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(7, 1, 4);
cmdbuf[1] = error;
cmdbuf[2] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
cmdbuf[3] = (u32)title;
cmdbuf[4] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
cmdbuf[5] = (u32)body;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__GetVersion(u32 *version)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(8, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
if (cmdbuf[0] != IPC_MakeHeader(8, 2, 0))
return 0xD900182F;
res = cmdbuf[1];
if (version)
*version = cmdbuf[2];
}
return res;
}
Result PLGLDR__GetPluginPath(char *path)
{
if (path == NULL)
return MAKERESULT(28, 7, 254, 1014); ///< Usage, App, Invalid argument
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(10, 0, 2);
cmdbuf[1] = IPC_Desc_Buffer(255, IPC_BUFFER_RW);
cmdbuf[2] = (u32)path;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetRosalinaMenuBlock(bool shouldBlock)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(11, 1, 0);
cmdbuf[1] = (u32)shouldBlock;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetSwapSettings(const char* swapPath, void* encFunc, void* decFunc, void* args)
{
Result res = 0;
u32* trueArgs;
u32* cmdbuf = getThreadCommandBuffer();
u32 buf[0x10] = { 0 };
const char* trueSwapPath;
if (swapPath) trueSwapPath = swapPath;
else trueSwapPath = "\0";
if (args) trueArgs = args;
else trueArgs = buf;
cmdbuf[0] = IPC_MakeHeader(12, 2, 4);
cmdbuf[1] = (encFunc) ? (svcConvertVAToPA(encFunc, false) | (1 << 31)) : 0;
cmdbuf[2] = (decFunc) ? (svcConvertVAToPA(decFunc, false) | (1 << 31)) : 0;
cmdbuf[3] = IPC_Desc_Buffer(16 * sizeof(u32), IPC_BUFFER_R);
cmdbuf[4] = (u32)trueArgs;
cmdbuf[5] = IPC_Desc_Buffer(strlen(trueSwapPath) + 1, IPC_BUFFER_R);
cmdbuf[6] = (u32)trueSwapPath;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
void PLGLDR__SetEventCallback(OnPlgLdrEventCb_t cb)
{
onPlgLdrEventCb = cb;
}
static s32 __ldrex__(s32 *addr)
{
s32 val;
do
val = __ldrex(addr);
while (__strex(addr, val));
return val;
}
static void __strex__(s32 *addr, s32 val)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
s32 v;
do
v = __ldrex(addr);
while (__strex(addr, val));
#pragma GCC diagnostic pop
}
void PLGLDR__Status(void)
{
}
s32 PLGLDR__FetchEvent(void)
{
s32 event = __ldrex__(plgEvent); // exclusive read necessary?
return event;
}
void PLGLDR__Reply(s32 event)
{
__strex__(plgReply, PLG_OK);
if (event < PLG_ABOUT_TO_SWAP)
{
__strex__(plgEvent, PLG_OK);
return;
}
svcArbitrateAddress(plgLdrArbiter, (u32)plgReply, ARBITRATION_SIGNAL, 1, 0);
if (event == PLG_ABOUT_TO_SWAP)
{
__strex__(plgEvent, PLG_WAIT);
svcArbitrateAddress(plgLdrArbiter, (u32)plgEvent, ARBITRATION_WAIT_IF_LESS_THAN, PLG_OK, 0);
}
else if (event == PLG_ABOUT_TO_EXIT) {
svcExitThread();
}
else
{
__strex__(plgEvent, PLG_OK);
}
}
Result PLGLDR__ClearPluginLoadParameters()
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(14, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}