Initial Commit

This commit is contained in:
Nessy
2023-06-07 09:46:00 +01:00
commit 595280e62e
65 changed files with 8324 additions and 0 deletions

224
Makefile Normal file
View File

@@ -0,0 +1,224 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>DEVKITARM")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITARM)/3ds_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#
# NO_SMDH: if set to anything, no SMDH file is generated.
# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional)
# APP_TITLE is the name of the app stored in the SMDH file (Optional)
# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional)
# APP_AUTHOR is the author of the app stored in the SMDH file (Optional)
# ICON is the filename of the icon (.png), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.png
# - icon.png
# - <libctru folder>/default_icon.png
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := $(sort $(dir $(wildcard src/*/ src/)))
DATA := data
INCLUDES := include $(SOURCES)
INCLUDES += assets
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
USA := USA
EUR := EUR
JP := JP
REGION := USA
ifeq ($(USA), $(REGION))
LINK_SCRIPT := oot.ld
ASFLAGS += -D _USA_=1 -D _JP_=0 -D _EUR_=0
endif
ifeq ($(JP), $(REGION))
LINK_SCRIPT := oot_j.ld
ASFLAGS += -D _USA_=0 -D _JP_=1 -D _EUR_=0
endif
ifeq ($(EUR), $(REGION))
LINK_SCRIPT := oot_e.ld
ASFLAGS += -D _USA_=0 -D _JP_=0 -D _EUR_=1
endif
VERFLAGS := -D USA=$(USA) -D JP=$(JP) -D EUR=$(EUR) -D REGION=$(REGION)
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=softfp -mtp=soft -mfpu=vfpv2
CFLAGS := -g -Wall -mword-relocations -D DEBUG \
-fomit-frame-pointer -ffunction-sections \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM11 -D_3DS $(VERFLAGS)
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS += -g $(ARCH) $(VERFLAGS)
LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) -T $(TOPDIR)/$(LINK_SCRIPT) -nostdlib $(VERFLAGS) -lgcc
LIBS := -lgcc
# Define version for the C code
ifeq ($(REGION), $(USA))
CFLAGS += -g -DVersion_USA
endif
ifeq ($(REGION), $(JP))
CFLAGS += -g -DVersion_JP
endif
ifeq ($(REGION), $(EUR))
CFLAGS += -g -DVersion_EUR
endif
citra ?= 0
ifneq ($(citra), 0)
CFLAGS += -g -DCITRA
endif
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(DEVKITARM) include/citro3d
#---------------------------------------------------------------------------------
# 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)))
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \
$(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)
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.png)
ifneq (,$(findstring $(TARGET).png,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).png
else
ifneq (,$(findstring icon.png,$(icons)))
export APP_ICON := $(TOPDIR)/icon.png
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@if ! python3 patch.py $(OUTPUT).elf; then \
python patch.py $(OUTPUT).elf; \
fi
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).elf
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).elf : $(OFILES) $(TOPDIR)/$(LINK_SCRIPT)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
# rules for assembling GPU shaders
#---------------------------------------------------------------------------------
define shader-as
$(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
picasso -o $(CURBIN) $1
bin2s $(CURBIN) | $(AS) -o $@
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h
endef
%.shbin.o : %.v.pica %.g.pica
@echo $(notdir $^)
@$(call shader-as,$^)
%.shbin.o : %.v.pica
@echo $(notdir $<)
@$(call shader-as,$<)
%.shbin.o : %.shlist
@echo $(notdir $<)
@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Patch Files/JP/3DS/code.ips Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

36
README.md Normal file
View File

@@ -0,0 +1,36 @@
# OoT3D Free Cam
This project allows new 3DS systems and Citra to control the camera in The Legend of Zelda: Ocarina of Time 3D with use of the c-stick, just like Majora's Mask 3D.
This project is still a WIP with 4 main features planned but not implemented:
* Options to invert the axes
* Sensitivity options
* Better floor detection (sometimes you're kicked out of free cam when you shouldn't be with the current system)
* JP version support (although the patch files are there they will currently crash as soon as you enter free cam)
# Shoutouts
* Shoutouts to gamestabled for creating the practice patch and randomizer projects this shamelessly steals from
* By extension, shoutouts to anyone gamestabled shouts out in those projects
* Shoutouts to the OoT decomp team for creating an invaluable resource I referenced heavily while creating this
* Shoutouts to Ura Yukimitsu for showing me how to get c-stick input working
* Shoutouts to Hylian Freddy for creating the practice patch's memory viewer, another fantastic resource
* Shoutouts to Kestron for motivating me to create the original free cam
* Shoutouts to Skilar for getting me to make it a standalone project
* Shoutouts to the whole OoT3DR dev team for being cool people and the community for keeping me motivated
# How to Use:
* Find the patch files inside the "Patch Files" folder in this repository. They're divided by regional version of OoT3D (USA/EUR/JP) and by platform (3DS/Citra), so choose the correct ones.
### 3DS
* Install a recent build of Luma3DS, or use the one linked above. If you already have Luma3DS, you can upgrade by simply replacing "boot.firm" at the root of your SD card.
* Place the patch files ("code.ips" and "exheader.bin") in the following folder (you may need to create the folder):
| Version | Location | Notes |
|---|---|---|
| USA | /luma/titles/0004000000033500 | |
| JP | /luma/titles/0004000000033400 | WIP - will crash |
| EUR | /luma/titles/0004000000033600 | |
* Hold Select while powering on the console to launch the Luma3DS menu. Turn on "Enable game patching". You should only need to do this once, unless if you disable game patching in the future.
* It should work now! If not, you likely need to use a different version of Luma3DS (try the one linked above).
### Citra Emulator
* Right-click on OoT3D from the game list and select "Open Mods Location". Place the patch files ("code.ips" and "exheader.bin") there.

View File

@@ -0,0 +1,32 @@
/**
* @file mappable.h
* @brief Mappable memory allocator.
* Edited from
* https://github.com/devkitPro/libctru/blob/b18f04d88739283f6ffb55fe5ea77c73796cf61b/libctru/include/3ds/allocator/mappable.h
*/
#pragma once
#include <3ds/types.h>
#define OS_MAP_AREA_BEGIN 0x10000000 ///< Start of the mappable area in the virtual address space
#define OS_MAP_AREA_END 0x14000000 ///< End of the mappable area in the virtual address space
/**
* @brief Initializes the mappable allocator.
* @param addrMin Minimum address.
* @param addrMax Maxium address.
*/
void mappableInit(u32 addrMin, u32 addrMax);
/**
* @brief Finds a mappable memory area.
* @param size Size of the area to find.
* @return The mappable area.
*/
void* mappableAlloc(size_t size);
/**
* @brief Frees a mappable area (stubbed).
* @param mem Mappable area to free.
*/
void mappableFree(void* mem);

76
include/3ds/env.h Normal file
View File

@@ -0,0 +1,76 @@
/**
* @file env.h
* @brief Homebrew environment information.
* Source: https://github.com/devkitPro/libctru/blob/6360f4bdb1ca5f8131ffc92640c1dd16afb63083/libctru/include/3ds/env.h
*/
#pragma once
#include "types.h"
/// System run-flags.
enum {
RUNFLAG_APTWORKAROUND = BIT(0), ///< Use APT workaround.
RUNFLAG_APTREINIT = BIT(1), ///< Reinitialize APT.
RUNFLAG_APTCHAINLOAD = BIT(2), ///< Chainload APT on return.
};
#define __service_ptr NULL
/**
* @brief Gets whether the application was launched from a homebrew environment.
* @return Whether the application was launched from a homebrew environment.
*/
static inline bool envIsHomebrew(void) {
return __service_ptr != NULL;
}
/**
* @brief Retrieves a handle from the environment handle list.
* @param name Name of the handle.
* @return The retrieved handle.
*/
Handle envGetHandle(const char* name);
/**
* @brief Gets the environment-recommended app ID to use with APT.
* @return The APT app ID.
*/
static inline u32 envGetAptAppId(void) {
extern u32 __apt_appid;
return __apt_appid;
}
/**
* @brief Gets the size of the application heap.
* @return The application heap size.
*/
static inline u32 envGetHeapSize(void) {
extern u32 __ctru_heap_size;
return __ctru_heap_size;
}
/**
* @brief Gets the size of the linear heap.
* @return The linear heap size.
*/
static inline u32 envGetLinearHeapSize(void) {
extern u32 __ctru_linear_heap_size;
return __ctru_linear_heap_size;
}
/**
* @brief Gets the environment argument list.
* @return The argument list.
*/
static inline const char* envGetSystemArgList(void) {
extern const char* __system_arglist;
return __system_arglist;
}
/**
* @brief Gets the environment run flags.
* @return The run flags.
*/
static inline u32 envGetSystemRunFlags(void) {
extern u32 __system_runflags;
return __system_runflags;
}

111
include/3ds/ipc.h Normal file
View File

@@ -0,0 +1,111 @@
/**
* @file ipc.h
* @brief Inter Process Communication helpers
*/
#pragma once
#include "3ds/types.h"
/// IPC buffer access rights.
typedef enum {
IPC_BUFFER_R = BIT(1), ///< Readable
IPC_BUFFER_W = BIT(2), ///< Writable
IPC_BUFFER_RW = IPC_BUFFER_R | IPC_BUFFER_W ///< Readable and Writable
} IPC_BufferRights;
/**
* @brief Creates a command header to be used for IPC
* @param command_id ID of the command to create a header for.
* @param normal_params Size of the normal parameters in words. Up to 63.
* @param translate_params Size of the translate parameters in words. Up to 63.
* @return The created IPC header.
*
* Normal parameters are sent directly to the process while the translate parameters might go through modifications and
* checks by the kernel. The translate parameters are described by headers generated with the IPC_Desc_* functions.
*
* @note While #normal_params is equivalent to the number of normal parameters, #translate_params includes the size
* occupied by the translate parameters headers.
*/
static inline u32 IPC_MakeHeader(u16 command_id, unsigned normal_params, unsigned translate_params) {
return ((u32)command_id << 16) | (((u32)normal_params & 0x3F) << 6) | (((u32)translate_params & 0x3F) << 0);
}
/**
* @brief Creates a header to share handles
* @param number The number of handles following this header. Max 64.
* @return The created shared handles header.
*
* The #number next values are handles that will be shared between the two processes.
*
* @note Zero values will have no effect.
*/
static inline u32 IPC_Desc_SharedHandles(unsigned number) {
return ((u32)(number - 1) << 26);
}
/**
* @brief Creates the header to transfer handle ownership
* @param number The number of handles following this header. Max 64.
* @return The created handle transfer header.
*
* The #number next values are handles that will be duplicated and closed by the other process.
*
* @note Zero values will have no effect.
*/
static inline u32 IPC_Desc_MoveHandles(unsigned number) {
return ((u32)(number - 1) << 26) | 0x10;
}
/**
* @brief Returns the code to ask the kernel to fill the handle with the current process ID.
* @return The code to request the current process ID.
*
* The next value is a placeholder that will be replaced by the current process ID by the kernel.
*/
static inline u32 IPC_Desc_CurProcessId(void) {
return 0x20;
}
static inline DEPRECATED u32 IPC_Desc_CurProcessHandle(void) {
return IPC_Desc_CurProcessId();
}
/**
* @brief Creates a header describing a static buffer.
* @param size Size of the buffer. Max ?0x03FFFF?.
* @param buffer_id The Id of the buffer. Max 0xF.
* @return The created static buffer header.
*
* The next value is a pointer to the buffer. It will be copied to TLS offset 0x180 + static_buffer_id*8.
*/
static inline u32 IPC_Desc_StaticBuffer(size_t size, unsigned buffer_id) {
return (size << 14) | ((buffer_id & 0xF) << 10) | 0x2;
}
/**
* @brief Creates a header describing a buffer to be sent over PXI.
* @param size Size of the buffer. Max 0x00FFFFFF.
* @param buffer_id The Id of the buffer. Max 0xF.
* @param is_read_only true if the buffer is read-only. If false, the buffer is considered to have read-write access.
* @return The created PXI buffer header.
*
* The next value is a phys-address of a table located in the BASE memregion.
*/
static inline u32 IPC_Desc_PXIBuffer(size_t size, unsigned buffer_id, bool is_read_only) {
u8 type = 0x4;
if (is_read_only)
type = 0x6;
return (size << 8) | ((buffer_id & 0xF) << 4) | type;
}
/**
* @brief Creates a header describing a buffer from the main memory.
* @param size Size of the buffer. Max 0x0FFFFFFF.
* @param rights The rights of the buffer for the destination process.
* @return The created buffer header.
*
* The next value is a pointer to the buffer.
*/
static inline u32 IPC_Desc_Buffer(size_t size, IPC_BufferRights rights) {
return (size << 4) | 0x8 | rights;
}

214
include/3ds/os.h Normal file
View File

@@ -0,0 +1,214 @@
/**
* @file os.h
* @brief OS related stuff.
*/
#pragma once
#include "svc.h"
#define SYSCLOCK_SOC (16756991)
#define SYSCLOCK_ARM9 (SYSCLOCK_SOC * 8)
#define SYSCLOCK_ARM11 (SYSCLOCK_ARM9 * 2)
#define SYSCLOCK_ARM11_NEW (SYSCLOCK_ARM11 * 3)
#define CPU_TICKS_PER_MSEC (SYSCLOCK_ARM11 / 1000.0)
#define CPU_TICKS_PER_USEC (SYSCLOCK_ARM11 / 1000000.0)
/// Packs a system version from its components.
#define SYSTEM_VERSION(major, minor, revision) (((major) << 24) | ((minor) << 16) | ((revision) << 8))
/// Retrieves the major version from a packed system version.
#define GET_VERSION_MAJOR(version) ((version) >> 24)
/// Retrieves the minor version from a packed system version.
#define GET_VERSION_MINOR(version) (((version) >> 16) & 0xFF)
/// Retrieves the revision version from a packed system version.
#define GET_VERSION_REVISION(version) (((version) >> 8) & 0xFF)
/// Memory regions.
typedef enum {
MEMREGION_ALL = 0, ///< All regions.
MEMREGION_APPLICATION = 1, ///< APPLICATION memory.
MEMREGION_SYSTEM = 2, ///< SYSTEM memory.
MEMREGION_BASE = 3, ///< BASE memory.
} MemRegion;
/// Tick counter.
typedef struct {
u64 elapsed; ///< Elapsed CPU ticks between measurements.
u64 reference; ///< Point in time used as reference.
} TickCounter;
/// OS_VersionBin. Format of the system version: "<major>.<minor>.<build>-<nupver><region>"
typedef struct {
u8 build;
u8 minor;
u8 mainver; //"major" in CVER, NUP version in NVer.
u8 reserved_x3;
char region; //"ASCII character for the system version region"
u8 reserved_x5[0x3];
} OS_VersionBin;
/**
* @brief Converts an address from virtual (process) memory to physical memory.
* @param vaddr Input virtual address.
* @return The corresponding physical address.
* It is sometimes required by services or when using the GPU command buffer.
*/
u32 osConvertVirtToPhys(const void* vaddr);
/**
* @brief Converts 0x14* vmem to 0x30*.
* @param vaddr Input virtual address.
* @return The corresponding address in the 0x30* range, the input address if it's already within the new vmem, or 0 if
* it's outside of both ranges.
*/
void* osConvertOldLINEARMemToNew(const void* vaddr);
/**
* @brief Retrieves basic information about a service error.
* @param error Error to retrieve information about.
* @return A string containing a summary of an error.
*
* This can be used to get some details about an error returned by a service call.
*/
const char* osStrError(u32 error);
/**
* @brief Gets the system's FIRM version.
* @return The system's FIRM version.
*
* This can be used to compare system versions easily with @ref SYSTEM_VERSION.
*/
static inline u32 osGetFirmVersion(void) {
return (*(vu32*)0x1FF80060) & ~0xFF;
}
/**
* @brief Gets the system's kernel version.
* @return The system's kernel version.
*
* This can be used to compare system versions easily with @ref SYSTEM_VERSION.
*
* @code
* if(osGetKernelVersion() > SYSTEM_VERSION(2,46,0)) printf("You are running 9.0 or higher\n");
* @endcode
*/
static inline u32 osGetKernelVersion(void) {
return (*(vu32*)0x1FF80000) & ~0xFF;
}
/**
* @brief Gets the size of the specified memory region.
* @param region Memory region to check.
* @return The size of the memory region, in bytes.
*/
static inline u32 osGetMemRegionSize(MemRegion region) {
if (region == MEMREGION_ALL) {
return osGetMemRegionSize(MEMREGION_APPLICATION) + osGetMemRegionSize(MEMREGION_SYSTEM) +
osGetMemRegionSize(MEMREGION_BASE);
} else {
return *(vu32*)(0x1FF80040 + (region - 1) * 0x4);
}
}
/**
* @brief Gets the number of used bytes within the specified memory region.
* @param region Memory region to check.
* @return The number of used bytes of memory.
*/
s64 osGetMemRegionUsed(MemRegion region);
/**
* @brief Gets the number of free bytes within the specified memory region.
* @param region Memory region to check.
* @return The number of free bytes of memory.
*/
static inline s64 osGetMemRegionFree(MemRegion region) {
return (s64)osGetMemRegionSize(region) - osGetMemRegionUsed(region);
}
/**
* @brief Gets the current time.
* @return The number of milliseconds since 1st Jan 1900 00:00.
*/
u64 osGetTime(void);
/**
* @brief Starts a tick counter.
* @param cnt The tick counter.
*/
static inline void osTickCounterStart(TickCounter* cnt) {
cnt->reference = svcGetSystemTick();
}
/**
* @brief Updates the elapsed time in a tick counter.
* @param cnt The tick counter.
*/
static inline void osTickCounterUpdate(TickCounter* cnt) {
u64 now = svcGetSystemTick();
cnt->elapsed = now - cnt->reference;
cnt->reference = now;
}
/**
* @brief Reads the elapsed time in a tick counter.
* @param cnt The tick counter.
* @return The number of milliseconds elapsed.
*/
double osTickCounterRead(const TickCounter* cnt);
/**
* @brief Gets the current Wifi signal strength.
* @return The current Wifi signal strength.
*
* Valid values are 0-3:
* - 0 means the singal strength is terrible or the 3DS is disconnected from
* all networks.
* - 1 means the signal strength is bad.
* - 2 means the signal strength is decent.
* - 3 means the signal strength is good.
*
* Values outside the range of 0-3 should never be returned.
*
* These values correspond with the number of wifi bars displayed by Home Menu.
*/
static inline u8 osGetWifiStrength(void) {
return *(vu8*)0x1FF81066;
}
/**
* @brief Gets the state of the 3D slider.
* @return The state of the 3D slider (0.0~1.0)
*/
static inline float osGet3DSliderState(void) {
return *(volatile float*)0x1FF81080;
}
/**
* @brief Configures the New 3DS speedup.
* @param enable Specifies whether to enable or disable the speedup.
*/
void osSetSpeedupEnable(bool enable);
/**
* @brief Gets the NAND system-version stored in NVer/CVer.
* @param nver_versionbin Output OS_VersionBin structure for the data read from NVer.
* @param cver_versionbin Output OS_VersionBin structure for the data read from CVer.
* @return The result-code. This value can be positive if opening "romfs:/version.bin" fails with stdio, since errno
* would be returned in that case. In some cases the error can be special negative values as well.
*/
Result osGetSystemVersionData(OS_VersionBin* nver_versionbin, OS_VersionBin* cver_versionbin);
/**
* @brief This is a wrapper for osGetSystemVersionData.
* @param nver_versionbin Optional output OS_VersionBin structure for the data read from NVer, can be NULL.
* @param cver_versionbin Optional output OS_VersionBin structure for the data read from CVer, can be NULL.
* @param sysverstr Output string where the printed system-version will be written, in the same format displayed by the
* System Settings title.
* @param sysverstr_maxsize Max size of the above string buffer, *including* NULL-terminator.
* @return See osGetSystemVersionData.
*/
Result osGetSystemVersionDataString(OS_VersionBin* nver_versionbin, OS_VersionBin* cver_versionbin, char* sysverstr,
u32 sysverstr_maxsize);

187
include/3ds/result.h Normal file
View File

@@ -0,0 +1,187 @@
/**
* @file result.h
* @brief 3DS result code tools
*/
#pragma once
#include "types.h"
/// Checks whether a result code indicates success.
#define R_SUCCEEDED(res) ((res) >= 0)
/// Checks whether a result code indicates failure.
#define R_FAILED(res) ((res) < 0)
/// Returns the level of a result code.
#define R_LEVEL(res) (((res) >> 27) & 0x1F)
/// Returns the summary of a result code.
#define R_SUMMARY(res) (((res) >> 21) & 0x3F)
/// Returns the module ID of a result code.
#define R_MODULE(res) (((res) >> 10) & 0xFF)
/// Returns the description of a result code.
#define R_DESCRIPTION(res) ((res)&0x3FF)
/// Builds a result code from its constituent components.
#define MAKERESULT(level, summary, module, description) \
((((level)&0x1F) << 27) | (((summary)&0x3F) << 21) | (((module)&0xFF) << 10) | ((description)&0x3FF))
/// Result code level values.
enum {
// >= 0
RL_SUCCESS = 0,
RL_INFO = 1,
// < 0
RL_FATAL = 0x1F,
RL_RESET = RL_FATAL - 1,
RL_REINITIALIZE = RL_FATAL - 2,
RL_USAGE = RL_FATAL - 3,
RL_PERMANENT = RL_FATAL - 4,
RL_TEMPORARY = RL_FATAL - 5,
RL_STATUS = RL_FATAL - 6,
};
/// Result code summary values.
enum {
RS_SUCCESS = 0,
RS_NOP = 1,
RS_WOULDBLOCK = 2,
RS_OUTOFRESOURCE = 3,
RS_NOTFOUND = 4,
RS_INVALIDSTATE = 5,
RS_NOTSUPPORTED = 6,
RS_INVALIDARG = 7,
RS_WRONGARG = 8,
RS_CANCELED = 9,
RS_STATUSCHANGED = 10,
RS_INTERNAL = 11,
RS_INVALIDRESVAL = 63,
};
/// Result code module values.
enum {
RM_COMMON = 0,
RM_KERNEL = 1,
RM_UTIL = 2,
RM_FILE_SERVER = 3,
RM_LOADER_SERVER = 4,
RM_TCB = 5,
RM_OS = 6,
RM_DBG = 7,
RM_DMNT = 8,
RM_PDN = 9,
RM_GSP = 10,
RM_I2C = 11,
RM_GPIO = 12,
RM_DD = 13,
RM_CODEC = 14,
RM_SPI = 15,
RM_PXI = 16,
RM_FS = 17,
RM_DI = 18,
RM_HID = 19,
RM_CAM = 20,
RM_PI = 21,
RM_PM = 22,
RM_PM_LOW = 23,
RM_FSI = 24,
RM_SRV = 25,
RM_NDM = 26,
RM_NWM = 27,
RM_SOC = 28,
RM_LDR = 29,
RM_ACC = 30,
RM_ROMFS = 31,
RM_AM = 32,
RM_HIO = 33,
RM_UPDATER = 34,
RM_MIC = 35,
RM_FND = 36,
RM_MP = 37,
RM_MPWL = 38,
RM_AC = 39,
RM_HTTP = 40,
RM_DSP = 41,
RM_SND = 42,
RM_DLP = 43,
RM_HIO_LOW = 44,
RM_CSND = 45,
RM_SSL = 46,
RM_AM_LOW = 47,
RM_NEX = 48,
RM_FRIENDS = 49,
RM_RDT = 50,
RM_APPLET = 51,
RM_NIM = 52,
RM_PTM = 53,
RM_MIDI = 54,
RM_MC = 55,
RM_SWC = 56,
RM_FATFS = 57,
RM_NGC = 58,
RM_CARD = 59,
RM_CARDNOR = 60,
RM_SDMC = 61,
RM_BOSS = 62,
RM_DBM = 63,
RM_CONFIG = 64,
RM_PS = 65,
RM_CEC = 66,
RM_IR = 67,
RM_UDS = 68,
RM_PL = 69,
RM_CUP = 70,
RM_GYROSCOPE = 71,
RM_MCU = 72,
RM_NS = 73,
RM_NEWS = 74,
RM_RO = 75,
RM_GD = 76,
RM_CARD_SPI = 77,
RM_EC = 78,
RM_WEB_BROWSER = 79,
RM_TEST = 80,
RM_ENC = 81,
RM_PIA = 82,
RM_ACT = 83,
RM_VCTL = 84,
RM_OLV = 85,
RM_NEIA = 86,
RM_NPNS = 87,
RM_AVD = 90,
RM_L2B = 91,
RM_MVD = 92,
RM_NFC = 93,
RM_UART = 94,
RM_SPM = 95,
RM_QTM = 96,
RM_NFP = 97,
RM_APPLICATION = 254,
RM_INVALIDRESVAL = 255,
};
/// Result code generic description values.
enum {
RD_SUCCESS = 0,
RD_INVALID_RESULT_VALUE = 0x3FF,
RD_TIMEOUT = RD_INVALID_RESULT_VALUE - 1,
RD_OUT_OF_RANGE = RD_INVALID_RESULT_VALUE - 2,
RD_ALREADY_EXISTS = RD_INVALID_RESULT_VALUE - 3,
RD_CANCEL_REQUESTED = RD_INVALID_RESULT_VALUE - 4,
RD_NOT_FOUND = RD_INVALID_RESULT_VALUE - 5,
RD_ALREADY_INITIALIZED = RD_INVALID_RESULT_VALUE - 6,
RD_NOT_INITIALIZED = RD_INVALID_RESULT_VALUE - 7,
RD_INVALID_HANDLE = RD_INVALID_RESULT_VALUE - 8,
RD_INVALID_POINTER = RD_INVALID_RESULT_VALUE - 9,
RD_INVALID_ADDRESS = RD_INVALID_RESULT_VALUE - 10,
RD_NOT_IMPLEMENTED = RD_INVALID_RESULT_VALUE - 11,
RD_OUT_OF_MEMORY = RD_INVALID_RESULT_VALUE - 12,
RD_MISALIGNED_SIZE = RD_INVALID_RESULT_VALUE - 13,
RD_MISALIGNED_ADDRESS = RD_INVALID_RESULT_VALUE - 14,
RD_BUSY = RD_INVALID_RESULT_VALUE - 15,
RD_NO_DATA = RD_INVALID_RESULT_VALUE - 16,
RD_INVALID_COMBINATION = RD_INVALID_RESULT_VALUE - 17,
RD_INVALID_ENUM_VALUE = RD_INVALID_RESULT_VALUE - 18,
RD_INVALID_SIZE = RD_INVALID_RESULT_VALUE - 19,
RD_ALREADY_DONE = RD_INVALID_RESULT_VALUE - 20,
RD_NOT_AUTHORIZED = RD_INVALID_RESULT_VALUE - 21,
RD_TOO_LARGE = RD_INVALID_RESULT_VALUE - 22,
RD_INVALID_SELECTION = RD_INVALID_RESULT_VALUE - 23,
};

View File

@@ -0,0 +1,73 @@
/**
* @file irrst.h
* @brief IRRST service.
* Edited from
* https://github.com/devkitPro/libctru/blob/b18f04d88739283f6ffb55fe5ea77c73796cf61b/libctru/include/3ds/services/irrst.h
*/
#pragma once
#include "3ds/types.h"
// See also: http://3dbrew.org/wiki/IR_Services http://3dbrew.org/wiki/IRRST_Shared_Memory
/// Circle Pad position.
typedef struct {
s16 dx; ///< Pad X
s16 dy; ///< Pad Y
} circlePosition;
/// IRRST's shared memory handle.
extern Handle irrstMemHandle;
/// IRRST's shared memory.
extern vu32* irrstSharedMem;
/// IRRST's state update event
extern Handle irrstEvent;
/// Initializes IRRST.
Result irrstInit(void);
/// Exits IRRST.
void irrstExit(void);
/// Scans IRRST for input.
void irrstScanInput(void);
/**
* @brief Gets IRRST's held keys.
* @return IRRST's held keys.
*/
u32 irrstKeysHeld(void);
/**
* @brief Reads the current c-stick position.
* @param pos Pointer to output the current c-stick position to.
*/
void irrstCstickRead(circlePosition* pos);
/**
* @brief Waits for the IRRST input event to trigger.
* @param nextEvent Whether to discard the current event and wait until the next event.
*/
void irrstWaitForEvent(bool nextEvent);
/// Macro for irrstCstickRead.
#define hidCstickRead irrstCstickRead
/**
* @brief Gets the shared memory and event handles for IRRST.
* @param outMemHandle Pointer to write the shared memory handle to.
* @param outEventHandle Pointer to write the event handle to.
*/
Result IRRST_GetHandles(Handle* outMemHandle, Handle* outEventHandle);
/**
* @brief Initializes IRRST.
* @param unk1 Unknown.
* @param unk2 Unknown.
*/
Result IRRST_Initialize(u32 unk1, u8 unk2);
/// Shuts down IRRST.
Result IRRST_Shutdown(void);

View File

@@ -0,0 +1,45 @@
/**
* @file srvpm.h
* @brief srv:pm service.
*/
#pragma once
#include "../result.h"
/// Initializes srv:pm and the service API.
Result srvPmInit(void);
/// Exits srv:pm and the service API.
void srvPmExit(void);
/**
* @brief Gets the current srv:pm session handle.
* @return The current srv:pm session handle.
*/
Handle* srvPmGetSessionHandle(void);
/**
* @brief Publishes a notification to a process.
* @param notificationId ID of the notification.
* @param process Process to publish to.
*/
Result SRVPM_PublishToProcess(u32 notificationId, Handle process);
/**
* @brief Publishes a notification to all processes.
* @param notificationId ID of the notification.
*/
Result SRVPM_PublishToAll(u32 notificationId);
/**
* @brief Registers a process with SRV.
* @param pid ID of the process.
* @param count Number of services within the service access control data.
* @param serviceAccessControlList Service Access Control list.
*/
Result SRVPM_RegisterProcess(u32 pid, u32 count, const char (*serviceAccessControlList)[8]);
/**
* @brief Unregisters a process with SRV.
* @param pid ID of the process.
*/
Result SRVPM_UnregisterProcess(u32 pid);

147
include/3ds/srv.h Normal file
View File

@@ -0,0 +1,147 @@
/**
* @file srv.h
* @brief Service API.
*/
#pragma once
#include "3ds/types.h"
/// Initializes the service API.
Result srvInit(void);
/// Exits the service API.
void srvExit(void);
/**
* @brief Makes srvGetServiceHandle non-blocking for the current thread (or blocking, the default), in case of
* unavailable (full) requested services.
* @param blocking Whether srvGetServiceHandle should be non-blocking.
* srvGetServiceHandle will always block if the service hasn't been registered yet,
* use srvIsServiceRegistered to check whether that is the case or not.
*/
// void srvSetBlockingPolicy(bool nonBlocking);
/**
* @brief Gets the current service API session handle.
* @return The current service API session handle.
*/
Handle* srvGetSessionHandle(void);
/**
* @brief Retrieves a service handle, retrieving from the environment handle list if possible.
* @param out Pointer to write the handle to.
* @param name Name of the service.
* @return 0 if no error occured,
* 0xD8E06406 if the caller has no right to access the service,
* 0xD0401834 if the requested service port is full and srvGetServiceHandle is non-blocking (see @ref
* srvSetBlockingPolicy).
*/
Result srvGetServiceHandle(Handle* out, const char* name);
/// Registers the current process as a client to the service API.
Result srvRegisterClient(void);
/**
* @brief Enables service notificatios, returning a notification semaphore.
* @param semaphoreOut Pointer to output the notification semaphore to.
*/
Result srvEnableNotification(Handle* semaphoreOut);
/**
* @brief Registers the current process as a service.
* @param out Pointer to write the service handle to.
* @param name Name of the service.
* @param maxSessions Maximum number of sessions the service can handle.
*/
Result srvRegisterService(Handle* out, const char* name, int maxSessions);
/**
* @brief Unregisters the current process as a service.
* @param name Name of the service.
*/
Result srvUnregisterService(const char* name);
/**
* @brief Retrieves a service handle.
* @param out Pointer to output the handle to.
* @param name Name of the service.
* * @return 0 if no error occured,
* 0xD8E06406 if the caller has no right to access the service,
* 0xD0401834 if the requested service port is full and srvGetServiceHandle is non-blocking (see @ref
* srvSetBlockingPolicy).
*/
Result srvGetServiceHandleDirect(Handle* out, const char* name);
/**
* @brief Registers a port.
* @param name Name of the port.
* @param clientHandle Client handle of the port.
*/
Result srvRegisterPort(const char* name, Handle clientHandle);
/**
* @brief Unregisters a port.
* @param name Name of the port.
*/
Result srvUnregisterPort(const char* name);
/**
* @brief Retrieves a port handle.
* @param out Pointer to output the handle to.
* @param name Name of the port.
*/
Result srvGetPort(Handle* out, const char* name);
/**
* @brief Waits for a port to be registered.
* @param name Name of the port to wait for registration.
*/
Result srvWaitForPortRegistered(const char* name);
/**
* @brief Subscribes to a notification.
* @param notificationId ID of the notification.
*/
Result srvSubscribe(u32 notificationId);
/**
* @brief Unsubscribes from a notification.
* @param notificationId ID of the notification.
*/
Result srvUnsubscribe(u32 notificationId);
/**
* @brief Receives a notification.
* @param notificationIdOut Pointer to output the ID of the received notification to.
*/
Result srvReceiveNotification(u32* notificationIdOut);
/**
* @brief Publishes a notification to subscribers.
* @param notificationId ID of the notification.
* @param flags Flags to publish with. (bit 0 = only fire if not fired, bit 1 = do not report an error if there are more
* than 16 pending notifications)
*/
Result srvPublishToSubscriber(u32 notificationId, u32 flags);
/**
* @brief Publishes a notification to subscribers and retrieves a list of all processes that were notified.
* @param processIdCountOut Pointer to output the number of process IDs to.
* @param processIdsOut Pointer to output the process IDs to. Should have size "60 * sizeof(u32)".
* @param notificationId ID of the notification.
*/
Result srvPublishAndGetSubscriber(u32* processIdCountOut, u32* processIdsOut, u32 notificationId);
/**
* @brief Checks whether a service is registered.
* @param registeredOut Pointer to output the registration status to.
* @param name Name of the service to check.
*/
Result srvIsServiceRegistered(bool* registeredOut, const char* name);
/**
* @brief Checks whether a port is registered.
* @param registeredOut Pointer to output the registration status to.
* @param name Name of the port to check.
*/
Result srvIsPortRegistered(bool* registeredOut, const char* name);

1212
include/3ds/svc.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,233 @@
/**
* @file synchronization.h
* @brief Provides synchronization locks.
*/
#pragma once
#include "3ds/sys/lock.h"
#include "svc.h"
/// A light lock.
typedef _LOCK_T LightLock;
/// A recursive lock.
typedef _LOCK_RECURSIVE_T RecursiveLock;
/// A light event.
typedef struct {
s32 state; ///< State of the event: -2=cleared sticky, -1=cleared oneshot, 0=signaled oneshot, 1=signaled sticky
LightLock lock; ///< Lock used for sticky timer operation
} LightEvent;
/// A light semaphore.
typedef struct {
s32 current_count; ///< The current release count of the semaphore
s16 num_threads_acq; ///< Number of threads concurrently acquiring the semaphore
s16 max_count; ///< The maximum release count of the semaphore
} LightSemaphore;
/// Performs a Data Synchronization Barrier operation.
static inline void __dsb(void) {
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 4" ::[val] "r"(0) : "memory");
}
/// Performs a clrex operation.
static inline void __clrex(void) {
__asm__ __volatile__("clrex" ::: "memory");
}
/**
* @brief Performs a ldrex operation.
* @param addr Address to perform the operation on.
* @return The resulting value.
*/
static inline s32 __ldrex(s32* addr) {
s32 val;
__asm__ __volatile__("ldrex %[val], %[addr]" : [val] "=r"(val) : [addr] "Q"(*addr));
return val;
}
/**
* @brief Performs a strex operation.
* @param addr Address to perform the operation on.
* @param val Value to store.
* @return Whether the operation was successful.
*/
static inline bool __strex(s32* addr, s32 val) {
bool res;
__asm__ __volatile__("strex %[res], %[val], %[addr]" : [res] "=&r"(res) : [val] "r"(val), [addr] "Q"(*addr));
return res;
}
/**
* @brief Performs a ldrexh operation.
* @param addr Address to perform the operation on.
* @return The resulting value.
*/
static inline u16 __ldrexh(u16* addr) {
u16 val;
__asm__ __volatile__("ldrexh %[val], %[addr]" : [val] "=r"(val) : [addr] "Q"(*addr));
return val;
}
/**
* @brief Performs a strexh operation.
* @param addr Address to perform the operation on.
* @param val Value to store.
* @return Whether the operation was successful.
*/
static inline bool __strexh(u16* addr, u16 val) {
bool res;
__asm__ __volatile__("strexh %[res], %[val], %[addr]" : [res] "=&r"(res) : [val] "r"(val), [addr] "Q"(*addr));
return res;
}
/**
* @brief Performs a ldrexb operation.
* @param addr Address to perform the operation on.
* @return The resulting value.
*/
static inline u8 __ldrexb(u8* addr) {
u8 val;
__asm__ __volatile__("ldrexb %[val], %[addr]" : [val] "=r"(val) : [addr] "Q"(*addr));
return val;
}
/**
* @brief Performs a strexb operation.
* @param addr Address to perform the operation on.
* @param val Value to store.
* @return Whether the operation was successful.
*/
static inline bool __strexb(u8* addr, u8 val) {
bool res;
__asm__ __volatile__("strexb %[res], %[val], %[addr]" : [res] "=&r"(res) : [val] "r"(val), [addr] "Q"(*addr));
return res;
}
/// Performs an atomic pre-increment operation.
#define AtomicIncrement(ptr) __atomic_add_fetch((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
/// Performs an atomic pre-decrement operation.
#define AtomicDecrement(ptr) __atomic_sub_fetch((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
/// Performs an atomic post-increment operation.
#define AtomicPostIncrement(ptr) __atomic_fetch_add((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
/// Performs an atomic post-decrement operation.
#define AtomicPostDecrement(ptr) __atomic_fetch_sub((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
/// Performs an atomic swap operation.
#define AtomicSwap(ptr, value) __atomic_exchange_n((u32*)(ptr), (value), __ATOMIC_SEQ_CST)
/**
* @brief Retrieves the synchronization subsystem's address arbiter handle.
* @return The synchronization subsystem's address arbiter handle.
*/
Handle __sync_get_arbiter(void);
/**
* @brief Initializes a light lock.
* @param lock Pointer to the lock.
*/
void LightLock_Init(LightLock* lock);
/**
* @brief Locks a light lock.
* @param lock Pointer to the lock.
*/
void LightLock_Lock(LightLock* lock);
/**
* @brief Attempts to lock a light lock.
* @param lock Pointer to the lock.
* @return Zero on success, non-zero on failure.
*/
int LightLock_TryLock(LightLock* lock);
/**
* @brief Unlocks a light lock.
* @param lock Pointer to the lock.
*/
void LightLock_Unlock(LightLock* lock);
/**
* @brief Initializes a recursive lock.
* @param lock Pointer to the lock.
*/
void RecursiveLock_Init(RecursiveLock* lock);
/**
* @brief Locks a recursive lock.
* @param lock Pointer to the lock.
*/
void RecursiveLock_Lock(RecursiveLock* lock);
/**
* @brief Attempts to lock a recursive lock.
* @param lock Pointer to the lock.
* @return Zero on success, non-zero on failure.
*/
int RecursiveLock_TryLock(RecursiveLock* lock);
/**
* @brief Unlocks a recursive lock.
* @param lock Pointer to the lock.
*/
void RecursiveLock_Unlock(RecursiveLock* lock);
/**
* @brief Initializes a light event.
* @param event Pointer to the event.
* @param reset_type Type of reset the event uses (RESET_ONESHOT/RESET_STICKY).
*/
void LightEvent_Init(LightEvent* event, ResetType reset_type);
/**
* @brief Clears a light event.
* @param event Pointer to the event.
*/
void LightEvent_Clear(LightEvent* event);
/**
* @brief Wakes up threads waiting on a sticky light event without signaling it. If the event had been signaled before,
* it is cleared instead.
* @param event Pointer to the event.
*/
void LightEvent_Pulse(LightEvent* event);
/**
* @brief Signals a light event, waking up threads waiting on it.
* @param event Pointer to the event.
*/
void LightEvent_Signal(LightEvent* event);
/**
* @brief Attempts to wait on a light event.
* @param event Pointer to the event.
* @return Non-zero if the event was signaled, zero otherwise.
*/
int LightEvent_TryWait(LightEvent* event);
/**
* @brief Waits on a light event.
* @param event Pointer to the event.
*/
void LightEvent_Wait(LightEvent* event);
/**
* @brief Initializes a light semaphore.
* @param event Pointer to the semaphore.
* @param max_count Initial count of the semaphore.
* @param max_count Maximum count of the semaphore.
*/
void LightSemaphore_Init(LightSemaphore* semaphore, s16 initial_count, s16 max_count);
/**
* @brief Acquires a light semaphore.
* @param semaphore Pointer to the semaphore.
* @param count Acquire count
*/
void LightSemaphore_Acquire(LightSemaphore* semaphore, s32 count);
/**
* @brief Releases a light semaphore.
* @param semaphore Pointer to the semaphore.
* @param count Release count
*/
void LightSemaphore_Release(LightSemaphore* semaphore, s32 count);

54
include/3ds/sys/lock.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef __SYS_LOCK_H__
#define __SYS_LOCK_H__
// #include <_ansi.h>
#include <stdint.h>
typedef int32_t _LOCK_T;
struct __lock_t {
_LOCK_T lock;
uint32_t thread_tag;
uint32_t counter;
};
typedef struct __lock_t _LOCK_RECURSIVE_T;
extern void __libc_lock_init(_LOCK_T* lock);
extern void __libc_lock_init_recursive(_LOCK_RECURSIVE_T* lock);
extern void __libc_lock_close(_LOCK_T* lock);
extern void __libc_lock_close_recursive(_LOCK_RECURSIVE_T* lock);
extern void __libc_lock_acquire(_LOCK_T* lock);
extern void __libc_lock_acquire_recursive(_LOCK_RECURSIVE_T* lock);
extern void __libc_lock_release(_LOCK_T* lock);
extern void __libc_lock_release_recursive(_LOCK_RECURSIVE_T* lock);
/* Returns 0 for success and non-zero for failure */
extern int __libc_lock_try_acquire(_LOCK_T* lock);
extern int __libc_lock_try_acquire_recursive(_LOCK_RECURSIVE_T* lock);
#define __LOCK_INIT(CLASS, NAME) CLASS _LOCK_T NAME = 1;
#define __LOCK_INIT_RECURSIVE(CLASS, NAME) CLASS _LOCK_RECURSIVE_T NAME = { 1, 0, 0 };
#define __lock_init(NAME) __libc_lock_init(&(NAME))
#define __lock_init_recursive(NAME) __libc_lock_init_recursive(&(NAME))
#define __lock_close(NAME) __libc_lock_close(&(NAME))
#define __lock_close_recursive(NAME) __libc_lock_close_recursive(&(NAME))
#define __lock_acquire(NAME) __libc_lock_acquire(&(NAME))
#define __lock_acquire_recursive(NAME) __libc_lock_acquire_recursive(&(NAME))
#define __lock_try_acquire(NAME) __libc_lock_try_acquire(&(NAME))
#define __lock_try_acquire_recursive(NAME) __libc_lock_try_acquire_recursive(&(NAME))
#define __lock_release(NAME) __libc_lock_release(&(NAME))
#define __lock_release_recursive(NAME) __libc_lock_release_recursive(&(NAME))
#endif // __SYS_LOCK_H__

81
include/3ds/types.h Normal file
View File

@@ -0,0 +1,81 @@
/**
* @file types.h
* @brief Various system types.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
/// The maximum value of a u64.
#define U64_MAX UINT64_MAX
/// would be nice if newlib had this already
#ifndef SSIZE_MAX
#ifdef SIZE_MAX
#define SSIZE_MAX ((SIZE_MAX) >> 1)
#endif
#endif
typedef uint8_t u8; ///< 8-bit unsigned integer
typedef uint16_t u16; ///< 16-bit unsigned integer
typedef uint32_t u32; ///< 32-bit unsigned integer
typedef uint64_t u64; ///< 64-bit unsigned integer
typedef int8_t s8; ///< 8-bit signed integer
typedef int16_t s16; ///< 16-bit signed integer
typedef int32_t s32; ///< 32-bit signed integer
typedef int64_t s64; ///< 64-bit signed integer
typedef volatile u8 vu8; ///< 8-bit volatile unsigned integer.
typedef volatile u16 vu16; ///< 16-bit volatile unsigned integer.
typedef volatile u32 vu32; ///< 32-bit volatile unsigned integer.
typedef volatile u64 vu64; ///< 64-bit volatile unsigned integer.
typedef volatile s8 vs8; ///< 8-bit volatile signed integer.
typedef volatile s16 vs16; ///< 16-bit volatile signed integer.
typedef volatile s32 vs32; ///< 32-bit volatile signed integer.
typedef volatile s64 vs64; ///< 64-bit volatile signed integer.
typedef u32 Handle; ///< Resource handle.
typedef s32 Result; ///< Function result.
typedef void (*ThreadFunc)(void*); ///< Thread entrypoint function.
typedef void (*voidfn)(void);
/// Creates a bitmask from a bit number.
#define BIT(n) (1U << (n))
/// Aligns a struct (and other types?) to m, making sure that the size of the struct is a multiple of m.
#define ALIGN(m) __attribute__((aligned(m)))
/// Packs a struct (and other types?) so it won't include padding bytes.
#define PACKED __attribute__((packed))
#ifndef LIBCTRU_NO_DEPRECATION
/// Flags a function as deprecated.
#define DEPRECATED __attribute__((deprecated))
#else
/// Flags a function as deprecated.
#define DEPRECATED
#endif
/// Structure representing CPU registers
typedef struct {
u32 r[13]; ///< r0-r12.
u32 sp; ///< sp.
u32 lr; ///< lr.
u32 pc; ///< pc. May need to be adjusted.
u32 cpsr; ///< cpsr.
} CpuRegisters;
/// Structure representing FPU registers
typedef struct {
union {
struct PACKED {
double d[16];
}; ///< d0-d15.
float s[32]; ///< s0-s31.
};
u32 fpscr; ///< fpscr.
u32 fpexc; ///< fpexc.
} FpuRegisters;

155
include/3ds/util/utf.h Normal file
View File

@@ -0,0 +1,155 @@
/**
* @file utf.h
* @brief UTF conversion functions.
*/
#pragma once
#include <stdint.h>
#include <sys/types.h>
/** Convert a UTF-8 sequence into a UTF-32 codepoint
*
* @param[out] out Output codepoint
* @param[in] in Input sequence
*
* @returns number of input code units consumed
* @returns -1 for error
*/
ssize_t decode_utf8(uint32_t* out, const uint8_t* in);
/** Convert a UTF-16 sequence into a UTF-32 codepoint
*
* @param[out] out Output codepoint
* @param[in] in Input sequence
*
* @returns number of input code units consumed
* @returns -1 for error
*/
ssize_t decode_utf16(uint32_t* out, const uint16_t* in);
/** Convert a UTF-32 codepoint into a UTF-8 sequence
*
* @param[out] out Output sequence
* @param[in] in Input codepoint
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out must be able to store 4 code units
*/
ssize_t encode_utf8(uint8_t* out, uint32_t in);
/** Convert a UTF-32 codepoint into a UTF-16 sequence
*
* @param[out] out Output sequence
* @param[in] in Input codepoint
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out must be able to store 2 code units
*/
ssize_t encode_utf16(uint16_t* out, uint32_t in);
/** Convert a UTF-8 sequence into a UTF-16 sequence
*
* Fills the output buffer up to \a len code units.
* Returns the number of code units that the input would produce;
* if it returns greater than \a len, the output has been
* truncated.
*
* @param[out] out Output sequence
* @param[in] in Input sequence (null-terminated)
* @param[in] len Output length
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out is not null-terminated
*/
ssize_t utf8_to_utf16(uint16_t* out, const uint8_t* in, size_t len);
/** Convert a UTF-8 sequence into a UTF-32 sequence
*
* Fills the output buffer up to \a len code units.
* Returns the number of code units that the input would produce;
* if it returns greater than \a len, the output has been
* truncated.
*
* @param[out] out Output sequence
* @param[in] in Input sequence (null-terminated)
* @param[in] len Output length
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out is not null-terminated
*/
ssize_t utf8_to_utf32(uint32_t* out, const uint8_t* in, size_t len);
/** Convert a UTF-16 sequence into a UTF-8 sequence
*
* Fills the output buffer up to \a len code units.
* Returns the number of code units that the input would produce;
* if it returns greater than \a len, the output has been
* truncated.
*
* @param[out] out Output sequence
* @param[in] in Input sequence (null-terminated)
* @param[in] len Output length
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out is not null-terminated
*/
ssize_t utf16_to_utf8(uint8_t* out, const uint16_t* in, size_t len);
/** Convert a UTF-16 sequence into a UTF-32 sequence
*
* Fills the output buffer up to \a len code units.
* Returns the number of code units that the input would produce;
* if it returns greater than \a len, the output has been
* truncated.
*
* @param[out] out Output sequence
* @param[in] in Input sequence (null-terminated)
* @param[in] len Output length
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out is not null-terminated
*/
ssize_t utf16_to_utf32(uint32_t* out, const uint16_t* in, size_t len);
/** Convert a UTF-32 sequence into a UTF-8 sequence
*
* Fills the output buffer up to \a len code units.
* Returns the number of code units that the input would produce;
* if it returns greater than \a len, the output has been
* truncated.
*
* @param[out] out Output sequence
* @param[in] in Input sequence (null-terminated)
* @param[in] len Output length
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out is not null-terminated
*/
ssize_t utf32_to_utf8(uint8_t* out, const uint32_t* in, size_t len);
/** Convert a UTF-32 sequence into a UTF-16 sequence
*
* @param[out] out Output sequence
* @param[in] in Input sequence (null-terminated)
* @param[in] len Output length
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out is not null-terminated
*/
ssize_t utf32_to_utf16(uint16_t* out, const uint32_t* in, size_t len);

24
include/common.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef _COMMON_H_
#define _COMMON_H_
#include "../include/z3D/z3D.h"
#include <stdarg.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define BIT_COUNT(x) (sizeof(x) * 8)
#define TICKS_PER_SEC 268123480
#define SEQ_AUDIO_BLANK 0x1000142
/// Returns 1 if the bit is set in value1 but not in value2, -1 if vice versa, and 0 if they're the same
s8 BitCompare(u32 value1, u32 value2, u8 bit);
u32 Hash(u32);
u8 Bias(u32);
u8 IsInGame(void);
u8 IsInGameOrBossChallenge(void);
void CitraPrint(const char*, ...);
#endif //_COMMON_H_

145
include/csvc.h Normal file
View File

@@ -0,0 +1,145 @@
/* 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 process Handle of the process.
* @param destAddress Address of the mapped block in the current process.
* @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 process, u32 destAddr, u32 srcAddr, u32 size);
/**
* @brief Unmaps a block of process memory.
* @param process Handle of the process.
* @param destAddress Address of the block of memory to unmap, in the current (destination) process.
* @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes).
*/
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
/**
* @brief Queries memory information.
* @param[out] info Pointer to output memory info to.
* @param out Pointer to output page info to.
* @param addr Virtual memory address to query.
*/
Result svcQueryMemory(MemInfo* info, PageInfo* out, u32 addr);
/**
* @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);
///@}

119
include/hid.h Normal file
View File

@@ -0,0 +1,119 @@
/*
* From n3rdswithgame, who may or may not have originally written this
*/
#ifndef HID_H
#define HID_H
#include <stdint.h>
typedef union {
uint32_t val;
struct {
uint32_t a : 1; // 1
uint32_t b : 1; // 2
uint32_t sel : 1; // 3
uint32_t strt : 1; // 4
uint32_t d_right : 1; // 5
uint32_t d_left : 1; // 6
uint32_t d_up : 1; // 7
uint32_t d_down : 1; // 8
uint32_t r : 1; // 9
uint32_t l : 1; // 10
uint32_t x : 1; // 11
uint32_t y : 1; // 12
uint32_t gpio : 2; // 14
uint32_t padding : 14; // 28
uint32_t c_right : 1; // 29
uint32_t c_left : 1; // 30
uint32_t c_up : 1; // 31
uint32_t c_down : 1; // 32
};
} btn_t;
typedef union {
uint32_t val;
struct {
int16_t x;
int16_t y;
};
} cp_t;
typedef struct {
btn_t curr;
btn_t pressed;
btn_t released;
cp_t cp;
} pad_t;
struct hid_pad_t {
uint64_t timestamp; // 0
uint64_t timestamp_last; // 8
uint32_t index; // 10
uint32_t pad_14[2]; // 14
btn_t btn_raw; // 1c
cp_t cp_raw; // 20
uint8_t pad_24; // 24
pad_t pads[8];
};
struct touch_input_t {
int16_t x;
int16_t y;
};
typedef struct {
struct touch_input_t touch;
uint32_t updated;
} touch_t;
struct hid_touch_t {
uint64_t timestamp; // 0
uint64_t timestamp_last; // 8
uint32_t index; // 10
uint32_t pad_14; // 14
touch_t raw;
touch_t touches[8];
};
typedef struct {
struct hid_pad_t pad;
struct hid_touch_t touch;
} hid_mem_t;
typedef struct {
uint32_t field_00;
struct hid_pad_t* hid_pad;
uint32_t field_08;
struct hid_touch_t* hid_touch;
uint32_t field_10;
uint32_t* hid_accl; // need to add
uint32_t field_18;
uint32_t field_1c;
uint32_t* hid_gyro; // need to add
uint32_t field_24;
uint32_t* hid_debug; // might add
uint32_t mappable_mem_chunk[4]; // need to add
uint32_t bool_3c;
uint32_t hid_handle;
uint32_t bool_44;
} hid_ctx_t;
#define BUTTON_A (1 << 0)
#define BUTTON_B (1 << 1)
#define BUTTON_SELECT (1 << 2)
#define BUTTON_START (1 << 3)
#define BUTTON_RIGHT (1 << 4)
#define BUTTON_LEFT (1 << 5)
#define BUTTON_UP (1 << 6)
#define BUTTON_DOWN (1 << 7)
#define BUTTON_R1 (1 << 8)
#define BUTTON_L1 (1 << 9)
#define BUTTON_X (1 << 10)
#define BUTTON_Y (1 << 11)
#define CPAD_RIGHT (1 << 28)
#define CPAD_LEFT (1 << 29)
#define CPAD_UP (1 << 30)
#define CPAD_DOWN (1 << 31)
#endif // HID_H

21
include/input.h Normal file
View File

@@ -0,0 +1,21 @@
#include "z3D/z3D.h"
#include "hid.h"
#include "3ds/services/irrst.h"
typedef struct {
btn_t cur;
btn_t up;
btn_t pressed;
btn_t old;
int16_t touchX;
int16_t touchY;
uint32_t touchPressed;
uint32_t touchHeld;
circlePosition cStick;
} InputContext;
void Input_Update(void);
u32 Input_WaitWithTimeout(u32 msec, u32 closingButton);
u32 Input_Wait(void);
extern InputContext rInputCtx;

108
include/lib/printf.h Normal file
View File

@@ -0,0 +1,108 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// 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.
//
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
// embedded systems with a very limited resources.
// Use this instead of bloated standard/newlib printf.
// These routines are thread safe and reentrant.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _PRINTF_H_
#define _PRINTF_H_
#include <stdarg.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Output a character to a custom device like UART, used by the printf() function
* This function is declared here only. You have to write your custom implementation somewhere
* \param character Character to output
*/
void _putchar(char character);
/**
* Tiny printf implementation
* You have to implement _putchar if you use printf()
* To avoid conflicts with the regular printf() API it is overridden by macro defines
* and internal underscore-appended functions like printf_() are used
* \param format A string that specifies the format of the output
* \return The number of characters that are written into the array, not counting the terminating null character
*/
#define printf printf_
int printf_(const char* format, ...);
/**
* Tiny sprintf implementation
* Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
* \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
* \param format A string that specifies the format of the output
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
*/
#define sprintf sprintf_
int sprintf_(char* buffer, const char* format, ...);
/**
* Tiny snprintf/vsnprintf implementation
* \param buffer A pointer to the buffer where to store the formatted string
* \param count The maximum number of characters to store in the buffer, including a terminating null character
* \param format A string that specifies the format of the output
* \param va A value identifying a variable arguments list
* \return The number of characters that COULD have been written into the buffer, not counting the terminating
* null character. A value equal or larger than count indicates truncation. Only when the returned value
* is non-negative and less than count, the string has been completely written.
*/
#define snprintf snprintf_
#define vsnprintf vsnprintf_
int snprintf_(char* buffer, size_t count, const char* format, ...);
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
/**
* Tiny vprintf implementation
* \param format A string that specifies the format of the output
* \param va A value identifying a variable arguments list
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
*/
#define vprintf vprintf_
int vprintf_(const char* format, va_list va);
/**
* printf with output function
* You may use this as dynamic alternative to printf() with its fixed _putchar() output
* \param out An output function which takes one character and an argument pointer
* \param arg An argument pointer for user data passed to output function
* \param format A string that specifies the format of the output
* \return The number of characters that are sent to the output function, not counting the terminating null character
*/
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
#ifdef __cplusplus
}
#endif
#endif // _PRINTF_H_

14
include/lib/string.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "lib/types.h"
#include <stdint.h>
size_t strlen(const char* str);
void* memset(void* ptr, int value, size_t num);
void* memcpy(void* dst, const void* src, size_t len);
size_t strnlen(const char* s, size_t max_len);
char* strncpy(char* dst, const char* src, size_t num);

3
include/lib/types.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
typedef unsigned int size_t;

7
include/loader.h Normal file
View File

@@ -0,0 +1,7 @@
#include "newcodeinfo.h"
#include "3ds/types.h"
Result svcControlProcessMemory(Handle process, u32 addr0, u32 addr1, u32 size, u32 type, u32 perm);
Handle getCurrentProcessHandle(void)
__attribute__((section (".loader")));

12
include/newcodeinfo.h Normal file
View File

@@ -0,0 +1,12 @@
/*
* This file will (eventually) be automatically generated, following
* the method used by RicBent in the program Magikoopa
*/
#ifndef _NEWCODEINFO_H_
#define _NEWCODEINFO_H_
#define NEWCODE_OFFSET 0x005C7000 //TODO: this
#define NEWCODE_SIZE 0x0002E000 //TODO: this. even now, this is too big.
#endif //_NEWCODEINFO_H_

59
include/utils.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#pragma once
#include "3ds/svc.h"
#include "3ds/result.h"
#include "csvc.h"
// For accessing physmem uncached (and directly)
#define PA_PTR(addr) (void*)((u32)(addr) | 1 << 31)
#ifndef PA_FROM_VA_PTR
#define PA_FROM_VA_PTR(addr) PA_PTR(svcConvertVAToPA((const void*)(addr), false))
#endif
#define REG32(addr) (*(vu32*)(PA_PTR(addr)))
static inline u32 makeARMBranch(const void* src, const void* dst, bool link) // the macros for those are ugly and buggy
{
u32 instrBase = link ? 0xEB000000 : 0xEA000000;
u32 off = (u32)((const u8*)dst -
((const u8*)src + 8)); // the PC is always two instructions ahead of the one being executed
return instrBase | ((off >> 2) & 0xFFFFFF);
}
static inline void* decodeARMBranch(const void* src) {
u32 instr = *(const u32*)src;
s32 off = (instr & 0xFFFFFF) << 2;
off = (off << 6) >> 6; // sign extend
return (void*)((const u8*)src + 8 + off);
}
Result OpenProcessByName(const char* name, Handle* h);

837
include/z3D/z3D.h Normal file
View File

@@ -0,0 +1,837 @@
#ifndef _Z3D_H_
#define _Z3D_H_
#include "z3Dactor.h"
#include "z3Dvec.h"
#include "z3Dcutscene.h"
#include "z3Ditem.h"
// #include "hid.h"
#define TRUE 1
#define FALSE 0
typedef struct {
/* 0x00 */ u8 buttonItems[5]; // B,Y,X,I,II
/* 0x05 */ u8 buttonSlots[4]; // Y,X,I,II
/* 0x0A */ u16 equipment;
} ItemEquips; // size = 0x0C
typedef struct {
/* 0x00 */ u32 chest;
/* 0x04 */ u32 swch;
/* 0x08 */ u32 clear;
/* 0x0C */ u32 collect;
/* 0x10 */ u32 unk;
/* 0x14 */ u32 rooms1;
/* 0x18 */ u32 rooms2;
} SaveSceneFlags; // size = 0x1C
typedef struct {
/* 0x00 */ s16 scene;
/* 0x02 */ Vec3s pos;
/* 0x08 */ s16 angle;
} HorseData; // size = 0x0A
typedef struct {
/* 0x00 */ Vec3f pos;
/* 0x0C */ s16 yaw;
/* 0x0E */ s16 playerParams;
/* 0x10 */ s16 entranceIndex;
/* 0x12 */ u8 roomIndex;
/* 0x13 */ u8 data;
/* 0x14 */ u32 tempSwchFlags;
/* 0x18 */ u32 tempCollectFlags;
} RespawnData; // size = 0x1C
typedef enum {
/* 0x00 */ RESPAWN_MODE_DOWN, /* Normal Void Outs */
/* 0x01 */ RESPAWN_MODE_RETURN, /* Grotto Returnpoints */
/* 0x02 */ RESPAWN_MODE_TOP /* Farore's Wind */
} RespawnMode;
typedef enum {
/* 0x00 */ BTN_ENABLED,
/* 0xFF */ BTN_DISABLED = 0xFF
} ButtonStatus;
// Save Context (ram start: 0x00587958)
typedef struct {
/* 0x0000 */ s32 entranceIndex;
/* 0x0004 */ s32 linkAge; // 0: Adult; 1: Child
/* 0x0008 */ s32 cutsceneIndex;
/* 0x000C */ u16 dayTime; // "zelda_time"
/* 0x000E */ u8 masterQuestFlag;
/* 0x000F */ u8 motionControlSetting;
/* 0x0010 */ s32 nightFlag;
/* 0x0014 */ s32 unk_14;
/* 0x0018 */ s32 unk_18;
/* 0x001C */ s16 playerName[0x8];
/* 0x002C */ u8 playerNameLength;
/* 0x002D */ u8 zTargetingSetting;
/* 0x002E */ s16 unk_2E;
/* 0x0030 */ char newf[6]; // string "ZELDAZ"
/* 0x0036 */ u16 saveCount;
/* 0x0038 */ char unk_38[0x000A];
/* 0x0042 */ u16 healthCapacity; // "max_life"
/* 0x0044 */ s16 health; // "now_life"
/* 0x0046 */ s8 magicLevel;
/* 0x0047 */ s8 magic;
/* 0x0048 */ s16 rupees;
/* 0x004A */ u16 bgsHitsLeft;
/* 0x004C */ u16 naviTimer;
/* 0x004E */ u8 magicAcquired;
/* 0x004F */ char unk_4F[0x0001];
/* 0x0050 */ u8 doubleMagic;
/* 0x0051 */ u8 doubleDefense;
/* 0x0052 */ s8 bgsFlag;
/* 0x0054 */ ItemEquips childEquips;
/* 0x0060 */ ItemEquips adultEquips;
/* 0x006C */ char unk_6C[0x0012];
/* 0x007E */ u16 sceneIndex;
/* 0x0080 */ ItemEquips equips;
/* 0x008C */ u8 items[26];
/* 0x00A6 */ s8 ammo[15];
/* 0x00B5 */ u8 magic_beans_available; // counts bought
/* 0x00B6 */ u16 equipment; // bits: swords 0-3, shields 4-6, tunics 8-10, boots 12-14
/* 0x00B8 */ u32 upgrades; // bits: quiver 0-2, bombs 3-5, strength 6-8, dive 9-11, wallet 12-13, seeds 14-16,
// sticks 17-19, nuts 20-22
/* 0x00BC */ u32 questItems; // bits: medallions 0-5, warp songs 6-11, songs 12-17, stones 18-20, shard 21, token
// 22, skull 23, heart pieces 24-31
/* 0x00C0 */ u8 dungeonItems[20]; // bits: boss key 0, compass 1, map 2
/* 0x00D4 */ s8 dungeonKeys[19];
/* 0x00E7 */ char unk_E7[0x0001]; // in oot: defenseHearts. seems not here.
/* 0x00E8 */ s16 gsTokens;
/* 0x00EC */ SaveSceneFlags sceneFlags[124];
struct {
/* 0x0E7C */ Vec3i pos;
/* 0x0E88 */ s32 yaw;
/* 0x0E8C */ s32 playerParams;
/* 0x0E90 */ s32 entranceIndex;
/* 0x0E94 */ s32 roomIndex;
/* 0x0E98 */ s32 set;
/* 0x0E9C */ s32 tempSwchFlags;
/* 0x0EA0 */ s32 tempCollectFlags;
} fw;
/* 0x0EA4 */ char unk_EA4[0x0010];
/* 0x0EB4 */ u8 gsFlags[22]; // due to reordering, array is smaller
/* 0x0ECA */ char unk_ECA[0x0006]; // the extra two bytes move here
/* 0x0ED0 */ u32 unk_ED0; // horseback archery highscore?
/* 0x0ED4 */ u32 bigPoePoints; // number of big poes sold * 100
struct {
/* 0x0ED4 */ u8 recordFishChild; // seems to be unique ID of fish, this is copied into adult value if player has
// not yet fished as adult
/* 0x0ED5 */ u8 flags; // bits: 0 - ever fished as child, 1 - ever fished as adult, 2 - caught record as child,
// 3 - caught record as adult
/* 0x0ED6 */ u8 timesPaidToFish;
/* 0x0ED7 */ u8 recordFishAdult; // seems to be unique ID of fish
} fishingStats;
/* 0x0EDC */ u32 unk_EDC; // horse race record time?
/* 0x0EE0 */ u32 unk_EE0; // marathon race record time?
/* 0x0EE4 */ char unk_EE4[0x0008];
/* 0x0EEC */ u16 eventChkInf[14]; // "event_chk_inf"
/* 0x0F08 */ u16 itemGetInf[4]; // "item_get_inf"
/* 0x0F10 */ u16 infTable[30]; // "inf_table"
/* 0x0F4C */ char unk_F34[0x0004];
/* 0x0F50 */ u32 worldMapAreaData; // "area_arrival"
/* 0x0F54 */ char unk_F54[0x0410]; // TODO: scarecrow's song
/* 0x1364 */ HorseData horseData;
/* 0x136E */ char unk_136E[0x0002];
/* 0x1370 */ u8 itemSlotDataRecords[26];
/* 0x138A */ u8 itemMenuChild[24];
/* 0x13A2 */ u8 itemMenuAdult[24];
/* 0x13BA */ char unk_13BA[0x0002];
struct {
/* 0x13BC */ u32 year;
/* 0x13C0 */ u32 month;
/* 0x13C4 */ u32 day;
/* 0x13C8 */ u32 hour;
/* 0x13CC */ u32 minute;
} saveTime;
/* 0x13D0 */ char unk_13D0[0x0004];
/* 0x13D4 */ u8 otherNewEventFlags; // Club Moblin cutscene and Sheikah Stone Navi message
/* 0x13D5 */ char unk_13D5[0x0003];
/* 0x13D8 */ u8 cameraControlSetting;
/* 0x13D9 */ char unk_13D9[0x0077];
/* 0x1450 */ u32 bossBattleVictories[9];
/* 0x1474 */ u32 bossBattleScores[9];
/* 0x1498 */ char unk_1498[0x0040]; // sheikah stone flags?
/* 0x14D8 */ u16 checksum; // "check_sum"
/* 0x14DC */ s32 fileNum; // "file_no"
/* 0x14E0 */ char unk_14E0[0x0004];
/* 0x14E4 */ s32 gameMode;
/* 0x14E8 */ s32 sceneSetupIndex;
/* 0x14EC */ s32 respawnFlag; // "restart_flag"
/* 0x14F0 */ RespawnData respawn[3]; // "restart_data"
/* 0x1544 */ char unk_1544[0x000E];
/* 0x1552 */ s16 nayrusLoveTimer;
/* 0x1554 */ char unk_1554[0x0008];
/* 0x155C */ s16 rupeeAccumulator;
/* 0x155E */ s16 timer1State;
/* 0x1560 */ s16 timer1Value;
/* 0x1562 */ s16 timer2State;
/* 0x1564 */ s16 timer2Value;
/* 0x1566 */ s16 timerX[2]; // changing these doesn't seem to actually move the timer?
/* 0x156A */ s16 timerY[2]; // changing these doesn't seem to actually move the timer?
/* 0x156E */ u8 nightSeqIndex;
/* 0x156F */ u8 buttonStatus[5];
/* 0x1574 */ char unk_1574[0x000B];
/* 0x1580 */ s16 magicState;
/* 0x1582 */ char unk_1582[0x0002];
/* 0x1584 */ u16 magicMeterSize;
/* 0x1586 */ char unk_1586[0x0002];
/* 0x1588 */ s16 magicTarget;
/* 0x158A */ u16 eventInf[4];
/* 0x1592 */ u16 dungeonIndex;
/* 0x1594 */ char unk_1594[0x000C];
/* 0x15A0 */ u16 nextCutsceneIndex;
/* 0x15A2 */ u8 cutsceneTrigger;
/* 0x15A3 */ char unk_15A3[0x008];
/* 0x15AB */ u8 nextTransition;
/* 0x15AC */ char unk_15AC[0x006];
/* 0x15B2 */ s16 healthAccumulator;
// stuff below is from z64.h
/* skipped over */
// /* 0x0022 */ s16 deaths;
// /* 0x002C */ s16 n64ddFlag;
// /* 0x0072 */ char unk_72[0x0002];
// /* 0x00CF */ s8 defenseHearts;
// /* 0x0F3C */ char unk_F3C[0x040C];
// /* 0x13BC */ char unk_13BC[0x0008];
// /* 0x13C4 */ s16 dogParams;
// /* 0x13C6 */ char unk_13C6[0x0001];
// /* 0x13C7 */ u8 unk_13C7;
/* still to compare */
// /* 0x13CA */ char unk_13CA[0x0002];
// /* 0x13DE */ char unk_13DE[0x0002];
// /* 0x13E0 */ u8 seqIndex;
// /* 0x13E1 */ u8 nightSeqIndex;
// /* 0x13E2 */ u8 buttonStatus[5];
// /* 0x13E7 */ u8 unk_13E7;
// /* 0x13E8 */ u16 unk_13E8; // alpha type?
// /* 0x13EA */ u16 unk_13EA; // also alpha type?
// /* 0x13EC */ u16 unk_13EC; // alpha type counter?
// /* 0x13EE */ u16 unk_13EE; // previous alpha type?
// /* 0x13F0 */ s16 unk_13F0;
// /* 0x13F2 */ s16 unk_13F2;
// /* 0x13F4 */ s16 unk_13F4;
// /* 0x13F6 */ s16 unk_13F6;
// /* 0x13F8 */ s16 unk_13F8;
// /* 0x13FA */ u16 eventInf[4]; // "event_inf"
// /* 0x1404 */ u16 minigameState;
// /* 0x1406 */ u16 minigameScore; // "yabusame_total"
// /* 0x1408 */ char unk_1408[0x0001];
// /* 0x1409 */ u8 language;
// /* 0x140A */ u8 audioSetting;
// /* 0x140B */ char unk_140B[0x0001];
// /* 0x140C */ u8 zTargetingSetting; // 0: Switch; 1: Hold
// /* 0x140E */ u16 unk_140E; // bgm related
// /* 0x1410 */ u8 unk_1410;
// /* 0x1411 */ u8 unk_1411;
// /* 0x1412 */ u16 nextCutsceneIndex;
// /* 0x1414 */ u8 cutsceneTrigger;
// /* 0x1415 */ u8 chamberCutsceneNum;
// /* 0x1416 */ u16 nextDayTime; // "next_zelda_time"
// /* 0x1418 */ u8 fadeDuration;
// /* 0x1419 */ u8 unk_1419; // transition related
// /* 0x141A */ u16 environmentTime;
// /* 0x141C */ u8 dogIsLost;
// /* 0x141D */ u8 nextTransition;
// /* 0x141E */ char unk_141E[0x0002];
// /* 0x1420 */ s16 worldMapArea;
// /* 0x1422 */ s16 unk_1422; // day time related
} SaveContext; // size = 0x15C4
typedef struct GraphicsContext GraphicsContext; // TODO
typedef struct GlobalContext GlobalContext;
typedef struct {
/* 0x000 */ char unk_000[0x080];
/* 0x080 */ Vec3f at;
/* 0x08C */ Vec3f eye;
/* 0x098 */ Vec3f up;
/* 0x0A4 */ Vec3f eyeNext;
/* 0x0B0 */ Vec3f skyboxOffset;
/* 0x0BC */ char unk_0BC[0x018];
/* 0x0D4 */ GlobalContext* globalCtx;
/* 0x0D8 */ Player* player;
/* 0x0DC */ PosRot playerPosRot;
/* 0x0F0 */ Actor* target;
/* 0x0F4 */ PosRot targetPosRot;
/* 0x108 */ f32 rUpdateRateInv;
/* 0x10C */ f32 pitchUpdateRateInv;
/* 0x110 */ f32 yawUpdateRateInv;
/* 0x114 */ f32 xzOffsetUpdateRate;
/* 0x118 */ f32 yOffsetUpdateRate;
/* 0x11C */ f32 fovUpdateRate;
/* 0x120 */ f32 xzSpeed;
/* 0x124 */ f32 dist;
/* 0x128 */ f32 speedRatio;
/* 0x12C */ Vec3f playerToAtOffset;
/* 0x138 */ Vec3f playerPosDelta;
/* 0x144 */ f32 fov;
/* 0x148 */ f32 atLERPStepScale;
/* 0x14C */ f32 playerGroundY;
/* 0x150 */ Vec3f floorNorm;
/* 0x15C */ f32 waterYPos;
/* 0x160 */ s32 waterPrevCamIdx;
/* 0x164 */ s32 waterPrevCamSetting;
/* 0x168 */ s32 waterQuakeIdx;
/* 0x16C */ char unk_16C[0x00C];
/* 0x178 */ s16 uid;
/* 0x17A */ char unk_17A[0x002];
/* 0x17C */ Vec3s inputDir;
/* 0x182 */ Vec3s camDir;
/* 0x188 */ s16 status;
/* 0x18A */ s16 setting;
/* 0x18C */ s16 mode;
/* 0x18E */ s16 bgCheckId;
/* 0x190 */ s16 camDataIdx;
/* 0x192 */ s16 behaviorFlags;
/* 0x194 */ s16 stateFlags;
/* 0x196 */ s16 childCamIdx;
/* 0x198 */ s16 waterDistortionTimer;
/* 0x19A */ s16 distortionFlags;
/* 0x19C */ s16 prevSetting;
/* 0x19E */ s16 nextCamDataIdx;
/* 0x1A0 */ s16 nextBgCheckId;
/* 0x1A2 */ s16 roll;
/* 0x1A4 */ s16 paramFlags;
/* 0x1A6 */ s16 animState;
/* 0x1A8 */ s16 timer;
/* 0x1AA */ s16 parentCamIdx;
/* 0x1AC */ s16 thisIdx;
/* 0x1AE */ s16 prevCamDataIdx;
/* 0x1B0 */ s16 csId;
/* 0x1B2 */ char unk_1B2[0x00A];
} Camera; // size = 0x1BC
typedef struct {
/* 0x0 */ u16 setting;
/* 0x2 */ s16 count;
/* 0x4 */ Vec3s* camFuncData;
} CamData; // size = 0x8
typedef struct {
/* 0x00 */ Vec3f atOffset;
/* 0x0C */ Vec3f eyeOffset;
/* 0x18 */ s16 upPitchOffset;
/* 0x1A */ s16 upYawOffset;
/* 0x1C */ s16 fovOffset;
/* 0x20 */ f32 maxOffset;
} ShakeInfo; // size = 0x24
typedef struct {
/* 0x00 */ char unk_00[0x04];
/* 0x04 */ Vec3s minBounds;
/* 0x0A */ Vec3s maxBounds;
/* 0x10 */ u16 numVertices;
/* 0x12 */ u16 numPolygons;
/* 0x14 */ u16 numWaterboxes;
/* 0x18 */ Vec3s* vtxList;
/* 0x1C */ CollisionPoly* polyList;
/* 0x20 */ void* surfaceTypeList;
/* 0x24 */ CamData* camDataList;
/* 0x28 */ void* waterboxes;
} CollisionHeader; // size = 0x2C
typedef struct {
/* 0x00 */ CollisionHeader* colHeader;
/* 0x04 */ char unk_04[0x4C];
} StaticCollisionContext; // size = 0x50
typedef struct {
/* 0x0000 */ char unk_00[0x04];
/* 0x0004 */ ActorMesh actorMeshArr[50];
/* 0x151C */ u16 flags[50];
/* 0x1580 */ char unk_13F0[0x24];
} DynaCollisionContext; // size = 0x15A4
typedef struct {
/* 0x0000 */ StaticCollisionContext stat;
/* 0x0050 */ DynaCollisionContext dyna;
} CollisionContext; // size = 0x15F4
typedef struct {
/* 0x00 */ u8* texture;
/* 0x04 */ s16 x;
/* 0x06 */ s16 y;
/* 0x08 */ s16 width;
/* 0x0A */ s16 height;
/* 0x0C */ s32 unk_0C;
/* 0x10 */ u8 durationTimer;
/* 0x11 */ u8 delayTimer;
/* 0x12 */ s16 alpha;
/* 0x14 */ s16 intensity;
/* 0x16 */ s16 unk_16;
} TitleCardContext; // size = 0x18
typedef struct {
/* 0x00 */ u32 length; // number of actors loaded of this type
/* 0x04 */ Actor* first; // pointer to first actor of this type
} ActorListEntry; // size = 0x08
typedef struct {
/* 0x00 */ u32 swch;
/* 0x04 */ u32 tempSwch;
/* 0x08 */ u32 unk0;
/* 0x0C */ u32 unk1;
/* 0x10 */ u32 chest;
/* 0x14 */ u32 clear;
/* 0x18 */ u32 tempClear;
/* 0x1C */ u32 collect;
/* 0x20 */ u32 tempCollect;
} ActorFlags; // size = 0x24
typedef struct {
/* 0x0000 */ u8 unk_00;
/* 0x0001 */ char unk_01[0x01];
/* 0x0002 */ u8 unk_02;
/* 0x0003 */ u8 unk_03;
/* 0x0004 */ char unk_04[0x04];
/* 0x0008 */ u8 total; // total number of actors loaded
/* 0x0009 */ char unk_09[0x03];
/* 0x000C */ ActorListEntry actorList[12];
// /* 0x006C */ TargetContext targetCtx;
/* 0x006C */ char unk_6C[0x130];
/* 0x019C */ ActorFlags flags;
/* 0x01C0 */ TitleCardContext titleCtx;
} ActorContext; // TODO: size = 0x1D8
typedef struct CutsceneContext {
/* 0x00 */ char unk_00[0x4];
/* 0x04 */ void* segment;
/* 0x08 */ u8 state;
/* 0x09 */ char unk_09[0x13];
/* 0x1C */ f32 unk_1C;
/* 0x20 */ u16 frames;
/* 0x22 */ u16 unk_22;
/* 0x24 */ s32 unk_24;
/* 0x28 */ char unk_28[0x18];
/* 0x40 */ CsCmdActorAction* linkAction;
/* 0x44 */ CsCmdActorAction* actorActions[10]; // "npcdemopnt"
} CutsceneContext; // size = 0x6C
typedef struct Sub_118_C {
s32 data[4];
} Sub_118_C;
typedef struct SubGlobalContext_118 {
/* 0x00 */ char unk_00[0x0C];
/* 0x0C */ Sub_118_C* sub0C; // an array of these
/* 0x10 */ char unk_10[0x24];
/* 0x34 */ s32 indexInto0C;
/* 0x38 */ char unk_38[0x28];
/* 0x60 */ void** unk_60; // seems to point to an array of cutscene pointers, maybe?
} SubGlobalContext_118; // size = at least 0x64
typedef struct OcLine OcLine; // TODO
#define COLLISION_CHECK_AT_MAX 50
#define COLLISION_CHECK_AC_MAX 60
#define COLLISION_CHECK_OC_MAX 50
#define COLLISION_CHECK_OC_LINE_MAX 3
typedef struct {
/* 0x000 */ s16 colAtCount;
/* 0x002 */ u16 sacFlags;
/* 0x004 */ Collider* colAt[COLLISION_CHECK_AT_MAX];
/* 0x0CC */ s32 colAcCount;
/* 0x0D0 */ Collider* colAc[COLLISION_CHECK_AC_MAX];
/* 0x1C0 */ s32 colOcCount;
/* 0x1C4 */ Collider* colOc[COLLISION_CHECK_OC_MAX];
/* 0x28C */ s32 colOcLineCount;
/* 0x290 */ OcLine* colOcLine[COLLISION_CHECK_OC_LINE_MAX];
} CollisionCheckContext; // size = 0x29C
// _Static_assert(sizeof(CollisionCheckContext) == 0x29C, "CollisionCheckContext size");
typedef struct {
/* 0x00 */ Vec3f pos;
/* 0x0C */ Vec3f norm;
/* 0x18 */ CollisionPoly* poly;
struct {
/* 0x1C */ f32 radius;
/* 0x20 */ s16 pitch;
/* 0x22 */ s16 yaw;
} geoNorm;
/* 0x24 */ s32 bgId;
} CamColChk; // size = 0x28
#define OBJECT_EXCHANGE_BANK_MAX 19
#define OBJECT_ID_MAX 417
typedef struct ZARInfo {
/* 0x00 */ void* buf;
/* 0x04 */ char unk_04[0x48];
/* 0x4C */ void*** cmbPtrs; /* Really, this is a pointer to an array of pointers to CMB managers,
the first member of which is a pointer to the CMB data */
/* 0x50 */ void*** csabPtrs; /* Same as above but for CSAB */
/* 0x54 */ char unk_54[0x04];
/* 0x58 */ void*** cmabPtrs; /* Same as above but for CMAB */
/* 0x5C */ char unk_5C[0x14];
} ZARInfo; // size = 0x70
typedef struct {
/* 0x00 */ s16 id;
/* 0x02 */ char unk_02[0x0E];
/* 0x10 */ ZARInfo zarInfo;
} ObjectStatus; // size = 0x80
typedef struct {
/* 0x000 */ u8 num;
/* 0x001 */ char unk_01[0x3];
/* 0x004 */ ObjectStatus status[OBJECT_EXCHANGE_BANK_MAX];
} ObjectContext; // size = 0x984
typedef struct {
/* 0x00 */ char filename[0x40];
/* 0x40 */ u32 size;
} ObjectFile;
typedef struct {
/* 0x00 */ s16 id;
/* 0x02 */ Vec3s pos;
/* 0x08 */ Vec3s rot;
/* 0x0E */ s16 params;
} ActorEntry; // size = 0x10
typedef struct {
/* 0x00 */ u8 spawn;
/* 0x01 */ u8 room;
} EntranceEntry;
typedef struct GameState {
/* 0x00 */ GraphicsContext* gfxCtx;
/* 0x04 */ void (*main)(struct GameState*);
/* 0x08 */ void (*destroy)(struct GameState*); // "cleanup"
/* 0x0C */ void (*init)(struct GameState*);
// TODO
} GameState;
// Global Context (ram start: 0871E840)
typedef struct GlobalContext {
// /* 0x0000 */ GameState state;
/* 0x0000 */ char unk_0[0x0104];
/* 0x0104 */ s16 sceneNum;
/* 0x0106 */ char unk_106[0x0012];
/* 0x0118 */ SubGlobalContext_118 sub118;
/* 0x017C */ char unk_17C[0x000C];
struct {
/* 0x0188 */ s32 topY;
/* 0x018C */ s32 bottomY;
/* 0x0190 */ s32 leftX;
/* 0x0194 */ s32 rightX;
/* 0x0198 */ f32 fovY;
/* 0x019C */ f32 zNear;
/* 0x01A0 */ f32 zFar;
/* 0x01A4 */ f32 scale;
/* 0x01A8 */ char unk_01A8[0x0010];
/* 0x01B8 */ Vec3f eye;
/* 0x01C4 */ Vec3f at;
/* 0x01D0 */ Vec3f up;
/* 0x01DC */ char unk_01DC[0x0150];
/* 0x032C */ Vec3f distortionOrientation;
/* 0x0338 */ Vec3f distortionScale;
/* 0x0344 */ char unk_0344[0x0018];
/* 0x035C */ f32 distortionSpeed;
/* 0x0360 */ char unk_0360[0x0004];
} view;
/* 0x0364 */ Camera mainCamera;
/* 0x0520 */ Camera subCameras[3];
/* 0x0A54 */ Camera* cameraPtrs[4];
/* 0x0A64 */ s16 activeCamera;
/* 0x0A66 */ char unk_A66[0x0032];
/* 0x0A98 */ CollisionContext colCtx;
/* 0x208C */ ActorContext actorCtx;
/* 0x2264 */ char unk_2264[0x0034];
/* 0x2298 */ CutsceneContext csCtx; // "demo_play"
/* 0x2304 */ char unk_2304[0x078C];
/* 0x2A90 */ u8 msgMode; // seems to be used primarily for the ocarina
/* 0x2A91 */ char unk_2A91[0xEB];
/* 0x2B7C */ u16 lastPlayedSong;
/* 0x2B7E */ s16 unk_2B7E; // msgCtx.unk_E3EE in OoT
/* 0x2B80 */ char unk_2B80[0x06B0];
/* 0x3230 */ u32 lightSettingsList_addr;
/* 0x3234 */ char unk_3234[0x0824];
/* 0x3A58 */ ObjectContext objectCtx;
/* 0x43DC */ char unk_43DC[0x0854];
/* 0x4C30 */ u8 roomNum;
/* 0x4C31 */ char unk_4C31[0x0FCF];
/* 0x5C00 */ u8 linkAgeOnLoad;
/* 0x5C01 */ u8 unk_5C01;
/* 0x5C02 */ u8 curSpawn;
/* 0x5C03 */ char unk_5C03[0x0006];
/* 0x5C09 */ ActorEntry* linkActorEntry;
/* 0x5C0D */ char unk_5C0D[0x0008];
/* 0x5C19 */ EntranceEntry* setupEntranceList;
/* 0x5C1C */ s16* setupExitList;
/* 0x5C20 */ char unk_5C20[0x000D];
/* 0x5C2D */ s8 sceneLoadFlag; // "fade_direction"
/* 0x5C2E */ char unk_5C2E[0x0004];
/* 0x5C32 */ s16 nextEntranceIndex;
/* 0x5C34 */ char unk_5C34[0x0042];
/* 0x5C76 */ u8 fadeOutTransition;
/* 0x5C78 */ CollisionCheckContext colChkCtx;
// TODO
} GlobalContext; // size = 0x5F14 TODO
_Static_assert(sizeof(GlobalContext) == 0x5F14, "Global Context size");
typedef struct StaticContext {
/* 0x0000 */ char unk_0[0x0D38];
/* 0x0D38 */ s16 dekuNutFlash; // set to -1 to trigger flash
/* 0x0D3A */ char unk_D3A[0x0126];
/* 0x0E60 */ u16 spawnOnEpona;
/* 0x0E62 */ char unk_E72[0x0010];
/* 0x0E72 */ u16 collisionDisplay;
/* 0x0E74 */ char unk_E74[0x015C];
/* 0x0FD0 */ u16 renderGeometryDisable;
/* 0x0FD2 */ char unk_FD2[0x0602];
} StaticContext; // size 0x15D4
// _Static_assert(sizeof(StaticContext) == 0x15D4, "Static Context size");
typedef struct {
/* 0x00 */ s8 scene;
/* 0x01 */ s8 spawn;
/* 0x02 */ u16 field;
} EntranceInfo; // size = 0x4
typedef struct {
/* 0x00 */ char infoFilename[0x44];
/* 0x44 */ char filename[0x44];
/* 0x88 */ char unk_88[0x01];
/* 0x89 */ u8 config;
/* 0x8A */ char unk_8A[0x02];
} Scene; // size = 0x8C
typedef struct {
/* 0x00 */ s16 objectId;
/* 0x02 */ u8 objectModelIdx;
/* 0x03 */ char unk_03[0x3];
} DrawItemTableEntry;
typedef struct {
/* 0x00 */ u8 scene;
/* 0x01 */ u8 flags1;
/* 0x02 */ u8 flags2;
/* 0x03 */ u8 flags3;
} RestrictionFlags;
typedef struct TargetIndicatorModels {
/* 0x00 */ SkeletonAnimationModel* pointer; // arrow above targetable actor
/* 0x04 */ SkeletonAnimationModel* reticle[4]; // four arrows circling around target
/* 0x14 */ SkeletonAnimationModel* reticleAfterimageOne[4]; // four arrows trailing behind `reticle`
/* 0x24 */ SkeletonAnimationModel* reticleAfterimageTwo[4]; // four arrows trailing behind `reticleAfterimageOne`
} TargetIndicatorModels;
typedef struct TargetContext {
/* 0x000 */ char unk_000[0x4E];
/* 0x04E */ u8 reticleActorType;
/* 0x04F */ char unk_04F[0x61];
/* 0x0B0 */ TargetIndicatorModels visibleTargetIndicators; // culled when behind a wall
/* 0x0E4 */ TargetIndicatorModels hiddenTargetIndicators; // drawn even when behind walls
/* 0x118 */ char unk_118[0x08];
/* 0x120 */ ZARInfo* zarInfo;
/* 0x124 */ char unk_120[0x04];
/* 0x128 */ u32 pointerActorType;
// ... size unknown
} TargetContext;
extern GlobalContext* gGlobalContext;
extern const u32 ItemSlots[];
extern const char DungeonNames[][25];
#define gSaveContext (*(SaveContext*)0x00587958)
#define gStaticContext (*(StaticContext*)0x08080010)
#define gObjectTable ((ObjectFile*)0x53CCF4)
#define gEntranceTable ((EntranceInfo*)0x543BB8)
#define gItemUsabilityTable ((u8*)0x506C58)
#define gGearUsabilityTable ((u32*)0x4D47C8)
#define gDungeonSceneTable ((Scene*)0x4DC400)
#define gMQDungeonSceneTable ((Scene*)0x4DCBA8)
#define gSceneTable ((Scene*)0x545484)
#define gRandInt (*(u32*)0x50C0C4)
#define gRandFloat (*(f32*)0x50C0C8)
#define gDrawItemTable ((DrawItemTableEntry*)0x4D88C8)
#define gRestrictionFlags ((RestrictionFlags*)0x539DC4)
#define PLAYER ((Player*)gGlobalContext->actorCtx.actorList[ACTORTYPE_PLAYER].first)
#define GearSlot(X) (X - ITEM_SWORD_KOKIRI)
typedef enum {
DUNGEON_DEKU_TREE = 0,
DUNGEON_DODONGOS_CAVERN,
DUNGEON_JABUJABUS_BELLY,
DUNGEON_FOREST_TEMPLE,
DUNGEON_FIRE_TEMPLE,
DUNGEON_WATER_TEMPLE,
DUNGEON_SPIRIT_TEMPLE,
DUNGEON_SHADOW_TEMPLE,
DUNGEON_BOTTOM_OF_THE_WELL,
DUNGEON_ICE_CAVERN,
DUNGEON_GANONS_TOWER,
DUNGEON_GERUDO_TRAINING_GROUNDS,
DUNGEON_THIEVES_HIDEOUT,
DUNGEON_INSIDE_GANONS_CASTLE,
DUNGEON_GANONS_TOWER_COLLAPSING_INTERIOR,
DUNGEON_GANONS_CASTLE_COLLAPSING,
DUNGEON_TREASURE_CHEST_SHOP,
DUNGEON_DEKU_TREE_BOSS_ROOM,
DUNGEON_DODONGOS_CAVERN_BOSS_ROOM,
DUNGEON_JABUJABUS_BELLY_BOSS_ROOM,
DUNGEON_FOREST_TEMPLE_BOSS_ROOM,
DUNGEON_FIRE_TEMPLE_BOSS_ROOM,
DUNGEON_WATER_TEMPLE_BOSS_ROOM,
DUNGEON_SPIRIT_TEMPLE_BOSS_ROOM,
DUNGEON_SHADOW_TEMPLE_BOSS_ROOM,
} DungeonId;
#define SCENE_LINK_HOUSE 52
/* TODO: figure out what to do with this stuff */
#define real_hid_addr 0x10002000
#define real_hid (*(hid_mem_t*)real_hid_addr)
#define Z3D_TOP_SCREEN_LEFT_1 0x14313890
#define Z3D_TOP_SCREEN_LEFT_2 0x14359DA0
#define Z3D_TOP_SCREEN_RIGHT_1 0x14410AD0
#define Z3D_TOP_SCREEN_RIGHT_2 0x14456FE0
#define Z3D_BOTTOM_SCREEN_1 0x143A02B0
#define Z3D_BOTTOM_SCREEN_2 0x143D86C0
typedef void (*Item_Give_proc)(GlobalContext* globalCtx, u8 item);
#define Item_Give_addr 0x376A78
#define Item_Give ((Item_Give_proc)Item_Give_addr)
typedef void (*DisplayTextbox_proc)(GlobalContext* globalCtx, u16 textId, Actor* actor);
#define DisplayTextbox_addr 0x367C7C
#define DisplayTextbox ((DisplayTextbox_proc)DisplayTextbox_addr)
typedef u32 (*EventCheck_proc)(u32 flag);
#define EventCheck_addr 0x350CF4
#define EventCheck ((EventCheck_proc)EventCheck_addr)
typedef void (*EventSet_proc)(u32 flag);
#define EventSet_addr 0x34CBF8
#define EventSet ((EventSet_proc)EventSet_addr)
typedef void (*Rupees_ChangeBy_proc)(s16 rupeeChange);
#define Rupees_ChangeBy_addr 0x376A60
#define Rupees_ChangeBy ((Rupees_ChangeBy_proc)Rupees_ChangeBy_addr)
typedef void (*LinkDamage_proc)(GlobalContext* globalCtx, Player* player, s32 arg2, f32 arg3, f32 arg4, s16 arg5,
s32 arg6);
#define LinkDamage_addr 0x35D304
#define LinkDamage ((LinkDamage_proc)LinkDamage_addr)
typedef u32 (*Inventory_HasEmptyBottle_proc)(void);
#define Inventory_HasEmptyBottle_addr 0x377A04
#define Inventory_HasEmptyBottle ((Inventory_HasEmptyBottle_proc)Inventory_HasEmptyBottle_addr)
typedef void (*PlaySound_proc)(u32);
#define PlaySound_addr 0x35C528
// This function plays sound effects and music tracks, overlaid on top of the current BGM
#define PlaySound ((PlaySound_proc)PlaySound_addr)
typedef Actor* (*Actor_Spawn_proc)(ActorContext* actorCtx, GlobalContext* globalCtx, s16 actorId, float posX,
float posY, float posZ, s16 rotX, s16 rotY, s16 rotZ, s16 params)
__attribute__((pcs("aapcs-vfp")));
#define Actor_Spawn_addr 0x3738D0
#define Actor_Spawn ((Actor_Spawn_proc)Actor_Spawn_addr)
typedef Actor* (*Actor_Find_proc)(ActorContext* actorCtx, s16 actorId, u8 actorType);
#define Actor_Find_addr 0x372D64
#define Actor_Find ((Actor_Find_proc)Actor_Find_addr)
typedef void (*Actor_GetScreenPos_proc)(GlobalContext* globalCtx, Actor* actor, s16* outX, s16* outY);
#define Actor_GetScreenPos_addr 0x363A20
#define Actor_GetScreenPos ((Actor_GetScreenPos_proc)Actor_GetScreenPos_addr)
typedef void (*FireDamage_proc)(Actor* player, GlobalContext* globalCtx, int flamesColor);
#define FireDamage_addr 0x35D8D8
#define FireDamage ((FireDamage_proc)FireDamage_addr)
typedef void (*Flags_SetEnv_proc)(GlobalContext* globalCtx, s16 flag);
#define Flags_SetEnv_addr 0x366704
#define Flags_SetEnv ((Flags_SetEnv_proc)Flags_SetEnv_addr)
typedef void (*GiveItem_proc)(Actor* actor, GlobalContext* globalCtx, s32 getItemId, f32 xzRange, f32 yRange)
__attribute__((pcs("aapcs-vfp")));
#define GiveItem_addr 0x3724DC
#define GiveItem ((GiveItem_proc)GiveItem_addr)
typedef void (*Message_CloseTextbox_proc)(GlobalContext* globalCtx);
#define Message_CloseTextbox_addr 0x3725E0
#define Message_CloseTextbox ((Message_CloseTextbox_proc)Message_CloseTextbox_addr)
typedef void (*SetupItemInWater_proc)(Player* player, GlobalContext* globalCtx);
#define SetupItemInWater_addr 0x354894
#define SetupItemInWater ((SetupItemInWater_proc)SetupItemInWater_addr)
typedef void (*Health_ChangeBy_proc)(GlobalContext* arg1, u32 arg2);
#define Health_ChangeBy_addr 0x352DBC
#define Health_ChangeBy ((Health_ChangeBy_proc)Health_ChangeBy_addr)
typedef void (*PlaySFX_proc)(u32 sfxId, Vec3f* pos, u32 token, f32* freqScale, f32* a4, s8* reverbAdd);
#define PlaySFX_addr 0x37547C
#define PlaySFX ((PlaySFX_proc)PlaySFX_addr)
typedef void (*Flags_SetSwitch_proc)(GlobalContext* globalCtx, u32 flag);
#define Flags_SetSwitch_addr 0x375C10
#define Flags_SetSwitch ((Flags_SetSwitch_proc)Flags_SetSwitch_addr)
typedef u32 (*Flags_GetSwitch_proc)(GlobalContext* globalCtx, u32 flag);
#define Flags_GetSwitch_addr 0x36E864
#define Flags_GetSwitch ((Flags_GetSwitch_proc)Flags_GetSwitch_addr)
typedef u32 (*Flags_GetCollectible_proc)(GlobalContext* globalCtx, u32 flag);
#define Flags_GetCollectible_addr 0x36405C
#define Flags_GetCollectible ((Flags_GetCollectible_proc)Flags_GetCollectible_addr)
typedef void (*Player_SetEquipmentData_proc)(GlobalContext* globalCtx, Player* player);
#define Player_SetEquipmentData_addr 0x34913C
#define Player_SetEquipmentData ((Player_SetEquipmentData_proc)Player_SetEquipmentData_addr)
typedef s32 (*BossChallenge_IsActive_proc)(void);
#define BossChallenge_IsActive_addr 0x35B164
#define BossChallenge_IsActive ((BossChallenge_IsActive_proc)BossChallenge_IsActive_addr)
typedef s32 (*Audio_PlayActorSfx2_proc)(Actor* actor, s32 sfxID);
#define Audio_PlayActorSfx2_addr 0x375BCC
#define Audio_PlayActorSfx2 ((Audio_PlayActorSfx2_proc)Audio_PlayActorSfx2_addr)
typedef void (*Model_EnableMeshGroupByIndex_proc)(SkeletonAnimationModel* skel, u32 index);
#define Model_EnableMeshGroupByIndex ((Model_EnableMeshGroupByIndex_proc)0x37266C)
typedef void (*Model_DisableMeshGroupByIndex_proc)(SkeletonAnimationModel* skel, u32 index);
#define Model_DisableMeshGroupByIndex ((Model_DisableMeshGroupByIndex_proc)0x36932C)
typedef s32 (*Player_InBlockingCsMode_proc)(GlobalContext* globalCtx, Player* player);
#define Player_InBlockingCsMode ((Player_InBlockingCsMode_proc)0x35DB20)
typedef s32 (*Camera_CheckWater_proc)(Camera* camera);
#define Camera_CheckWater ((Camera_CheckWater_proc)0x2D06A0)
typedef void (*Camera_UpdateInterface_proc)(u32 flags);
#define Camera_UpdateInterface ((Camera_UpdateInterface_proc)0x330D84)
typedef f32 (*Camera_BGCheckInfo_proc)(Camera* camera, Vec3f* from, CamColChk* to);
#define Camera_BGCheckInfo ((Camera_BGCheckInfo_proc)0x3553FC)
typedef s32 (*Quake_Update_proc)(Camera* camera, ShakeInfo* camShake);
#ifdef Version_EUR
#define Quake_Update_addr 0x4787E8
#else
#define Quake_Update_addr 0x4787C8
#endif
#define Quake_Update ((Quake_Update_proc)Quake_Update_addr)
typedef s16 (*Camera_GetCamDataId_proc)(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
#ifdef Version_EUR
#define Camera_GetCamDataId_addr 0x47BFF8
#else
#define Camera_GetCamDataId_addr 0x47BFD8
#endif
#define Camera_GetCamDataId ((Camera_GetCamDataId_proc)Camera_GetCamDataId_addr)
#endif //_Z3D_H_

350
include/z3D/z3Dactor.h Normal file
View File

@@ -0,0 +1,350 @@
#ifndef _Z3DACTOR_H_
#define _Z3DACTOR_H_
#include "z3Dvec.h"
struct Actor;
struct GlobalContext;
struct LightMapper;
struct ZARInfo;
typedef struct {
Vec3f pos;
Vec3s rot;
} PosRot; // size = 0x14
typedef struct {
/* 0x00 */ char unk_00[0x8];
/* 0x08 */ Vec3s norm; // Normal vector
/* 0x0E */ s16 dist; // Plane distance from origin
} CollisionPoly; // size = 0x10
struct SkeletonAnimationModel;
typedef void (*SkeletonAnimationModelFunc)(struct SkeletonAnimationModel*);
typedef struct {
/* 0x00 */ char unk_00[0x4];
/* 0x04 */ SkeletonAnimationModelFunc destroy;
} SkeletonAnimationModel_VTable;
typedef struct SkeletonAnimationModel_unk_10 {
/* 0x00 */ char unk_00[0x14];
} SkeletonAnimationModel_unk_10; // size = 0x14
typedef struct SkeletonAnimationModel_unk_0C {
/* 0x00 */ SkeletonAnimationModel_unk_10* unk_00;
/* 0x04 */ void* cmabManager;
/* 0x08 */ f32 curFrame;
/* 0x0C */ f32 animSpeed;
/* 0x10 */ s8 animMode;
/* 0x11 */ char unk_11[0x87];
} SkeletonAnimationModel_unk_0C; // size = 0x98
typedef struct SkeletonAnimationModel {
/* 0x00 */ SkeletonAnimationModel_VTable* vtbl;
/* 0x04 */ char unk_04[0x08];
/* 0x0C */ SkeletonAnimationModel_unk_0C* unk_0C;
/* 0x10 */ SkeletonAnimationModel_unk_10* unk_10;
/* 0x14 */ void* unk_draw_struct_14;
/* 0x18 */ char unk_18[0x64];
/* 0x7C */ nn_math_MTX34 mtx;
/* 0xAC */ s8 unk_AC;
/* 0xAD */ char unk_AD[0x03];
} SkeletonAnimationModel; // size = 0xB0
typedef struct SkelAnime {
/* 0x00 */ void* unk_00;
/* 0x04 */ struct ZARInfo* zarInfo;
/* 0x08 */ char unk_08[0x08];
/* 0x10 */ struct GlobalContext* globalCtx;
/* 0x14 */ char unk_14[0x14];
/* 0x28 */ struct SkeletonAnimationModel* unk_28;
/* 0x2C */ char unk_2C[0x4];
/* 0x30 */ s32 animationType;
/* 0x34 */ char unk_34[0x8];
/* 0x3C */ f32 curFrame;
/* 0x40 */ f32 playSpeed;
/* 0x44 */ f32 startFrame;
/* 0x48 */ f32 endFrame;
/* 0x4C */ f32 animLength;
/* 0x50 */ char unk_50[0x24];
/* 0x74 */ u8 limbCount;
/* 0x75 */ char unk_75[0x03];
/* 0x78 */ void* jointTable;
/* 0x7C */ void* morphTable;
/* 0x80 */ char unk_80[0x02];
/* 0x82 */ u8 tablesAllocated;
/* 0x83 */ char unk_83[0x01];
} SkelAnime; // size = 0x84
typedef void (*ActorFunc)(struct Actor*, struct GlobalContext*);
typedef struct {
/* 0x00 */ s16 id;
/* 0x02 */ u8 type; // Classifies actor and determines when actor will execute
/* 0x03 */ u8 room; // Room instance was spawned in. If value set to FF in rom,
// instance does not despawn when swapping rooms
/* 0x04 */ u32 flags;
/* 0x08 */ s16 objectId;
/* 0x0C */ u32 instanceSize;
/* 0x10 */ ActorFunc init; // Constructor
/* 0x14 */ ActorFunc destroy; // Destructor
/* 0x18 */ ActorFunc update; // Main Update Function
/* 0x1C */ ActorFunc draw; // Draw function
} ActorInit; // size = 0x20
typedef enum {
ALLOCTYPE_NORMAL,
ALLOCTYPE_ABSOLUTE,
ALLOCTYPE_PERMANENT
} AllocType;
typedef struct {
/* 0x00 */ u32 vromStart; // unused?
/* 0x04 */ u32 vromEnd; // unused?
/* 0x08 */ void* vramStart; // unused?
/* 0x0C */ void* vramEnd; // unused?
/* 0x10 */ void* loadedRamAddr; // unused?
/* 0x14 */ ActorInit* initInfo;
/* 0x18 */ char* name; // unused?
/* 0x1C */ u16 allocType; // unused?
/* 0x1E */ s8 nbLoaded; // unused?
} ActorOverlay; // size = 0x20
typedef struct {
struct {
char damage : 4;
char effect : 4;
} attack[32];
} ActorDamageChart;
typedef struct {
/* 0x00 */ ActorDamageChart* damageChart; // For actors which contain a damage chart (example: Stalfos)...
/* 0x04 */ Vec3f displacement; // Amount to correct velocity (0x5C) by when colliding into a body
/* 0x10 */ s16 unk_10;
/* 0x12 */ s16 unk_12;
/* 0x14 */ u16 unk_14;
/* 0x16 */ u8 mass; // Used to compute displacement, 50 is common value, 0xFF for infinite mass/unmoveable
/* 0x17 */ u8 health;
/* 0x18 */ u8 damage; // Amount to decrement health by
/* 0x19 */ u8 damageEffect; // Stores what effect should occur when hit by a weapon
/* 0x1A */ u8 atHitEffect;
/* 0x1B */ u8 acHitEffect;
} CollisionCheckInfo; // size = 0x1C
typedef struct {
/* 0x00 */ struct Actor* actor; // Attached actor
/* 0x04 */ struct Actor* at; // Actor attached to what it collided with as an AT collider.
/* 0x08 */ struct Actor* ac; // Actor attached to what it collided with as an AC collider.
/* 0x0C */ struct Actor* oc; // Actor attached to what it collided with as an OC collider.
/* 0x10 */ u8 atFlags; // Information flags for AT collisions.
/* 0x11 */ u8 acFlags; // Information flags for AC collisions.
/* 0x12 */ u8 ocFlags1; // Information flags for OC collisions.
/* 0x13 */ u8 ocFlags2; // Flags related to which colliders it can OC collide with.
/* 0x14 */ u8 colType; // Determines hitmarks and sound effects during AC collisions.
/* 0x15 */ u8 shape; // JntSph, Cylinder, Tris, or Quad
} Collider; // size = 0x18
typedef struct {
/* 0x00 */ Collider base;
/* 0x18 */ // ColliderInfo info;
/* 0x40 */ // Cylinderf dim;
} ColliderCylinder; // size = 0x58
typedef struct {
/* 0x00 */ Vec3s rot; // Current actor shape rotation
/* 0x06 */ u8 unk_06;
/* 0x08 */ f32 unk_08; // Model y axis offset. Represents model space units. collision mesh related
/* 0x0C */ void (*shadowDrawFunc)(struct Actor*, struct LightMapper*, struct GlobalContext*);
/* 0x10 */ f32 unk_10;
/* 0x14 */ u8 unk_14;
/* 0x15 */ u8 unk_15;
} ActorShape; // size = 0x18
typedef struct Actor {
/* 0x000 */ s16 id; // Actor Id
/* 0x002 */ u8 type; // Actor Type. Refer to the corresponding enum for values
/* 0x003 */ s8 room; // Room number the actor is in. -1 denotes that the actor won't despawn on a room change
/* 0x004 */ u32 flags; // Flags used for various purposes
/* 0x008 */ PosRot home; // Initial position/rotation when spawned. Can be used for other purposes
/* 0x01C */ s16 params; // Configurable variable set by the actor's spawn data; original name: "args_data"
/* 0x01E */ s8 objBankIndex; // Object bank index of the actor's object dependency; original name: "bank"
/* 0x01F */ s8 targetMode; // Controls how far the actor can be targeted from and how far it can stay locked on
/* 0x020 */ u8 unk_20;
/* 0x021 */ char unk_21[0x3];
/* 0x024 */ u32 sfx; // SFX ID to play. Sound plays when value is set, then is cleared the following update cycle
/* 0x028 */ PosRot world; // Position/rotation in the world
/* 0x03C */ PosRot focus; // Target reticle focuses on this position. For player this represents head pos and rot
/* 0x050 */ f32 targetArrowOffset; // Height offset of the target arrow relative to `focus` position
/* 0x054 */ Vec3f scale; // Scale of the actor in each axis
/* 0x060 */ Vec3f velocity; // Velocity of the actor in each axis
/* 0x06C */ f32 speedXZ; // How fast the actor is traveling along the XZ plane
/* 0x070 */ f32 gravity; // Acceleration due to gravity. Value is added to Y velocity every frame
/* 0x074 */ f32 minVelocityY; // Sets the lower bounds cap on velocity along the Y axis
/* 0x078 */ CollisionPoly* wallPoly; // Wall polygon an actor is touching
/* 0x07C */ CollisionPoly* floorPoly; // Floor polygon directly below the actor
/* 0x080 */ u8 wallBgId; // Bg ID of the wall polygon the actor is touching
/* 0x081 */ u8 floorBgId; // Bg ID of the floor polygon directly below the actor
/* 0x082 */ s16 wallYaw; // Y rotation of the wall polygon the actor is touching
/* 0x084 */ f32 floorHeight; // Y position of the floor polygon directly below the actor
/* 0x088 */ f32 yDistToWater; // Distance to the surface of active waterbox. Negative value means above water
/* 0x08C */ f32 waterBoxYSurface;
/* 0x090 */ u16 bgCheckFlags;
/* 0x092 */ s16 yawTowardsPlayer; // Y rotation difference between the actor and the player
/* 0x094 */ f32 xyzDistToPlayerSq; // Squared distance between the actor and the player in the x,y,z axis
/* 0x098 */ f32 xzDistToPlayer; // Distance between the actor and the player in the XZ plane
/* 0x09C */ f32 yDistToPlayer; // Dist is negative if the actor is above the player
/* 0x0A0 */ CollisionCheckInfo colChkInfo; // Variables related to the Collision Check system
/* 0x0BC */ ActorShape shape; // Variables related to the physical shape of the actor
/* 0x0D4 */ Vec3f unk_D4[2];
/* 0x0EC */ Vec3f unk_EC; // Stores result of some vector transformation involving actor xyz vector, and a matrix at
// Global Context + 11D60
/* 0x0F8 */ f32 unk_F8; // Related to above
/* 0x0FC */ f32 uncullZoneForward; // Amount to increase the uncull zone forward by (in projected space)
/* 0x100 */ f32 uncullZoneScale; // Amount to increase the uncull zone scale by (in projected space)
/* 0x104 */ f32 uncullZoneDownward; // Amount to increase uncull zone downward by (in projected space)
/* 0x108 */ Vec3f prevPos; // World position from the previous update cycle
/* 0x114 */ u8 isTargeted; // Set to true if the actor is currently being targeted by the player
/* 0x115 */ u8 targetPriority; // Lower values have higher priority. Resets to 0 when player stops targeting
/* 0x116 */ u16 textId; // Text ID to pass to link/display when interacting with the actor
/* 0x118 */ u16 freezeTimer; // Actor does not update when set. Timer decrements automatically
/* 0x11A */ s16 colorFilterTimer;
/* 0x11C */ u32 colorFilterParams;
/* 0x120 */ char unk_120;
/* 0x121 */ u8 isDrawn; // Set to true if the actor is currently being drawn. Always stays false for lens actors
/* 0x122 */ u8 unk_122; // Set within a routine that deals with collision
/* 0x123 */ u8 naviEnemyId; // Sets what 0600 dialog to display when talking to navi. Default 0xFF
/* 0x124 */ struct Actor* parent; // Usage is actor specific. Set if actor is spawned via `Actor_SpawnAsChild`
/* 0x128 */ struct Actor* child; // Usage is actor specific. Set if actor is spawned via `Actor_SpawnAsChild`
/* 0x12C */ struct Actor* prev; // Previous actor of this category
/* 0x130 */ struct Actor* next; // Next actor of this category
/* 0x134 */ ActorFunc init; // Initialization Routine. Called by `Actor_Init` or `Actor_UpdateAll`
/* 0x138 */ ActorFunc destroy; // Destruction Routine. Called by `Actor_Destroy`
/* 0x13C */ ActorFunc update; // Update Routine. Called by `Actor_UpdateAll`
/* 0x140 */ ActorFunc draw; // Draw Routine. Called by `Actor_Draw`
/* 0x144 */ ActorOverlay* overlayEntry; // Pointer to the overlay table entry for this actor
/* 0x148 */ nn_math_MTX34 modelMtx; // Transforms model space coordinates to world coordinates
/* 0x178 */ void* unk_178; // Unknown pointer type
/* 0x17C */ void* unk_17C[6]; // Unknown pointer type
/* 0x194 */ u32 unk_194;
/* 0x198 */ u8 unk_198;
/* 0x199 */ char unk_199[0x3];
/* 0x19C */ u16 unk_19C;
/* 0x19E */ char unk_19E[0x2];
/* 0x1A0 */ f32 unk_1A0;
/* From here on, the structure and size varies for each actor */
} Actor; // size = 0x1A4
typedef struct DynaPolyActor {
/* 0x000 */ struct Actor actor;
/* 0x1A4 */ s32 bgId;
/* 0x1A8 */ f32 unk_1A8;
/* 0x1AC */ f32 unk_1AC;
/* 0x1B0 */ s16 unk_1B0;
/* 0x1B2 */ u16 unk_1B2;
/* 0x1B4 */ u32 unk_1B4;
/* 0x1B8 */ u8 unk_1B8;
/* 0x1BA */ s16 unk_1BA;
} DynaPolyActor; // size = 0x1BC
typedef struct {
/* 0x00 */ Actor* actor;
/* 0x04 */ char unk_04[0x10];
/* 0x14 */ Vec3f scale1;
/* 0x20 */ Vec3s rot1;
/* 0x28 */ Vec3f pos1;
/* 0x34 */ Vec3f scale2;
/* 0x40 */ Vec3s rot2;
/* 0x48 */ Vec3f pos2;
/* 0x54 */ char unk_54[0x18];
} ActorMesh; // size = 0x6C
typedef struct {
/* 0x0000 */ Actor actor;
/* 0x01A4 */ char unk_148[0x0005];
/* 0x01A9 */ s8 heldItemActionParam;
/* 0x01AA */ u8 heldItemId;
/* 0x01AB */ char unk_1AB[0x1];
/* 0x01AC */ s8 itemActionParam;
/* 0x01AD */ char unk_1AD[0x0003];
/* 0x01B0 */ u8 modelGroup;
/* 0x01B1 */ u8 nextModelGroup;
/* 0x01B2 */ s8 unk_1B2;
/* 0x01B3 */ u8 modelAnimType;
/* 0x01B4 */ u8 leftHandType;
/* 0x01B5 */ u8 rightHandType;
/* 0x01B6 */ u8 sheathType;
/* 0x01B7 */ u8 currentMask;
/* 0x01B8 */ char unk_1B8[0x0004];
/* 0x01BC */ void* rightHandDLists;
/* 0x01C0 */ void* leftHandDLists;
/* 0x01C4 */ void* sheathDLists;
/* 0x01C8 */ void* waistDLists;
/* 0x01CC */ char unk_1CC[0x80];
/* 0x024C */ void* giDrawSpace;
/* 0x0250 */ char unk_250[0x0004];
/* 0x0254 */ struct SkelAnime skelAnime;
/* 0x02D8 */ char unk_2D8[0x0F4C];
/* 0x1224 */ Actor* heldActor;
/* 0x1228 */ char unk_1228[0x84];
/* 0x12AC */ u8 getItemId;
/* 0x12AD */ char unk_12AD[0x0001];
/* 0x12AE */ u16 getItemDirection;
/* 0x12B0 */ Actor* interactRangeActor;
/* 0x12B4 */ char unk_12B4[0x0454];
/* 0x1708 */ void* stateFuncPtr;
/* 0x170C */ char unk_170C[0x0004];
/* 0x1710 */ u32 stateFlags1;
/* 0x1714 */ u32 stateFlags2;
/* 0x1718 */ Actor* unk_1718;
/* 0x171C */ Actor* boomerangActor;
/* 0x1720 */ Actor* unk_1720;
/* 0x1724 */ Actor* naviActor;
/* 0x1728 */ s16 naviTextId;
/* 0x172A */ u8 stateFlags3;
/* 0x172B */ s8 exchangeItemId;
/* 0x172C */ char unk_172C[0x0AF0];
/* 0x221C */ float xzSpeed; // probably
/* 0x2220 */ char unk_2220[0x0007];
/* 0x2227 */ s8 meleeWeaponState;
/* 0x2228 */ char unk_2228[0x0020];
/* 0x2248 */ s16 fishingState; // 1: casting line, 2: can reel, 3: holding catch
/* 0x224A */ char unk_224A[0x023E];
/* 0x2488 */ s8 invincibilityTimer; // prevents damage when nonzero
// (positive = visible, counts towards zero each frame)
/* 0x2489 */ char unk_2489[0x27B];
/* 0x2704 */ struct SkeletonAnimationModel_unk_0C* bodyTexAnim;
} Player; // total size (from init vars): 2A4C
typedef enum {
/* 0x00 */ ACTORTYPE_SWITCH,
/* 0x01 */ ACTORTYPE_BG,
/* 0x02 */ ACTORTYPE_PLAYER,
/* 0x03 */ ACTORTYPE_EXPLOSIVES,
/* 0x04 */ ACTORTYPE_NPC,
/* 0x05 */ ACTORTYPE_ENEMY,
/* 0x06 */ ACTORTYPE_PROP,
/* 0x07 */ ACTORTYPE_ITEMACTION,
/* 0x08 */ ACTORTYPE_MISC,
/* 0x09 */ ACTORTYPE_BOSS,
/* 0x0A */ ACTORTYPE_DOOR,
/* 0x0B */ ACTORTYPE_CHEST
} ActorType;
typedef struct ActorHeapNode {
u16 magic;
u16 free;
u32 size;
struct ActorHeapNode* next;
struct ActorHeapNode* prev;
} ActorHeapNode;
void Actor_Kill(Actor* actor);
#define gActorOverlayTable ((ActorOverlay*)0x50CD84)
typedef u32 (*Actor_HasParent_proc)(Actor* actor, struct GlobalContext* globalCtx);
#define Actor_HasParent_addr 0x371E40
#define Actor_HasParent ((Actor_HasParent_proc)Actor_HasParent_addr)
#endif

46
include/z3D/z3Dcutscene.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef _Z3DCUTSCENE_H_
#define _Z3DCUTSCENE_H_
#include "z3Dvec.h"
typedef struct {
/* 0x00 */ u16 action; // "dousa"
/* 0x02 */ u16 startFrame;
/* 0x04 */ u16 endFrame;
/* 0x06 */ Vec3s rot;
/* 0x0C */ Vec3i startPos;
/* 0x18 */ Vec3i endPos;
/* 0x24 */ Vec3i normal;
} CsCmdActorAction; // size = 0x30
typedef enum {
/* 0x00 */ OCARINA_ACTION_UNK_0,
/* 0x01 */ OCARINA_ACTION_FREE_PLAY_OCARINA,
/* 0x02 */ OCARINA_ACTION_MINUET,
/* 0x03 */ OCARINA_ACTION_BOLERO,
/* 0x04 */ OCARINA_ACTION_SERENADE,
/* 0x05 */ OCARINA_ACTION_REQUIEM,
/* 0x06 */ OCARINA_ACTION_NOCTURNE,
/* 0x07 */ OCARINA_ACTION_PRELUDE,
/* 0x08 */ OCARINA_ACTION_SARIAS_SONG,
/* 0x09 */ OCARINA_ACTION_EPONAS_SONG,
/* 0x0A */ OCARINA_ACTION_ZELDAS_LULLABY,
/* 0x0B */ OCARINA_ACTION_SUNS_SONG,
/* 0x0C */ OCARINA_ACTION_SONG_OF_TIME,
/* 0x0D */ OCARINA_ACTION_SONG_OF_STORMS,
/* 0x0E */ OCARINA_ACTION_OCARINA_ACTION_UNK_E,
/* 0x0F */ OCARINA_ACTION_MINUET_PLAYBACK,
/* 0x10 */ OCARINA_ACTION_BOLERO_PLAYBACK,
/* 0x11 */ OCARINA_ACTION_SERENADE_PLAYBACK,
/* 0x12 */ OCARINA_ACTION_REQUIEM_PLAYBACK,
/* 0013 */ OCARINA_ACTION_NOCTURNE_PLAYBACK,
/* 0x14 */ OCARINA_ACTION_PRELUDE_PLAYBACK,
/* 0x15 */ OCARINA_ACTION_SARIAS_SONG_PLAYBACK,
/* 0x16 */ OCARINA_ACTION_EPONAS_SONG_PLAYBACK,
/* 0x17 */ OCARINA_ACTION_ZELDAS_LULLABY_PLAYBACK,
/* 0x18 */ OCARINA_ACTION_SUNS_SONG_PLAYBACK,
/* 0x19 */ OCARINA_ACTION_SONG_OF_TIME_PLAYBACK,
/* 0x1A */ OCARINA_ACTION_SONG_OF_STORMS_PLAYBACK
} OcarinaSongActionIDs;
#endif //_Z3DCUTSCENE_H_

361
include/z3D/z3Ditem.h Normal file
View File

@@ -0,0 +1,361 @@
#ifndef _Z3D_ITEM_H_
#define _Z3D_ITEM_H_
#include "z3Dvec.h"
typedef enum {
/* 0x00 */ SLOT_STICK,
/* 0x01 */ SLOT_NUT,
/* 0x02 */ SLOT_BOMB,
/* 0x03 */ SLOT_BOW,
/* 0x04 */ SLOT_ARROW_FIRE,
/* 0x05 */ SLOT_DINS_FIRE,
/* 0x06 */ SLOT_SLINGSHOT,
/* 0x07 */ SLOT_OCARINA,
/* 0x08 */ SLOT_BOMBCHU,
/* 0x09 */ SLOT_HOOKSHOT,
/* 0x0A */ SLOT_ARROW_ICE,
/* 0x0B */ SLOT_FARORES_WIND,
/* 0x0C */ SLOT_BOOMERANG,
/* 0x0D */ SLOT_LENS,
/* 0x0E */ SLOT_BEAN,
/* 0x0F */ SLOT_HAMMER,
/* 0x10 */ SLOT_ARROW_LIGHT,
/* 0x11 */ SLOT_NAYRUS_LOVE,
/* 0x12 */ SLOT_BOTTLE_1,
/* 0x13 */ SLOT_BOTTLE_2,
/* 0x14 */ SLOT_BOTTLE_3,
/* 0x15 */ SLOT_BOTTLE_4,
/* 0x16 */ SLOT_TRADE_ADULT,
/* 0x17 */ SLOT_TRADE_CHILD,
/* 0x18 */ SLOT_IRON_BOOTS,
/* 0x19 */ SLOT_HOVER_BOOTS,
/* 0xFF */ SLOT_NONE = 0xFF
} InventorySlot;
typedef enum {
/* 0x00 */ ITEM_STICK,
/* 0x01 */ ITEM_NUT,
/* 0x02 */ ITEM_BOMB,
/* 0x03 */ ITEM_BOW,
/* 0x04 */ ITEM_ARROW_FIRE,
/* 0x05 */ ITEM_DINS_FIRE,
/* 0x06 */ ITEM_SLINGSHOT,
/* 0x07 */ ITEM_OCARINA_FAIRY,
/* 0x08 */ ITEM_OCARINA_TIME,
/* 0x09 */ ITEM_BOMBCHU,
/* 0x0A */ ITEM_HOOKSHOT,
/* 0x0B */ ITEM_LONGSHOT,
/* 0x0C */ ITEM_ARROW_ICE,
/* 0x0D */ ITEM_FARORES_WIND,
/* 0x0E */ ITEM_BOOMERANG,
/* 0x0F */ ITEM_LENS,
/* 0x10 */ ITEM_BEAN,
/* 0x11 */ ITEM_HAMMER,
/* 0x12 */ ITEM_ARROW_LIGHT,
/* 0x13 */ ITEM_NAYRUS_LOVE,
/* 0x14 */ ITEM_BOTTLE,
/* 0x15 */ ITEM_POTION_RED,
/* 0x16 */ ITEM_POTION_GREEN,
/* 0x17 */ ITEM_POTION_BLUE,
/* 0x18 */ ITEM_FAIRY,
/* 0x19 */ ITEM_FISH,
/* 0x1A */ ITEM_MILK_BOTTLE,
/* 0x1B */ ITEM_LETTER_RUTO,
/* 0x1C */ ITEM_BLUE_FIRE,
/* 0x1D */ ITEM_BUG,
/* 0x1E */ ITEM_BIG_POE,
/* 0x1F */ ITEM_MILK_HALF,
/* 0x20 */ ITEM_POE,
/* 0x21 */ ITEM_WEIRD_EGG,
/* 0x22 */ ITEM_CHICKEN,
/* 0x23 */ ITEM_LETTER_ZELDA,
/* 0x24 */ ITEM_MASK_KEATON,
/* 0x25 */ ITEM_MASK_SKULL,
/* 0x26 */ ITEM_MASK_SPOOKY,
/* 0x27 */ ITEM_MASK_BUNNY,
/* 0x28 */ ITEM_MASK_GORON,
/* 0x29 */ ITEM_MASK_ZORA,
/* 0x2A */ ITEM_MASK_GERUDO,
/* 0x2B */ ITEM_MASK_TRUTH,
/* 0x2C */ ITEM_SOLD_OUT,
/* 0x2D */ ITEM_POCKET_EGG,
/* 0x2E */ ITEM_POCKET_CUCCO,
/* 0x2F */ ITEM_COJIRO,
/* 0x30 */ ITEM_ODD_MUSHROOM,
/* 0x31 */ ITEM_ODD_POTION,
/* 0x32 */ ITEM_SAW,
/* 0x33 */ ITEM_SWORD_BROKEN,
/* 0x34 */ ITEM_PRESCRIPTION,
/* 0x35 */ ITEM_FROG,
/* 0x36 */ ITEM_EYEDROPS,
/* 0x37 */ ITEM_CLAIM_CHECK,
/* 0x38 */ ITEM_BOW_ARROW_FIRE,
/* 0x39 */ ITEM_BOW_ARROW_ICE,
/* 0x3A */ ITEM_BOW_ARROW_LIGHT,
/* 0x3B */ ITEM_SWORD_KOKIRI,
/* 0x3C */ ITEM_SWORD_MASTER,
/* 0x3D */ ITEM_SWORD_BGS,
/* 0x3E */ ITEM_SHIELD_DEKU,
/* 0x3F */ ITEM_SHIELD_HYLIAN,
/* 0x40 */ ITEM_SHIELD_MIRROR,
/* 0x41 */ ITEM_TUNIC_KOKIRI,
/* 0x42 */ ITEM_TUNIC_GORON,
/* 0x43 */ ITEM_TUNIC_ZORA,
/* 0x44 */ ITEM_BOOTS_KOKIRI,
/* 0x45 */ ITEM_BOOTS_IRON,
/* 0x46 */ ITEM_BOOTS_HOVER,
/* 0x47 */ ITEM_BULLET_BAG_30,
/* 0x48 */ ITEM_BULLET_BAG_40,
/* 0x49 */ ITEM_BULLET_BAG_50,
/* 0x4A */ ITEM_QUIVER_30,
/* 0x4B */ ITEM_QUIVER_40,
/* 0x4C */ ITEM_QUIVER_50,
/* 0x4D */ ITEM_BOMB_BAG_20,
/* 0x4E */ ITEM_BOMB_BAG_30,
/* 0x4F */ ITEM_BOMB_BAG_40,
/* 0x50 */ ITEM_BRACELET,
/* 0x51 */ ITEM_GAUNTLETS_SILVER,
/* 0x52 */ ITEM_GAUNTLETS_GOLD,
/* 0x53 */ ITEM_SCALE_SILVER,
/* 0x54 */ ITEM_SCALE_GOLDEN,
/* 0x55 */ ITEM_SWORD_KNIFE,
/* 0x56 */ ITEM_WALLET_ADULT,
/* 0x57 */ ITEM_WALLET_GIANT,
/* 0x58 */ ITEM_SEEDS,
/* 0x59 */ ITEM_FISHING_POLE,
/* 0x5A */ ITEM_SONG_MINUET,
/* 0x5B */ ITEM_SONG_BOLERO,
/* 0x5C */ ITEM_SONG_SERENADE,
/* 0x5D */ ITEM_SONG_REQUIEM,
/* 0x5E */ ITEM_SONG_NOCTURNE,
/* 0x5F */ ITEM_SONG_PRELUDE,
/* 0x60 */ ITEM_SONG_LULLABY,
/* 0x61 */ ITEM_SONG_EPONA,
/* 0x62 */ ITEM_SONG_SARIA,
/* 0x63 */ ITEM_SONG_SUN,
/* 0x64 */ ITEM_SONG_TIME,
/* 0x65 */ ITEM_SONG_STORMS,
/* 0x66 */ ITEM_MEDALLION_FOREST,
/* 0x67 */ ITEM_MEDALLION_FIRE,
/* 0x68 */ ITEM_MEDALLION_WATER,
/* 0x69 */ ITEM_MEDALLION_SPIRIT,
/* 0x6A */ ITEM_MEDALLION_SHADOW,
/* 0x6B */ ITEM_MEDALLION_LIGHT,
/* 0x6C */ ITEM_KOKIRI_EMERALD,
/* 0x6D */ ITEM_GORON_RUBY,
/* 0x6E */ ITEM_ZORA_SAPPHIRE,
/* 0x6F */ ITEM_STONE_OF_AGONY,
/* 0x70 */ ITEM_GERUDO_CARD,
/* 0x71 */ ITEM_SKULL_TOKEN,
/* 0x72 */ ITEM_HEART_CONTAINER,
/* 0x73 */ ITEM_HEART_PIECE,
/* 0x74 */ ITEM_KEY_BOSS,
/* 0x75 */ ITEM_COMPASS,
/* 0x76 */ ITEM_DUNGEON_MAP,
/* 0x77 */ ITEM_KEY_SMALL,
/* 0x78 */ ITEM_MAGIC_SMALL,
/* 0x79 */ ITEM_MAGIC_LARGE,
/* 0x7A */ ITEM_HEART_PIECE_2,
/* 0x7B */ ITEM_INVALID_1,
/* 0x7C */ ITEM_INVALID_2,
/* 0x7D */ ITEM_INVALID_3,
/* 0x7E */ ITEM_INVALID_4,
/* 0x7F */ ITEM_INVALID_5,
/* 0x80 */ ITEM_INVALID_6,
/* 0x81 */ ITEM_INVALID_7,
/* 0x82 */ ITEM_MILK,
/* 0x83 */ ITEM_HEART,
/* 0x84 */ ITEM_RUPEE_GREEN,
/* 0x85 */ ITEM_RUPEE_BLUE,
/* 0x86 */ ITEM_RUPEE_RED,
/* 0x87 */ ITEM_RUPEE_PURPLE,
/* 0x88 */ ITEM_RUPEE_GOLD,
/* 0x89 */ ITEM_INVALID_8,
/* 0x8A */ ITEM_STICKS_5,
/* 0x8B */ ITEM_STICKS_10,
/* 0x8C */ ITEM_NUTS_5,
/* 0x8D */ ITEM_NUTS_10,
/* 0x8E */ ITEM_BOMBS_5,
/* 0x8F */ ITEM_BOMBS_10,
/* 0x90 */ ITEM_BOMBS_20,
/* 0x91 */ ITEM_BOMBS_30,
/* 0x92 */ ITEM_ARROWS_SMALL,
/* 0x93 */ ITEM_ARROWS_MEDIUM,
/* 0x94 */ ITEM_ARROWS_LARGE,
/* 0x95 */ ITEM_SEEDS_30,
/* 0x96 */ ITEM_BOMBCHUS_5,
/* 0x97 */ ITEM_BOMBCHUS_20,
/* 0x98 */ ITEM_STICK_UPGRADE_20,
/* 0x99 */ ITEM_STICK_UPGRADE_30,
/* 0x9A */ ITEM_NUT_UPGRADE_30,
/* 0x9B */ ITEM_NUT_UPGRADE_40,
/* 0xFF */ ITEM_NONE = 0xFF
} ItemID;
// Get Item result may vary depending on context (chest/shop/scrub/drop)
typedef enum {
/* 0x00 */ GI_INVALID, // Link picks up chest and it sends him flying upwards
/* 0x01 */ GI_BOMBS_5,
/* 0x02 */ GI_NUTS_5,
/* 0x03 */ GI_BOMBCHUS_10,
/* 0x04 */ GI_BOW,
/* 0x05 */ GI_SLINGSHOT,
/* 0x06 */ GI_BOOMERANG,
/* 0x07 */ GI_STICKS_1,
/* 0x08 */ GI_HOOKSHOT,
/* 0x09 */ GI_LONGSHOT,
/* 0x0A */ GI_LENS,
/* 0x0B */ GI_LETTER_ZELDA,
/* 0x0C */ GI_OCARINA_OOT,
/* 0x0D */ GI_HAMMER,
/* 0x0E */ GI_COJIRO,
/* 0x0F */ GI_BOTTLE,
/* 0x10 */ GI_POTION_RED,
/* 0x11 */ GI_POTION_GREEN,
/* 0x12 */ GI_POTION_BLUE,
/* 0x13 */ GI_FAIRY,
/* 0x14 */ GI_MILK_BOTTLE,
/* 0x15 */ GI_LETTER_RUTO,
/* 0x16 */ GI_BEAN,
/* 0x17 */ GI_MASK_SKULL,
/* 0x18 */ GI_MASK_SPOOKY,
/* 0x19 */ GI_CHICKEN, // uses bean message ID
/* 0x1A */ GI_MASK_KEATON,
/* 0x1B */ GI_MASK_BUNNY,
/* 0x1C */ GI_MASK_TRUTH,
/* 0x1D */ GI_POCKET_EGG,
/* 0x1E */ GI_POCKET_CUCCO, // uses bean message ID
/* 0x1F */ GI_ODD_MUSHROOM,
/* 0x20 */ GI_ODD_POTION,
/* 0x21 */ GI_SAW,
/* 0x22 */ GI_SWORD_BROKEN,
/* 0x23 */ GI_PERSCRIPTION,
/* 0x24 */ GI_FROG,
/* 0x25 */ GI_EYEDROPS,
/* 0x26 */ GI_CLAIM_CHECK,
/* 0x27 */ GI_SWORD_KOKIRI,
/* 0x28 */ GI_SWORD_KNIFE,
/* 0x29 */ GI_SHIELD_DEKU, // or blue rupee if you have the shield
/* 0x2A */ GI_SHIELD_HYLIAN, // or blue rupee if you have the shield
/* 0x2B */ GI_SHIELD_MIRROR,
/* 0x2C */ GI_TUNIC_GORON, // or blue rupee if you have the tunic
/* 0x2D */ GI_TUNIC_ZORA, // or blue rupee if you have the tunic
/* 0x2E */ GI_BOOTS_IRON,
/* 0x2F */ GI_BOOTS_HOVER,
/* 0x30 */ GI_QUIVER_40,
/* 0x31 */ GI_QUIVER_50,
/* 0x32 */ GI_BOMB_BAG_20,
/* 0x33 */ GI_BOMB_BAG_30,
/* 0x34 */ GI_BOMB_BAG_40,
/* 0x35 */ GI_GAUNTLETS_SILVER,
/* 0x36 */ GI_GAUNTLETS_GOLD,
/* 0x37 */ GI_SCALE_SILVER,
/* 0x38 */ GI_SCALE_GOLD,
/* 0x39 */ GI_STONE_OF_AGONY,
/* 0x3A */ GI_GERUDO_CARD,
/* 0x3B */ GI_OCARINA_FAIRY, // uses Ocarina of Time message ID
/* 0x3C */ GI_SEEDS_5,
/* 0x3D */ GI_HEART_CONTAINER,
/* 0x3E */ GI_HEART_PIECE,
/* 0x3F */ GI_KEY_BOSS,
/* 0x40 */ GI_COMPASS,
/* 0x41 */ GI_MAP,
/* 0x42 */ GI_KEY_SMALL,
/* 0x43 */ GI_MAGIC_SMALL, // or blue rupee if not from a drop
/* 0x44 */ GI_MAGIC_LARGE, // or blue rupee if not from a drop
/* 0x45 */ GI_WALLET_ADULT,
/* 0x46 */ GI_WALLET_GIANT,
/* 0x47 */ GI_WEIRD_EGG,
/* 0x48 */ GI_HEART,
/* 0x49 */ GI_ARROWS_SMALL, // amount changes depending on context
/* 0x4A */ GI_ARROWS_MEDIUM, // amount changes depending on context
/* 0x4B */ GI_ARROWS_LARGE, // amount changes depending on context
/* 0x4C */ GI_RUPEE_GREEN,
/* 0x4D */ GI_RUPEE_BLUE,
/* 0x4E */ GI_RUPEE_RED,
/* 0x4F */ GI_HEART_CONTAINER_2,
/* 0x50 */ GI_MILK,
/* 0x51 */ GI_MASK_GORON,
/* 0x52 */ GI_MASK_ZORA,
/* 0x53 */ GI_MASK_GERUDO,
/* 0x54 */ GI_BRACELET,
/* 0x55 */ GI_RUPEE_PURPLE,
/* 0x56 */ GI_RUPEE_GOLD,
/* 0x57 */ GI_SWORD_BGS,
/* 0x58 */ GI_ARROW_FIRE,
/* 0x59 */ GI_ARROW_ICE,
/* 0x5A */ GI_ARROW_LIGHT,
/* 0x5B */ GI_SKULL_TOKEN,
/* 0x5C */ GI_DINS_FIRE,
/* 0x5D */ GI_FARORES_WIND,
/* 0x5E */ GI_NAYRUS_LOVE,
/* 0x5F */ GI_BULLET_BAG_30,
/* 0x60 */ GI_BULLET_BAG_40,
/* 0x61 */ GI_STICKS_5,
/* 0x62 */ GI_STICKS_10,
/* 0x63 */ GI_NUTS_5_2,
/* 0x64 */ GI_NUTS_10,
/* 0x65 */ GI_BOMBS_1,
/* 0x66 */ GI_BOMBS_10,
/* 0x67 */ GI_BOMBS_20,
/* 0x68 */ GI_BOMBS_30,
/* 0x69 */ GI_SEEDS_30,
/* 0x6A */ GI_BOMBCHUS_5,
/* 0x6B */ GI_BOMBCHUS_20,
/* 0x6C */ GI_FISH,
/* 0x6D */ GI_BUGS,
/* 0x6E */ GI_BLUE_FIRE,
/* 0x6F */ GI_POE,
/* 0x70 */ GI_BIG_POE,
/* 0x71 */ GI_DOOR_KEY, // specific to chest minigame
/* 0x72 */ GI_RUPEE_GREEN_LOSE, // specific to chest minigame
/* 0x73 */ GI_RUPEE_BLUE_LOSE, // specific to chest minigame
/* 0x74 */ GI_RUPEE_RED_LOSE, // specific to chest minigame
/* 0x75 */ GI_RUPEE_PURPLE_LOSE, // specific to chest minigame
/* 0x76 */ GI_HEART_PIECE_WIN, // specific to chest minigame
/* 0x77 */ GI_STICK_UPGRADE_20,
/* 0x78 */ GI_STICK_UPGRADE_30,
/* 0x79 */ GI_NUT_UPGRADE_30,
/* 0x7A */ GI_NUT_UPGRADE_40,
/* 0x7B */ GI_BULLET_BAG_50,
/* 0x7C */ GI_ICE_TRAP, // freezes link when opened from a chest
/* 0x7D */ GI_TEXT_0, // no model appears over Link, shows text id 0 (pocket egg)
/* 0x7E */ GI_SWORD_MASTER
} GetItemID;
typedef enum {
/* 0x00 */ EXCH_ITEM_NONE,
/* 0x01 */ EXCH_ITEM_LETTER_ZELDA,
/* 0x02 */ EXCH_ITEM_WEIRD_EGG,
/* 0x03 */ EXCH_ITEM_CHICKEN,
/* 0x04 */ EXCH_ITEM_BEAN,
/* 0x05 */ EXCH_ITEM_POCKET_EGG,
/* 0x06 */ EXCH_ITEM_POCKET_CUCCO,
/* 0x07 */ EXCH_ITEM_COJIRO,
/* 0x08 */ EXCH_ITEM_ODD_MUSHROOM,
/* 0x09 */ EXCH_ITEM_ODD_POTION,
/* 0x0A */ EXCH_ITEM_SAW,
/* 0x0B */ EXCH_ITEM_SWORD_BROKEN,
/* 0x0C */ EXCH_ITEM_PRESCRIPTION,
/* 0x0D */ EXCH_ITEM_FROG,
/* 0x0E */ EXCH_ITEM_EYEDROPS,
/* 0x0F */ EXCH_ITEM_CLAIM_CHECK,
/* 0x10 */ EXCH_ITEM_MASK_SKULL,
/* 0x11 */ EXCH_ITEM_MASK_SPOOKY,
/* 0x12 */ EXCH_ITEM_MASK_KEATON,
/* 0x13 */ EXCH_ITEM_MASK_BUNNY,
/* 0x14 */ EXCH_ITEM_MASK_TRUTH,
/* 0x15 */ EXCH_ITEM_MASK_GORON,
/* 0x16 */ EXCH_ITEM_MASK_ZORA,
/* 0x17 */ EXCH_ITEM_MASK_GERUDO,
/* 0x18 */ EXCH_ITEM_FISH,
/* 0x19 */ EXCH_ITEM_BLUE_FIRE,
/* 0x1A */ EXCH_ITEM_BUG,
/* 0x1B */ EXCH_ITEM_POE,
/* 0x1C */ EXCH_ITEM_BIG_POE,
/* 0x1D */ EXCH_ITEM_LETTER_RUTO,
/* 0x1E */ EXCH_ITEM_MAX
} ExchangeItemID;
#endif //_Z3D_ITEM_H_

38
include/z3D/z3Dvec.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef _Z3DVEC_H_
#define _Z3DVEC_H_
#include <stdint.h>
typedef float f32;
typedef int8_t s8;
typedef uint8_t u8;
typedef int16_t s16;
typedef uint16_t u16;
typedef int32_t s32;
typedef uint32_t u32;
typedef struct {
f32 x, y;
} Vec2f;
typedef struct {
f32 x, y, z;
} Vec3f;
typedef struct {
s16 x, y, z;
} Vec3s;
typedef struct {
s32 x, y, z;
} Vec3i;
typedef struct {
f32 data[3][4];
} nn_math_MTX34;
typedef struct {
f32 data[4][4];
} nn_math_MTX44;
#endif

29
makeAll.sh Normal file
View File

@@ -0,0 +1,29 @@
make clean
make -j REGION=EUR
echo Moving to ./Patch Files/EUR/3DS/code.ips
mv code.ips "./Patch Files/EUR/3DS/code.ips"
make clean
make -j REGION=EUR citra=1
echo Moving to ./Patch Files/EUR/Citra/code.ips
mv code.ips "./Patch Files/EUR/Citra/code.ips"
make clean
make -j REGION=JP
echo Moving to ./Patch Files/JP/3DS/code.ips
mv code.ips "./Patch Files/JP/3DS/code.ips"
make clean
make -j REGION=JP citra=1
echo Moving to ./Patch Files/JP/Citra/code.ips
mv code.ips "./Patch Files/JP/Citra/code.ips"
make clean
make -j REGION=USA citra=1
echo Moving to ./Patch Files/USA/Citra/code.ips
mv code.ips "./Patch Files/USA/Citra/code.ips"
make clean
make -j REGION=USA
echo Moving to ./Patch Files/USA/3DS/code.ips
mv code.ips "./Patch Files/USA/3DS/code.ips"

36
oot.ld Normal file
View File

@@ -0,0 +1,36 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(loader_patch)
SECTIONS
{
. = 0x100000;
.patch_loader 0x100000 : {
*(.patch_loader)
}
.patch_CameraUpdate 0x2D84C8 : {
* (.patch_CameraUpdate)
}
.patch_before_GlobalContext_Update 0x452390 : {
*(.patch_before_GlobalContext_Update)
}
. = 0x4C99A8;
. = ALIGN(4);
.loader : {
*(.loader*)
}
. = 0x005C7000;
.text : {
__text_start = . ;
*(.text)
*(.text.*)
*(.rodata)
*(.data)
*(.bss)
*(COMMON)
__text_end = . ;
}
}

36
oot_e.ld Normal file
View File

@@ -0,0 +1,36 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(loader_patch)
SECTIONS
{
. = 0x100000;
.patch_loader 0x100000 : {
*(.patch_loader)
}
.patch_CameraUpdate 0x2D84C8 : {
* (.patch_CameraUpdate)
}
.patch_before_GlobalContext_Update 0x4523B0 : {
*(.patch_before_GlobalContext_Update)
}
. = 0x4C99A8;
. = ALIGN(4);
.loader : {
*(.loader*)
}
. = 0x005C7000;
.text : {
__text_start = . ;
*(.text)
*(.text.*)
*(.rodata)
*(.data)
*(.bss)
*(COMMON)
__text_end = . ;
}
}

36
oot_j.ld Normal file
View File

@@ -0,0 +1,36 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(loader_patch)
SECTIONS
{
. = 0x100000;
.patch_loader 0x100000 : {
*(.patch_loader)
}
.patch_CameraUpdate 0x2D7FE0 : {
* (.patch_CameraUpdate)
}
.patch_before_GlobalContext_Update 0x452368 : {
*(.patch_before_GlobalContext_Update)
}
. = 0x4C99A8;
. = ALIGN(4);
.loader : {
*(.loader*)
}
. = 0x005C7000;
.text : {
__text_start = . ;
*(.text)
*(.text.*)
*(.rodata)
*(.data)
*(.bss)
*(COMMON)
__text_end = . ;
}
}

44
patch.py Normal file
View File

@@ -0,0 +1,44 @@
import os
import struct
import subprocess
import sys
elf = sys.argv[1]
result = subprocess.run([os.environ["DEVKITARM"] + r'/bin/arm-none-eabi-objdump', '--section-headers', elf], stdout=subprocess.PIPE)
lines = str(result.stdout).split('\\n')
sectionsInfo = [line.split()[1:6] for line in lines if line.split() and line.split()[0].isdigit()]
sections = ((int(sec[2],16), int(sec[4],16), int(sec[1],16)) for sec in sectionsInfo if int(sec[2],16) != 0)
off = lambda vaddr: struct.pack(">I",vaddr - 0x100000)[1:]
sz = lambda size: struct.pack(">H", size)
ips = b'PATCH'
with open(elf, 'rb') as e:
for vaddr, offset, size in sections:
if vaddr >= 0x4CA000 and vaddr < 0x5C7000: #this is not good lol just trying to avoid __GNU_EH_FRAME_HDR section
continue
e.seek(offset, 0)
while size > 65535:
patch = e.read(65535)
# print('{:0x}'.format(vaddr))
ips += off(vaddr)
ips += sz(65535)
ips += patch
vaddr += 65535
offset += 65535
size -= 65535
patch = e.read(size)
if len(patch) != 0:
# print('{:0x}'.format(vaddr))
ips += off(vaddr)
ips += sz(size)
ips += patch
ips += b'EOF'
with open("code.ips", 'wb') as patchFile:
patchFile.write(ips)
print("created basecode.ips\n")

252
src/camera.c Normal file
View File

@@ -0,0 +1,252 @@
#include "z3D/z3D.h"
#include "common.h"
#include "input.h"
#define GyroDrawHUDIcon *(u8*)0x4FC648
s16 pitch = 0, yaw = 0;
f32 dist = 0;
f32 sins(u16 angle) {
// Taylor expansion up to x^7. Use symmetries for larger angles.
if (angle <= 0x4000) {
f32 theta = angle * 0.0000958737992429, theta2 = theta * theta, result = theta;
theta *= theta2 * 0.166666666667;
result -= theta;
theta *= theta2 * 0.05;
result += theta;
theta *= theta2 * 0.0238095238095;
result -= theta;
return result;
} else if (angle <= 0x8000) {
return sins(0x8000 - angle);
}
return -sins(angle - 0x8000);
}
f32 coss(u16 angle) {
return sins(angle + 0x4000);
}
f32 sqrtf(f32 x) {
f32 n = (1 + x) * 0.5;
while (n * n < x * 0.999f || n * n > x * 1.001f) {
n = (n + x / n) * 0.5;
}
return n;
}
f32 distXYZ(Vec3f a, Vec3f b) {
f32 x = a.x - b.x, y = a.y - b.y, z = a.z - b.z;
return sqrtf(x * x + y * y + z * z);
}
f32 distXZ(Vec3f a, Vec3f b) {
f32 x = a.x - b.x, z = a.z - b.z;
return sqrtf(x * x + z * z);
}
s16 Clamp(f32 val) {
if (val >= 0x3000)
return 0x3000;
if (val <= -0x3000)
return -0x3000;
return (s16)val;
}
f32 lerpf(f32 a, f32 b, f32 t) {
return a + (b - a) * t;
}
Vec3f lerpv(Vec3f a, Vec3f b, f32 t) {
a.x += (b.x - a.x) * t;
a.y += (b.y - a.y) * t;
a.z += (b.z - a.z) * t;
return a;
}
// Original function got inlined so recreated with help from decomp
#define CAM_DEG_TO_BINANG(degrees) (s16)(s32)((degrees)*182.04167 + 0.5)
void Camera_UpdateDistortion(Camera* camera) {
static u16 screenPlanePhase = 0;
f32 screenPlanePhaseStep;
f32 xScale;
f32 yScale;
f32 speed;
f32 speedFactor;
f32 scaleFactor;
if (camera->distortionFlags) {
if (camera->distortionFlags & 4) {
// DISTORTION_UNDERWATER_MEDIUM
screenPlanePhaseStep = 170;
xScale = -0.01;
yScale = 0.01;
speed = 0.6;
scaleFactor = camera->waterDistortionTimer / 60.0;
speedFactor = 1;
} else if (camera->distortionFlags & 8) {
// DISTORTION_UNDERWATER_STRONG
screenPlanePhaseStep = -90;
xScale = -0.22;
yScale = 0.12;
speed = 0.1;
scaleFactor = camera->waterDistortionTimer / 80.0;
speedFactor = 1;
} else if (camera->distortionFlags & 2) {
// DISTORTION_UNDERWATER_WEAK
screenPlanePhaseStep = -18.5;
xScale = 0.09;
yScale = 0.09;
speed = 0.08;
scaleFactor = camera->waterYPos - camera->eye.y;
scaleFactor = ((scaleFactor > 150) ? 1 : scaleFactor / 150) * 0.45 + camera->speedRatio * 0.45;
speedFactor = scaleFactor;
} else if (camera->distortionFlags & 1) {
// DISTORTION_HOT_ROOM
screenPlanePhaseStep = 150;
xScale = -0.01;
yScale = 0.01;
speed = 0.6;
scaleFactor = 1;
speedFactor = 1;
} else {
// DISTORTION_UNDERWATER_FISHING
return;
}
screenPlanePhase += CAM_DEG_TO_BINANG(screenPlanePhaseStep * 2 / 3.0);
speedFactor *= 2 / 3.0;
camera->globalCtx->view.distortionScale.x = xScale * scaleFactor * sins(screenPlanePhase) + 1;
camera->globalCtx->view.distortionScale.y = yScale * scaleFactor * coss(screenPlanePhase) + 1;
camera->globalCtx->view.distortionSpeed = speed * speedFactor;
camera->stateFlags |= 0x0040;
} else if (camera->stateFlags & 0x0040) {
camera->globalCtx->view.distortionScale.x = 1;
camera->globalCtx->view.distortionScale.y = 1;
camera->globalCtx->view.distortionSpeed = 1;
camera->stateFlags &= ~0x0040;
}
}
s32 Camera_UpdateHotRoom(Camera* camera) {
camera->distortionFlags &= 0xFFFE;
// Parts of RoomContext, bool value seems new to 3D
if (camera->globalCtx->unk_4C31[1] == 3 || camera->globalCtx->unk_4C31[6]) {
camera->distortionFlags |= 1;
}
return 1;
}
char hd2c(u32 hd) {
if (hd < 10)
return hd + 48;
return hd + 55;
}
u8 Camera_FreeCamEnabled(Camera* camera) {
static u8 freeCamEnabled = 0;
// Keep track of these to smoothly switch to free cam
dist = distXYZ(camera->at, camera->eye);
if (!freeCamEnabled) {
pitch = camera->camDir.x;
yaw = camera->camDir.y;
}
// Deadzone of 30 units
if (rInputCtx.cStick.dx * rInputCtx.cStick.dx + rInputCtx.cStick.dy * rInputCtx.cStick.dy > 900) {
freeCamEnabled = 1;
}
// Titlescreen or cutscene or no player or targeting or first person or cutscene or horse or crawlspace or special
// camera state/setting (MK balcony, chu bowling, static, rotating, hedge maze, GF cells, shops, back alley)
if (!IsInGameOrBossChallenge() || camera != &camera->globalCtx->mainCamera || !camera->player ||
camera->player->stateFlags1 & 0x20938230 || camera->player->stateFlags2 & 0x00040000 || camera->status != 7 ||
camera->setting == 0x14 || camera->setting == 0x15 || camera->setting == 0x19 || camera->setting == 0x1A ||
camera->setting == 0x1B || camera->setting == 0x23 || camera->setting == 0x40 || camera->setting == 0x46) {
freeCamEnabled = 0;
}
return freeCamEnabled;
}
void Camera_FreeCamUpdate(Vec3s* out, Camera* camera) {
Camera_CheckWater(camera); // Changes skybox colour and audio when camera is underwater
Camera_UpdateHotRoom(camera); // Check if in a hot room (mainly for DC load planes)
Camera_UpdateDistortion(camera); // Handle heat/water screen distortions
Camera_UpdateInterface(0); // Remove the black bars at the top/bottom of the screen
GyroDrawHUDIcon = 0; // Remove the icon in the top right indicating motion controls
if (camera->player) {
Vec3f at;
CamColChk eye;
// Fixes some weird camera behaviour when leaving free cam out of bounds
// Causes some issues with pulling objects so don't run while in bounds
if (!camera->player->actor.floorPoly) {
camera->animState = 1;
camera->behaviorFlags = 0;
}
// Aim camera at Link's head. Aim lower when hanging from a ledge as position and model become disjointed
camera->playerPosRot = camera->player->actor.world;
at = eye.pos = camera->playerPosRot.pos;
at.y = eye.pos.y += ((gSaveContext.linkAge) ? 38 : 50) * ((camera->player->stateFlags1 & 0x00002000) ? 0.5 : 1);
// TODO: options for inverted axes and sensitivity
if (rInputCtx.cStick.dx * rInputCtx.cStick.dx + rInputCtx.cStick.dy * rInputCtx.cStick.dy > 900) {
// Invert X input in mirror world
yaw -= rInputCtx.cStick.dx * 8 * ((gSaveContext.masterQuestFlag) ? -1 : 1);
pitch = Clamp(pitch + rInputCtx.cStick.dy * 8);
}
// Set intended camera position
dist = lerpf(dist, ((gSaveContext.linkAge) ? 150 : 200) - 50 * sins(pitch), 0.5);
eye.pos.x -= dist * coss(pitch) * sins(yaw);
eye.pos.y -= dist * sins(pitch);
eye.pos.z -= dist * coss(pitch) * coss(yaw);
// Move intended position in front of collision
Camera_BGCheckInfo(camera, &at, &eye);
// Move actual camera positions towards intended positions
camera->globalCtx->view.at = camera->at = lerpv(camera->at, at, 0.3);
camera->globalCtx->view.eye = camera->eye = camera->eyeNext = lerpv(camera->eye, eye.pos, 0.3);
// Apply quake offsets
ShakeInfo camShake;
if (Quake_Update(camera, &camShake)) {
camera->globalCtx->view.at.x += camShake.atOffset.x;
camera->globalCtx->view.at.y += camShake.atOffset.y;
camera->globalCtx->view.at.z += camShake.atOffset.z;
camera->globalCtx->view.eye.x += camShake.eyeOffset.x;
camera->globalCtx->view.eye.y += camShake.eyeOffset.y;
camera->globalCtx->view.eye.z += camShake.eyeOffset.z;
}
// Set up vector
camera->globalCtx->view.up.x = 0;
camera->globalCtx->view.up.y = 1;
camera->globalCtx->view.up.z = 0;
// Set the direction of control stick inputs
out->x = camera->inputDir.x = camera->camDir.x = pitch;
out->y = camera->inputDir.y = camera->camDir.y = yaw;
out->z = camera->inputDir.z = camera->camDir.z = 0;
// Update camera setting
// TODO: use correct method for finding floor poly
s16 newCamDataIdx = Camera_GetCamDataId(&camera->globalCtx->colCtx, camera->player->actor.floorPoly, 0x32);
s16 newSetting = camera->globalCtx->colCtx.stat.colHeader->camDataList[newCamDataIdx].setting;
if (newCamDataIdx != -1 && newSetting && (newSetting != 0x35 || gSaveContext.linkAge)) {
camera->camDataIdx = newCamDataIdx;
if (newSetting != camera->setting) {
camera->prevSetting = camera->setting;
camera->setting = newSetting;
}
}
}
return;
}

31
src/hooks.s Normal file
View File

@@ -0,0 +1,31 @@
.arm
.text
.global hook_before_GlobalContext_Update
hook_before_GlobalContext_Update:
push {r0-r12, lr}
bl before_GlobalContext_Update
pop {r0-r12, lr}
cpy r7,r0
bx lr
.section .loader
.global hook_into_loader
hook_into_loader:
push {r0-r12, lr}
bl loader_main
pop {r0-r12, lr}
bl 0x100028
b 0x100004
.global hook_CameraUpdate
hook_CameraUpdate:
push {r0-r12, lr}
cpy r0,r1
bl Camera_FreeCamEnabled
cmp r0,#0x0
pop {r0-r12, lr}
cpyeq r6,r0
bxeq lr
bl Camera_FreeCamUpdate
ldmia sp!,{r4-r11,pc}

63
src/includes/common.c Normal file
View File

@@ -0,0 +1,63 @@
#include "common.h"
#include "3ds/svc.h"
#include "lib/printf.h"
s8 BitCompare(u32 value1, u32 value2, u8 bit) {
if ((value1 & (1 << bit)) > (value2 & (1 << bit))) {
return 1;
} else if ((value2 & (1 << bit)) > (value1 & (1 << bit))) {
return -1;
}
return 0;
}
// From section 5 of https://www.cs.ubc.ca/~rbridson/docs/schechter-sca08-turbulence.pdf
u32 Hash(u32 state) {
state ^= 0xDC3A653D;
state *= 0xE1C88647;
state ^= state >> 16;
state *= 0xE1C88647;
state ^= state >> 16;
state *= 0xE1C88647;
return state;
}
u8 And(u32 seed, u8 start, u8 end) {
u8 value = 1;
for (u8 i = start; i < end && value; i++) {
value &= seed >> i;
}
return value;
}
u8 Bias(u32 seed) {
u8 value = (seed & 0x00000007);
value |= And(seed, 3, 5) << 3;
value |= And(seed, 5, 7) << 4;
value |= And(seed, 7, 11) << 5;
value |= And(seed, 11, 16) << 6;
return value;
}
u8 IsInGame(void) {
return IsInGameOrBossChallenge() && !BossChallenge_IsActive();
}
u8 IsInGameOrBossChallenge(void) {
s32 entr = gSaveContext.entranceIndex;
s32 mode = gSaveContext.gameMode;
return mode == 0 || (mode == 1 && (gSaveContext.cutsceneIndex < 0xFFF0 ||
(entr != 0x0629 && entr != 0x0147 && entr != 0x00A0 && entr != 0x008D)));
}
void CitraPrint(const char* message, ...) {
va_list args;
va_start(args, message);
char buf[128];
int length = vsnprintf(buf, 128, message, args);
svcOutputDebugString(buf, length);
va_end(args);
}

101
src/includes/csvc.s Normal file
View File

@@ -0,0 +1,101 @@
@ 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
svc 0xA0
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 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

66
src/includes/env.c Normal file
View File

@@ -0,0 +1,66 @@
/* Source: https://github.com/devkitPro/libctru/blob/6360f4bdb1ca5f8131ffc92640c1dd16afb63083/libctru/source/env.c */
#include "3ds/types.h"
#include "3ds/svc.h"
#include "3ds/env.h"
/*
The homebrew loader can choose to supply a list of service handles that have
been "stolen" from other processes that have been compromised. This allows us
to access services that are normally restricted from the current process.
For every service requested by the application, we shall first check if the
list given to us contains the requested service and if so use it. If we don't
find the service in that list, we ask the service manager and hope for the
best.
*/
typedef struct {
u32 num;
struct {
char name[8];
Handle handle;
} services[];
} service_list_t;
static int __name_cmp(const char* a, const char* b) {
u32 i;
for (i = 0; i < 8; i++) {
if (a[i] != b[i])
return 1;
if (a[i] == '\0')
return 0;
}
return 0;
}
Handle __attribute__((weak)) envGetHandle(const char* name) {
if (__service_ptr == NULL)
return 0;
service_list_t* service_list = (service_list_t*)__service_ptr;
u32 i, num = service_list->num;
for (i = 0; i < num; i++) {
if (__name_cmp(service_list->services[i].name, name) == 0)
return service_list->services[i].handle;
}
return 0;
}
void __attribute__((weak)) envDestroyHandles(void) {
if (__service_ptr == NULL)
return;
service_list_t* service_list = (service_list_t*)__service_ptr;
u32 i, num = service_list->num;
for (i = 0; i < num; i++)
svcCloseHandle(service_list->services[i].handle);
service_list->num = 0;
}

85
src/includes/input.c Normal file
View File

@@ -0,0 +1,85 @@
#include "input.h"
#include "z3D/z3D.h"
#include "hid.h"
#include "3ds/svc.h"
#include "utils.h"
#include "3ds/types.h"
#define HID_PAD (real_hid.pad.pads[real_hid.pad.index].curr.val)
InputContext rInputCtx;
void Input_Update(void) {
rInputCtx.cur.val = real_hid.pad.pads[real_hid.pad.index].curr.val;
rInputCtx.pressed.val = (rInputCtx.cur.val) & (~rInputCtx.old.val);
rInputCtx.up.val = (~rInputCtx.cur.val) & (rInputCtx.old.val);
rInputCtx.old.val = rInputCtx.cur.val;
rInputCtx.touchX = real_hid.touch.touches[real_hid.touch.index].touch.x;
rInputCtx.touchY = real_hid.touch.touches[real_hid.touch.index].touch.y;
rInputCtx.touchPressed = real_hid.touch.touches[real_hid.touch.index].updated && !rInputCtx.touchHeld;
rInputCtx.touchHeld = real_hid.touch.touches[real_hid.touch.index].updated;
irrstScanInput();
irrstCstickRead(&(rInputCtx.cStick));
}
u32 buttonCheck(u32 key) {
for (u32 i = 0x26000; i > 0; i--) {
if (key != real_hid.pad.pads[real_hid.pad.index].curr.val)
return 0;
}
return 1;
}
u32 Input_WaitWithTimeout(u32 msec, u32 closingButton) {
u32 pressedKey = 0;
u32 key = 0;
u32 n = 0;
u32 startingButtonState = HID_PAD;
// Wait for no keys to be pressed
while (HID_PAD && (msec == 0 || n <= msec)) {
svcSleepThread(1 * 1000 * 1000LL);
n++;
// If the player presses the closing button while still holding other buttons, the menu closes (useful for
// buffering);
u32 tempButtons = HID_PAD;
if (tempButtons != startingButtonState && buttonCheck(tempButtons)) {
if (tempButtons & closingButton) {
break;
} else {
startingButtonState = tempButtons;
}
}
}
if (msec != 0 && n >= msec) {
return 0;
}
do {
// Wait for a key to be pressed
while (!HID_PAD && (msec == 0 || n < msec)) {
svcSleepThread(1 * 1000 * 1000LL);
n++;
}
if (msec != 0 && n >= msec) {
return 0;
}
key = HID_PAD;
// Make sure it's pressed
pressedKey = buttonCheck(key);
} while (!pressedKey);
return key;
}
u32 Input_Wait(void) {
return Input_WaitWithTimeout(0, 0);
}

181
src/includes/irrst.c Normal file
View File

@@ -0,0 +1,181 @@
/*
_irrst.c - C-stick, ZL/ZR
Edited from
https://github.com/devkitPro/libctru/blob/b18f04d88739283f6ffb55fe5ea77c73796cf61b/libctru/source/services/irrst.c
*/
#include <stdlib.h>
#include <string.h>
#include "3ds/types.h"
#include "3ds/result.h"
#include "3ds/svc.h"
#include "3ds/srv.h"
#include "3ds/allocator/mappable.h"
#include "3ds/synchronization.h"
#include "3ds/services/irrst.h"
#include "3ds/ipc.h"
#include "3ds/env.h"
Handle irrstHandle;
Handle irrstMemHandle;
Handle irrstEvent;
vu32* irrstSharedMem;
static u32 kHeld;
static circlePosition csPos;
static int irrstRefCount;
Result irrstInit(void) {
if (AtomicPostIncrement(&irrstRefCount))
return 0;
mappableInit(OS_MAP_AREA_BEGIN, OS_MAP_AREA_END);
Result ret = 0;
// Request service.
if (R_FAILED(ret = srvGetServiceHandle(&irrstHandle, "ir:rst")))
goto cleanup0;
// Get sharedmem handle.
if (R_FAILED(ret = IRRST_GetHandles(&irrstMemHandle, &irrstEvent)))
goto cleanup1;
// Initialize ir:rst
if (envGetHandle("ir:rst") == 0)
ret = IRRST_Initialize(10, 0);
// Map ir:rst shared memory.
irrstSharedMem = (vu32*)mappableAlloc(0x98);
if (!irrstSharedMem) {
ret = -1;
goto cleanup1;
}
if (R_FAILED(ret = svcMapMemoryBlock(irrstMemHandle, (u32)irrstSharedMem, MEMPERM_READ, MEMPERM_DONTCARE)))
goto cleanup2;
return 0;
cleanup2:
svcCloseHandle(irrstMemHandle);
if (irrstSharedMem != NULL) {
mappableFree((void*)irrstSharedMem);
irrstSharedMem = NULL;
}
cleanup1:
svcCloseHandle(irrstHandle);
cleanup0:
AtomicDecrement(&irrstRefCount);
return ret;
}
void irrstExit(void) {
if (AtomicDecrement(&irrstRefCount))
return;
// Reset internal state.
kHeld = 0;
svcCloseHandle(irrstEvent);
// Unmap ir:rst sharedmem and close handles.
svcUnmapMemoryBlock(irrstMemHandle, (u32)irrstSharedMem);
if (envGetHandle("ir:rst") == 0)
IRRST_Shutdown();
svcCloseHandle(irrstMemHandle);
svcCloseHandle(irrstHandle);
if (irrstSharedMem != NULL) {
mappableFree((void*)irrstSharedMem);
irrstSharedMem = NULL;
}
}
void irrstWaitForEvent(bool nextEvent) {
if (nextEvent)
svcClearEvent(irrstEvent);
svcWaitSynchronization(irrstEvent, U64_MAX);
if (!nextEvent)
svcClearEvent(irrstEvent);
}
u32 irrstCheckSectionUpdateTime(vu32* sharedmem_section, u32 id) {
s64 tick0 = 0, tick1 = 0;
if (id == 0) {
tick0 = *((u64*)&sharedmem_section[0]);
tick1 = *((u64*)&sharedmem_section[2]);
if (tick0 == tick1 || tick0 < 0 || tick1 < 0)
return 1;
}
return 0;
}
void irrstScanInput(void) {
if (irrstRefCount == 0)
return;
u32 Id = 0;
kHeld = 0;
memset(&csPos, 0, sizeof(circlePosition));
Id = irrstSharedMem[4]; // PAD / circle-pad
if (Id > 7)
Id = 7;
if (irrstCheckSectionUpdateTime(irrstSharedMem, Id) == 0) {
kHeld = irrstSharedMem[6 + Id * 4];
csPos = *(circlePosition*)&irrstSharedMem[6 + Id * 4 + 3];
}
}
u32 irrstKeysHeld(void) {
if (irrstRefCount > 0)
return kHeld;
return 0;
}
void irrstCstickRead(circlePosition* pos) {
if (pos)
*pos = csPos;
}
Result IRRST_GetHandles(Handle* outMemHandle, Handle* outEventHandle) {
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x1, 0, 0); // 0x10000
Result ret = 0;
if (R_FAILED(ret = svcSendSyncRequest(irrstHandle)))
return ret;
if (outMemHandle)
*outMemHandle = cmdbuf[3];
if (outEventHandle)
*outEventHandle = cmdbuf[4];
return cmdbuf[1];
}
Result IRRST_Initialize(u32 unk1, u8 unk2) {
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x2, 2, 0); // 0x20080
cmdbuf[1] = unk1;
cmdbuf[2] = unk2;
Result ret = 0;
if (R_FAILED(ret = svcSendSyncRequest(irrstHandle)))
return ret;
return cmdbuf[1];
}
Result IRRST_Shutdown(void) {
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x3, 0, 0); // 0x30000
Result ret = 0;
if (R_FAILED(ret = svcSendSyncRequest(irrstHandle)))
return ret;
return cmdbuf[1];
}

43
src/includes/loader.c Normal file
View File

@@ -0,0 +1,43 @@
/*
* This file is a modified version of a file originally written by RicBent
* for the program Magikoopa.
*/
#include "newcodeinfo.h"
#include "3ds/types.h"
#include "loader.h"
Result svcOpenProcess(Handle* process, u32 processId);
Result svcGetProcessId(u32* out, Handle handle);
void svcBreak(u32 breakReason);
void loader_main (void)
__attribute__((section (".loader")));
void loader_main(void)
{
Result res;
u32 address = NEWCODE_OFFSET;
u32 neededMemory = (NEWCODE_SIZE + 0xFFF) & ~0xFFF; //rounding up
res = svcControlProcessMemory(getCurrentProcessHandle(), address, address, neededMemory, 6, 7);
if (res < 0)
svcBreak(1);
}
Handle getCurrentProcessHandle(void)
{
Handle handle = 0;
u32 currentPid = 0;
Result res;
svcGetProcessId(&currentPid, 0xffff8001);
res = svcOpenProcess(&handle, currentPid);
if (res != 0)
return 0;
return handle;
}

57
src/includes/mappable.c Normal file
View File

@@ -0,0 +1,57 @@
/*
Source
https://github.com/devkitPro/libctru/blob/b18f04d88739283f6ffb55fe5ea77c73796cf61b/libctru/source/allocator/mappable.c
*/
#include "3ds/allocator/mappable.h"
#include "3ds/svc.h"
#include "3ds/result.h"
static u32 minAddr, maxAddr, currentAddr;
void mappableInit(u32 addrMin, u32 addrMax) {
minAddr = addrMin;
maxAddr = addrMax;
currentAddr = minAddr;
}
static u32 mappableFindAddressWithin(u32 start, u32 end, u32 size) {
MemInfo info;
PageInfo pgInfo;
u32 addr = start;
while (addr >= start && (addr + size) < end && (addr + size) >= addr) {
if (R_FAILED(svcQueryMemory(&info, &pgInfo, addr)))
return 0;
if (info.state == MEMSTATE_FREE) {
u32 sz = info.size - (addr - info.base_addr); // a free block might cover all the memory, etc.
if (sz >= size)
return addr;
}
addr = info.base_addr + info.size;
}
return 0;
}
void* mappableAlloc(size_t size) {
// Round up, can only allocate in page units
size = (size + 0xFFF) & ~0xFFF;
u32 addr = mappableFindAddressWithin(currentAddr, maxAddr, size);
if (addr == 0) {
// Need to rollover (maybe)
addr = mappableFindAddressWithin(minAddr, currentAddr, size);
if (addr == 0)
return NULL;
}
currentAddr = addr + size >= maxAddr ? minAddr : addr + size;
return (void*)addr;
}
void mappableFree(void* mem) {
(void)mem;
}

801
src/includes/printf.c Normal file
View File

@@ -0,0 +1,801 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// 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.
//
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
// embedded systems with a very limited resources. These routines are thread
// safe and reentrant!
// Use this instead of the bloated standard/newlib printf cause these use
// malloc for printf (and may not be thread safe).
//
///////////////////////////////////////////////////////////////////////////////
#include <stdbool.h>
#include <stdint.h>
#include "lib/printf.h"
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
// printf_config.h header file
// default: undefined
#ifdef PRINTF_INCLUDE_CONFIG_H
#include "printf_config.h"
#endif
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
// numeric number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_NTOA_BUFFER_SIZE
#define PRINTF_NTOA_BUFFER_SIZE 32U
#endif
// 'ftoa' conversion buffer size, this must be big enough to hold one converted
// float number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_FTOA_BUFFER_SIZE
#define PRINTF_FTOA_BUFFER_SIZE 32U
#endif
// support for the floating point type (%f)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
#define PRINTF_SUPPORT_FLOAT
#endif
// support for exponential floating point notation (%e/%g)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
#define PRINTF_SUPPORT_EXPONENTIAL
#endif
// define the default floating point precision
// default: 6 digits
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
#endif
// define the largest float suitable to print with %f
// default: 1e9
#ifndef PRINTF_MAX_FLOAT
#define PRINTF_MAX_FLOAT 1e9
#endif
///////////////////////////////////////////////////////////////////////////////
// internal flag definitions
#define FLAGS_ZEROPAD (1U << 0U)
#define FLAGS_LEFT (1U << 1U)
#define FLAGS_PLUS (1U << 2U)
#define FLAGS_SPACE (1U << 3U)
#define FLAGS_HASH (1U << 4U)
#define FLAGS_UPPERCASE (1U << 5U)
#define FLAGS_CHAR (1U << 6U)
#define FLAGS_SHORT (1U << 7U)
#define FLAGS_LONG (1U << 8U)
#define FLAGS_PRECISION (1U << 10U)
#define FLAGS_ADAPT_EXP (1U << 11U)
// import float.h for DBL_MAX
#if defined(PRINTF_SUPPORT_FLOAT)
#include <float.h>
#endif
// output function type
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
// wrapper (used as buffer) for output function type
typedef struct {
void (*fct)(char character, void* arg);
void* arg;
} out_fct_wrap_type;
// internal buffer output
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) {
if (idx < maxlen) {
((char*)buffer)[idx] = character;
}
}
// internal null output
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) {
(void)character;
(void)buffer;
(void)idx;
(void)maxlen;
}
// internal output function wrapper
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) {
(void)idx;
(void)maxlen;
if (character) {
// buffer is the output fct pointer
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
}
}
// internal secure strlen
// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
static inline unsigned int _strnlen_s(const char* str, size_t maxsize) {
const char* s;
for (s = str; *s && maxsize--; ++s)
;
return (unsigned int)(s - str);
}
// internal test if char is a digit (0-9)
// \return true if char is a digit
static inline bool _is_digit(char ch) {
return (ch >= '0') && (ch <= '9');
}
// internal ASCII string to unsigned int conversion
static unsigned int _atoi(const char** str) {
unsigned int i = 0U;
while (_is_digit(**str)) {
i = i * 10U + (unsigned int)(*((*str)++) - '0');
}
return i;
}
// output the specified string in reverse, taking care of any zero-padding
static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len,
unsigned int width, unsigned int flags) {
const size_t start_idx = idx;
// pad spaces up to given width
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
for (size_t i = len; i < width; i++) {
out(' ', buffer, idx++, maxlen);
}
}
// reverse string
while (len) {
out(buf[--len], buffer, idx++, maxlen);
}
// append pad spaces up to given width
if (flags & FLAGS_LEFT) {
while (idx - start_idx < width) {
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
// internal itoa format
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len,
bool negative, bool hex, unsigned int prec, unsigned int width, unsigned int flags) {
// pad leading zeros
if (!(flags & FLAGS_LEFT)) {
if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
// handle hash
if (flags & FLAGS_HASH) {
if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
len--;
if (len && hex) {
len--;
}
}
if (hex && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'x';
} else if (hex && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'X';
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = '0';
}
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
// internal itoa for 'long' type
static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative,
bool hex, unsigned int prec, unsigned int width, unsigned int flags) {
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
if (hex) {
const char digit = (char)(value % 16U);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= 16U;
} else {
const char digit = (char)(value % 10U);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= 10U;
}
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, hex, prec, width, flags);
}
#if defined(PRINTF_SUPPORT_FLOAT)
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
unsigned int width, unsigned int flags);
#endif
// internal ftoa for fixed decimal floating point
static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
unsigned int width, unsigned int flags) {
char buf[PRINTF_FTOA_BUFFER_SIZE];
size_t len = 0U;
double diff = 0.0;
// powers of 10
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
// test for special values
if (value != value)
return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
if (value < -DBL_MAX)
return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
if (value > DBL_MAX)
return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U,
width, flags);
// test for very large values
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing
// your buffers == bad
if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
#else
return 0U;
#endif
}
// test for negative
bool negative = false;
if (value < 0) {
negative = true;
value = 0 - value;
}
// set default precision, if not set explicitly
if (!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
buf[len++] = '0';
prec--;
}
int whole = (int)value;
double tmp = (value - whole) * pow10[prec];
unsigned long frac = (unsigned long)tmp;
diff = tmp - frac;
if (diff > 0.5) {
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= pow10[prec]) {
frac = 0;
++whole;
}
} else if (diff < 0.5) {
} else if ((frac == 0U) || (frac & 1U)) {
// if halfway, round up if odd OR if last digit is 0
++frac;
}
if (prec == 0U) {
diff = value - (double)whole;
if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
} else {
unsigned int count = prec;
// now do fractional part, as an unsigned number
while (len < PRINTF_FTOA_BUFFER_SIZE) {
--count;
buf[len++] = (char)(48U + (frac % 10U));
if (!(frac /= 10U)) {
break;
}
}
// add extra 0s
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
buf[len++] = '0';
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
// add decimal
buf[len++] = '.';
}
}
// do whole part, number is reversed
while (len < PRINTF_FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
if (!(whole /= 10)) {
break;
}
}
// pad leading zeros
if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
unsigned int width, unsigned int flags) {
// check for NaN and special values
if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
}
// determine the sign
const bool negative = value < 0;
if (negative) {
value = -value;
}
// default precision
if (!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// determine the decimal exponent
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
union {
uint64_t U;
double F;
} conv;
conv.F = value;
int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
// now we want to compute 10^expval but we want to be sure it won't overflow
exp2 = (int)(expval * 3.321928094887362 + 0.5);
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
const double z2 = z * z;
conv.U = (uint64_t)(exp2 + 1023) << 52U;
// compute exp(z) using continued fractions, see
// https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
if (value < conv.F) {
expval--;
conv.F /= 10;
}
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
// in "%g" mode, "prec" is the number of *significant figures* not decimals
if (flags & FLAGS_ADAPT_EXP) {
// do we want to fall-back to "%f" mode?
if ((value >= 1e-4) && (value < 1e6)) {
if ((int)prec > expval) {
prec = (unsigned)((int)prec - expval - 1);
} else {
prec = 0;
}
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
// no characters in exponent
minwidth = 0U;
expval = 0;
} else {
// we use one sigfig for the whole part
if ((prec > 0) && (flags & FLAGS_PRECISION)) {
--prec;
}
}
}
// will everything fit?
unsigned int fwidth = width;
if (width > minwidth) {
// we didn't fall-back so subtract the characters required for the exponent
fwidth -= minwidth;
} else {
// not enough characters, so go back to default sizing
fwidth = 0U;
}
if ((flags & FLAGS_LEFT) && minwidth) {
// if we're padding on the right, DON'T pad the floating part
fwidth = 0U;
}
// rescale the float value
if (expval) {
value /= conv.F;
}
// output the floating part
const size_t start_idx = idx;
idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
// output the exponent part
if (minwidth) {
// output the exponential symbol
out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
// output the exponent value
idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, false, 0, minwidth - 1,
FLAGS_ZEROPAD | FLAGS_PLUS);
// might need to right-pad spaces
if (flags & FLAGS_LEFT) {
while (idx - start_idx < width)
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
// internal vsnprintf
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) {
unsigned int flags, width, precision, n;
size_t idx = 0U;
if (!buffer) {
// use null output function
out = _out_null;
}
while (*format) {
// format specifier? %[flags][width][.precision][length]
if (*format != '%') {
// no
out(*format, buffer, idx++, maxlen);
format++;
continue;
} else {
// yes, evaluate it
format++;
}
// evaluate flags
flags = 0U;
do {
switch (*format) {
case '0':
flags |= FLAGS_ZEROPAD;
format++;
n = 1U;
break;
case '-':
flags |= FLAGS_LEFT;
format++;
n = 1U;
break;
case '+':
flags |= FLAGS_PLUS;
format++;
n = 1U;
break;
case ' ':
flags |= FLAGS_SPACE;
format++;
n = 1U;
break;
case '#':
flags |= FLAGS_HASH;
format++;
n = 1U;
break;
default:
n = 0U;
break;
}
} while (n);
// evaluate width field
width = 0U;
if (_is_digit(*format)) {
width = _atoi(&format);
} else if (*format == '*') {
const int w = va_arg(va, int);
if (w < 0) {
flags |= FLAGS_LEFT; // reverse padding
width = (unsigned int)-w;
} else {
width = (unsigned int)w;
}
format++;
}
// evaluate precision field
precision = 0U;
if (*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if (_is_digit(*format)) {
precision = _atoi(&format);
} else if (*format == '*') {
const int prec = (int)va_arg(va, int);
precision = prec > 0 ? (unsigned int)prec : 0U;
format++;
}
}
// evaluate length field
switch (*format) {
case 'h':
flags |= FLAGS_SHORT;
format++;
if (*format == 'h') {
flags |= FLAGS_CHAR;
format++;
}
break;
default:
break;
}
// evaluate specifier
switch (*format) {
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
case 'b': {
// set the base
// unsigned int base;
bool hex;
if (*format == 'x' || *format == 'X') {
hex = true;
}
// else if (*format == 'o') {
// base = 8U;
// }
// else if (*format == 'b') {
// base = 2U;
// }
else {
hex = false;
flags &= ~FLAGS_HASH; // no hash for dec format
}
// uppercase
if (*format == 'X') {
flags |= FLAGS_UPPERCASE;
}
// no plus or space flag for u, x, X, o, b
if ((*format != 'i') && (*format != 'd')) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
// ignore '0' flag when precision is given
if (flags & FLAGS_PRECISION) {
flags &= ~FLAGS_ZEROPAD;
}
// convert the integer
if ((*format == 'i') || (*format == 'd')) {
// signed
if (flags & FLAGS_LONG) {
const long value = va_arg(va, long);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value),
value < 0, hex, precision, width, flags);
} else {
const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int)
: (flags & FLAGS_SHORT) ? (short int)va_arg(va, int)
: va_arg(va, int);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value),
value < 0, hex, precision, width, flags);
}
} else {
// unsigned
if (flags & FLAGS_LONG) {
idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, hex, precision,
width, flags);
} else {
const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int)
: (flags & FLAGS_SHORT)
? (unsigned short int)va_arg(va, unsigned int)
: va_arg(va, unsigned int);
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, hex, precision, width, flags);
}
}
format++;
break;
}
#if defined(PRINTF_SUPPORT_FLOAT)
case 'f':
case 'F':
if (*format == 'F')
flags |= FLAGS_UPPERCASE;
idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
format++;
break;
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
case 'e':
case 'E':
case 'g':
case 'G':
if ((*format == 'g') || (*format == 'G'))
flags |= FLAGS_ADAPT_EXP;
if ((*format == 'E') || (*format == 'G'))
flags |= FLAGS_UPPERCASE;
idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
format++;
break;
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
case 'c': {
unsigned int l = 1U;
// pre padding
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// char output
out((char)va_arg(va, int), buffer, idx++, maxlen);
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 's': {
const char* p = va_arg(va, char*);
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
// pre padding
if (flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// string output
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), buffer, idx++, maxlen);
}
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 'p': {
width = sizeof(void*) * 2U;
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, true,
precision, width, flags);
format++;
break;
}
case '%':
out('%', buffer, idx++, maxlen);
format++;
break;
default:
out(*format, buffer, idx++, maxlen);
format++;
break;
}
}
// termination
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return (int)idx;
}
///////////////////////////////////////////////////////////////////////////////
int sprintf_(char* buffer, const char* format, ...) {
va_list va;
va_start(va, format);
const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
va_end(va);
return ret;
}
int snprintf_(char* buffer, size_t count, const char* format, ...) {
va_list va;
va_start(va, format);
const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
va_end(va);
return ret;
}
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) {
return _vsnprintf(_out_buffer, buffer, count, format, va);
}
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) {
va_list va;
va_start(va, format);
const out_fct_wrap_type out_fct_wrap = { out, arg };
const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
va_end(va);
return ret;
}

322
src/includes/srv.c Normal file
View File

@@ -0,0 +1,322 @@
/*
srv.c _ Service manager.
*/
#include <string.h>
#include "3ds/types.h"
#include "3ds/result.h"
#include "3ds/env.h"
#include "3ds/srv.h"
#include "3ds/svc.h"
#include "3ds/ipc.h"
#include "3ds/synchronization.h"
#include "3ds/os.h"
#include "3ds/services/srvpm.h"
static Handle srvHandle;
static int srvRefCount;
// static bool srvGetBlockingPolicy(void)
// {
// ThreadVars *tv = getThreadVars();
// return tv->magic == THREADVARS_MAGIC && tv->srv_blocking_policy;
// }
Result srvInit(void) {
Result rc = 0;
if (AtomicPostIncrement(&srvRefCount))
return 0;
if (osGetFirmVersion() < SYSTEM_VERSION(2, 39, 4) && *srvPmGetSessionHandle() != 0)
rc = svcDuplicateHandle(&srvHandle,
*srvPmGetSessionHandle()); // Prior to system version 7.0 srv:pm was a superset of srv:
else
rc = svcConnectToPort(&srvHandle, "srv:");
if (R_FAILED(rc))
goto end;
rc = srvRegisterClient();
end:
if (R_FAILED(rc))
srvExit();
return rc;
}
void srvExit(void) {
if (AtomicDecrement(&srvRefCount))
return;
if (srvHandle != 0)
svcCloseHandle(srvHandle);
srvHandle = 0;
}
// void srvSetBlockingPolicy(bool nonBlocking)
// {
// ThreadVars *tv = getThreadVars();
// tv->srv_blocking_policy = nonBlocking;
// }
Handle* srvGetSessionHandle(void) {
return &srvHandle;
}
Result srvGetServiceHandle(Handle* out, const char* name) {
/* Look in service-list given to us by loader. If we find find a match,
we return it. */
Handle h = envGetHandle(name);
if (h != 0) {
return svcDuplicateHandle(out, h);
}
/* Normal request to service manager. */
return srvGetServiceHandleDirect(out, name);
}
Result srvRegisterClient(void) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x1, 0, 2); // 0x10002
cmdbuf[1] = IPC_Desc_CurProcessId();
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
return cmdbuf[1];
}
Result srvEnableNotification(Handle* semaphoreOut) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x2, 0, 0);
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
if (semaphoreOut)
*semaphoreOut = cmdbuf[3];
return cmdbuf[1];
}
Result srvRegisterService(Handle* out, const char* name, int maxSessions) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x3, 4, 0); // 0x30100
strncpy((char*)&cmdbuf[1], name, 8);
cmdbuf[3] = strnlen(name, 8);
cmdbuf[4] = maxSessions;
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
if (out)
*out = cmdbuf[3];
return cmdbuf[1];
}
Result srvUnregisterService(const char* name) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x4, 3, 0); // 0x400C0
strncpy((char*)&cmdbuf[1], name, 8);
cmdbuf[3] = strnlen(name, 8);
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
return cmdbuf[1];
}
Result srvGetServiceHandleDirect(Handle* out, const char* name) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x5, 4, 0); // 0x50100
strncpy((char*)&cmdbuf[1], name, 8);
cmdbuf[3] = strnlen(name, 8);
// cmdbuf[4] = (u32)srvGetBlockingPolicy(); // per-thread setting, default is blocking
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
if (out)
*out = cmdbuf[3];
return cmdbuf[1];
}
Result srvRegisterPort(const char* name, Handle clientHandle) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x6, 3, 2); // 0x600C2
strncpy((char*)&cmdbuf[1], name, 8);
cmdbuf[3] = strnlen(name, 8);
cmdbuf[4] = IPC_Desc_SharedHandles(1);
cmdbuf[5] = clientHandle;
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
return cmdbuf[1];
}
Result srvUnregisterPort(const char* name) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x7, 3, 0); // 0x700C0
strncpy((char*)&cmdbuf[1], name, 8);
cmdbuf[3] = strnlen(name, 8);
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
return cmdbuf[1];
}
Result srvGetPort(Handle* out, const char* name) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x8, 4, 0); // 0x80100
strncpy((char*)&cmdbuf[1], name, 8);
cmdbuf[3] = strnlen(name, 8);
cmdbuf[4] = 0x0;
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
if (out)
*out = cmdbuf[3];
return cmdbuf[1];
}
Result srvWaitForPortRegistered(const char* name) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x8, 4, 0); // 0x80100
strncpy((char*)&cmdbuf[1], name, 8);
cmdbuf[3] = strnlen(name, 8);
cmdbuf[4] = 0x1;
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
return cmdbuf[1];
}
Result srvSubscribe(u32 notificationId) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x9, 1, 0); // 0x90040
cmdbuf[1] = notificationId;
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
return cmdbuf[1];
}
Result srvUnsubscribe(u32 notificationId) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0xA, 1, 0); // 0xA0040
cmdbuf[1] = notificationId;
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
return cmdbuf[1];
}
Result srvReceiveNotification(u32* notificationIdOut) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0xB, 0, 0); // 0xB0000
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
if (notificationIdOut)
*notificationIdOut = cmdbuf[2];
return cmdbuf[1];
}
Result srvPublishToSubscriber(u32 notificationId, u32 flags) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0xC, 2, 0); // 0xC0080
cmdbuf[1] = notificationId;
cmdbuf[2] = flags;
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
return cmdbuf[1];
}
Result srvPublishAndGetSubscriber(u32* processIdCountOut, u32* processIdsOut, u32 notificationId) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0xD, 1, 0); // 0xD0040
cmdbuf[1] = notificationId;
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
if (processIdCountOut)
*processIdCountOut = cmdbuf[2];
if (processIdsOut)
memcpy(processIdsOut, &cmdbuf[3], cmdbuf[2] * sizeof(u32));
return cmdbuf[1];
}
Result srvIsServiceRegistered(bool* registeredOut, const char* name) {
Result rc = 0;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0xE, 3, 0); // 0xE00C0
strncpy((char*)&cmdbuf[1], name, 8);
cmdbuf[3] = strnlen(name, 8);
if (R_FAILED(rc = svcSendSyncRequest(srvHandle)))
return rc;
if (registeredOut)
*registeredOut = cmdbuf[2] & 0xFF;
return cmdbuf[1];
}
Result srvIsPortRegistered(bool* registeredOut, const char* name) {
Handle port;
Result rc = srvGetPort(&port, name);
if (rc == 0xD8801BFA) {
if (registeredOut)
*registeredOut = false;
return 0;
} else if (R_SUCCEEDED(rc)) {
if (registeredOut)
*registeredOut = true;
svcCloseHandle(port);
}
return rc;
}

104
src/includes/srvpm.c Normal file
View File

@@ -0,0 +1,104 @@
#include <stdlib.h>
#include "3ds/types.h"
#include "3ds/result.h"
#include "3ds/svc.h"
#include "3ds/srv.h"
#include "3ds/synchronization.h"
#include "3ds/services/srvpm.h"
#include "3ds/ipc.h"
#include "3ds/os.h"
#define IS_PRE_7X (osGetFirmVersion() < SYSTEM_VERSION(2, 39, 4))
static Handle srvPmHandle;
static int srvPmRefCount;
Result srvPmInit(void) {
Result res = 0;
if (!IS_PRE_7X)
res = srvInit();
if (R_FAILED(res))
return res;
if (AtomicPostIncrement(&srvPmRefCount))
return 0;
if (!IS_PRE_7X)
res = srvGetServiceHandleDirect(&srvPmHandle, "srv:pm");
else {
res = svcConnectToPort(&srvPmHandle, "srv:pm");
if (R_SUCCEEDED(res))
res = srvInit();
}
if (R_FAILED(res))
srvPmExit();
return res;
}
void srvPmExit(void) {
if (*srvGetSessionHandle() != 0)
srvExit();
if (AtomicDecrement(&srvPmRefCount))
return;
if (srvPmHandle != 0)
svcCloseHandle(srvPmHandle);
srvPmHandle = 0;
}
Handle* srvPmGetSessionHandle(void) {
return &srvPmHandle;
}
static Result srvPmSendCommand(u32* cmdbuf) {
Result rc = 0;
if (IS_PRE_7X)
cmdbuf[0] |= 0x04000000;
rc = svcSendSyncRequest(srvPmHandle);
if (R_SUCCEEDED(rc))
rc = cmdbuf[1];
return rc;
}
Result SRVPM_PublishToProcess(u32 notificationId, Handle process) {
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x1, 1, 2); // 0x10042
cmdbuf[1] = notificationId;
cmdbuf[2] = IPC_Desc_SharedHandles(1);
cmdbuf[3] = process;
return srvPmSendCommand(cmdbuf);
}
Result SRVPM_PublishToAll(u32 notificationId) {
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x2, 1, 0); // 0x20040
cmdbuf[1] = notificationId;
return srvPmSendCommand(cmdbuf);
}
Result SRVPM_RegisterProcess(u32 pid, u32 count, const char (*serviceAccessControlList)[8]) {
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x3, 2, 2); // 0x30082
cmdbuf[1] = pid;
cmdbuf[2] = count * 2;
cmdbuf[3] = IPC_Desc_StaticBuffer(count * 8, 0);
cmdbuf[4] = (u32)serviceAccessControlList;
return srvPmSendCommand(cmdbuf);
}
Result SRVPM_UnregisterProcess(u32 pid) {
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x4, 1, 0); // 0x40040
cmdbuf[1] = pid;
return srvPmSendCommand(cmdbuf);
}

52
src/includes/string.c Normal file
View File

@@ -0,0 +1,52 @@
#include "lib/types.h"
#include <stdint.h>
size_t strlen(const char* str) {
const char* s;
for (s = str; *s; ++s)
;
return (s - str);
}
void* memset(void* ptr, int value, size_t num) {
char* s = ptr;
while (num--)
*s++ = value;
return ptr;
}
void* memcpy(void* dst, const void* src, size_t len) {
size_t i;
if ((uintptr_t)dst % sizeof(long) == 0 && (uintptr_t)src % sizeof(long) == 0 && len % sizeof(long) == 0) {
long* d = dst;
const long* s = src;
for (i = 0; i < len / sizeof(long); i++) {
d[i] = s[i];
}
} else {
char* d = dst;
const char* s = src;
for (i = 0; i < len; i++) {
d[i] = s[i];
}
}
return dst;
}
size_t strnlen(const char* s, size_t max_len) {
size_t i = 0;
for (; (i < max_len) && s[i]; ++i)
;
return i;
}
char* strncpy(char* dst, const char* src, size_t num) {
char* b = dst;
size_t i = 0;
while (i++ != num && (*dst++ = *src++))
;
return b;
}

658
src/includes/svc.s Normal file
View File

@@ -0,0 +1,658 @@
.arm
.align 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 svcControlMemory
push {r0, r4}
ldr r0, [sp, #0x8]
ldr r4, [sp, #0x8+0x4]
svc 0x01
ldr r2, [sp], #4
str r1, [r2]
ldr r4, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcQueryMemory
push {r0, r1, r4-r6}
svc 0x02
ldr r6, [sp]
str r1, [r6]
str r2, [r6, #4]
str r3, [r6, #8]
str r4, [r6, #0xc]
ldr r6, [sp, #4]
str r5, [r6]
add sp, sp, #8
pop {r4-r6}
bx lr
SVC_END
SVC_BEGIN svcExitProcess
svc 0x03
bx lr
SVC_END
SVC_BEGIN svcGetProcessAffinityMask
svc 0x04
bx lr
SVC_END
SVC_BEGIN svcSetProcessAffinityMask
svc 0x05
bx lr
SVC_END
SVC_BEGIN svcGetProcessIdealProcessor
str r0, [sp, #-0x4]!
svc 0x06
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcSetProcessIdealProcessor
svc 0x07
bx lr
SVC_END
SVC_BEGIN svcCreateThread
push {r0, r4}
ldr r0, [sp, #0x8]
ldr r4, [sp, #0x8+0x4]
svc 0x08
ldr r2, [sp], #4
str r1, [r2]
ldr r4, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcExitThread
svc 0x09
bx lr
SVC_END
SVC_BEGIN svcSleepThread
svc 0x0A
bx lr
SVC_END
SVC_BEGIN svcGetThreadPriority
str r0, [sp, #-0x4]!
svc 0x0B
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcSetThreadPriority
svc 0x0C
bx lr
SVC_END
SVC_BEGIN svcGetThreadAffinityMask
svc 0x0D
bx lr
SVC_END
SVC_BEGIN svcSetThreadAffinityMask
svc 0x0E
bx lr
SVC_END
SVC_BEGIN svcGetThreadIdealProcessor
str r0, [sp, #-0x4]!
svc 0x0F
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcSetThreadIdealProcessor
svc 0x10
bx lr
SVC_END
SVC_BEGIN svcGetProcessorID
svc 0x11
bx lr
SVC_END
SVC_BEGIN svcRun
push {r4,r5}
ldr r2, [r1, #0x04]
ldr r3, [r1, #0x08]
ldr r4, [r1, #0x0C]
ldr r5, [r1, #0x10]
ldr r1, [r1, #0x00]
svc 0x12
pop {r4,r5}
bx lr
SVC_END
SVC_BEGIN svcCreateMutex
str r0, [sp, #-4]!
svc 0x13
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcReleaseMutex
svc 0x14
bx lr
SVC_END
SVC_BEGIN svcCreateSemaphore
push {r0}
svc 0x15
pop {r3}
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcReleaseSemaphore
push {r0}
svc 0x16
pop {r3}
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcCreateEvent
str r0, [sp, #-4]!
svc 0x17
ldr r2, [sp], #4
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcSignalEvent
svc 0x18
bx lr
SVC_END
SVC_BEGIN svcClearEvent
svc 0x19
bx lr
SVC_END
SVC_BEGIN svcCreateTimer
str r0, [sp, #-4]!
svc 0x1A
ldr r2, [sp], #4
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcSetTimer
str r4, [sp, #-4]!
ldr r1, [sp, #4]
ldr r4, [sp, #8]
svc 0x1B
ldr r4, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcCancelTimer
svc 0x1C
bx lr
SVC_END
SVC_BEGIN svcClearTimer
svc 0x1D
bx lr
SVC_END
SVC_BEGIN svcCreateMemoryBlock
str r0, [sp, #-4]!
ldr r0, [sp, #4]
svc 0x1E
ldr r2, [sp], #4
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcMapMemoryBlock
svc 0x1F
bx lr
SVC_END
SVC_BEGIN svcUnmapMemoryBlock
svc 0x20
bx lr
SVC_END
SVC_BEGIN svcCreateAddressArbiter
push {r0}
svc 0x21
pop {r2}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcArbitrateAddress
push {r4, r5}
ldr r4, [sp, #8]
ldr r5, [sp, #12]
svc 0x22
pop {r4, r5}
bx lr
SVC_END
SVC_BEGIN svcCloseHandle
svc 0x23
bx lr
SVC_END
SVC_BEGIN svcWaitSynchronization
svc 0x24
bx lr
SVC_END
SVC_BEGIN svcWaitSynchronizationN
str r5, [sp, #-4]!
str r4, [sp, #-4]!
mov r5, r0
ldr r0, [sp, #0x8]
ldr r4, [sp, #0x8+0x4]
svc 0x25
str r1, [r5]
ldr r4, [sp], #4
ldr r5, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcDuplicateHandle
str r0, [sp, #-0x4]!
svc 0x27
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcGetSystemTick
svc 0x28
bx lr
SVC_END
SVC_BEGIN svcGetHandleInfo
str r0, [sp, #-0x4]!
svc 0x29
ldr r3, [sp], #4
str r1, [r3]
str r2, [r3,#4]
bx lr
SVC_END
SVC_BEGIN svcGetSystemInfo
str r0, [sp, #-0x4]!
svc 0x2A
ldr r3, [sp], #4
str r1, [r3]
str r2, [r3,#4]
bx lr
SVC_END
SVC_BEGIN svcGetProcessInfo
str r0, [sp, #-0x4]!
svc 0x2B
ldr r3, [sp], #4
str r1, [r3]
str r2, [r3,#4]
bx lr
SVC_END
SVC_BEGIN svcGetThreadInfo
str r0, [sp, #-0x4]!
svc 0x2C
ldr r3, [sp], #4
str r1, [r3]
str r2, [r3,#4]
bx lr
SVC_END
SVC_BEGIN svcConnectToPort
str r0, [sp, #-0x4]!
svc 0x2D
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcSendSyncRequest
svc 0x32
bx lr
SVC_END
SVC_BEGIN svcOpenThread
push {r0}
svc 0x34
pop {r2}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcGetProcessIdOfThread
str r0, [sp, #-0x4]!
svc 0x36
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcGetThreadId
str r0, [sp, #-0x4]!
svc 0x37
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcGetResourceLimit
str r0, [sp, #-0x4]!
svc 0x38
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcGetResourceLimitLimitValues
svc 0x39
bx lr
SVC_END
SVC_BEGIN svcGetResourceLimitCurrentValues
svc 0x3A
bx lr
SVC_END
SVC_BEGIN svcOutputDebugString
svc 0x3D
bx lr
SVC_END
SVC_BEGIN svcCreatePort
push {r0, r1}
svc 0x47
ldr r3, [sp, #0]
str r1, [r3]
ldr r3, [sp, #4]
str r2, [r3]
add sp, sp, #8
bx lr
SVC_END
SVC_BEGIN svcCreateSessionToPort
push {r0}
svc 0x48
pop {r2}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcCreateSession
push {r0, r1}
svc 0x49
ldr r3, [sp, #0]
str r1, [r3]
ldr r3, [sp, #4]
str r2, [r3]
add sp, sp, #8
bx lr
SVC_END
SVC_BEGIN svcAcceptSession
str r0, [sp, #-4]!
svc 0x4A
ldr r2, [sp]
str r1, [r2]
add sp, sp, #4
bx lr
SVC_END
SVC_BEGIN svcReplyAndReceive
str r0, [sp, #-4]!
svc 0x4F
ldr r2, [sp]
str r1, [r2]
add sp, sp, #4
bx lr
SVC_END
SVC_BEGIN svcBindInterrupt
svc 0x50
bx lr
SVC_END
SVC_BEGIN svcUnbindInterrupt
svc 0x51
bx lr
SVC_END
SVC_BEGIN svcInvalidateProcessDataCache
svc 0x52
bx lr
SVC_END
SVC_BEGIN svcStoreProcessDataCache
svc 0x53
bx lr
SVC_END
SVC_BEGIN svcFlushProcessDataCache
svc 0x54
bx lr
SVC_END
SVC_BEGIN svcStartInterProcessDma
stmfd sp!, {r0, r4, r5}
ldr r0, [sp, #0xC]
ldr r4, [sp, #0x10]
ldr r5, [sp, #0x14]
svc 0x55
ldmfd sp!, {r2, r4, r5}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcStopDma
svc 0x56
bx lr
SVC_END
SVC_BEGIN svcGetDmaState
str r0, [sp, #-4]!
svc 0x57
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcSetGpuProt
svc 0x59
bx lr
SVC_END
SVC_BEGIN svcSetWifiEnabled
svc 0x5A
bx lr
SVC_END
SVC_BEGIN svcDebugActiveProcess
push {r0}
svc 0x60
pop {r2}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcBreakDebugProcess
svc 0x61
bx lr
SVC_END
SVC_BEGIN svcTerminateDebugProcess
svc 0x62
bx lr
SVC_END
SVC_BEGIN svcGetProcessDebugEvent
svc 0x63
bx lr
SVC_END
SVC_BEGIN svcContinueDebugEvent
svc 0x64
bx lr
SVC_END
SVC_BEGIN svcGetProcessList
str r0, [sp, #-0x4]!
svc 0x65
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcGetThreadList
str r0, [sp, #-0x4]!
svc 0x66
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcGetDebugThreadContext
svc 0x67
bx lr
SVC_END
SVC_BEGIN svcSetDebugThreadContext
svc 0x68
bx lr
SVC_END
SVC_BEGIN svcQueryDebugProcessMemory
push {r0, r1, r4-r6}
svc 0x69
ldr r6, [sp]
stm r6, {r1-r4}
ldr r6, [sp, #4]
str r5, [r6]
add sp, sp, #8
pop {r4-r6}
bx lr
SVC_END
SVC_BEGIN svcReadProcessMemory
svc 0x6A
bx lr
SVC_END
SVC_BEGIN svcWriteProcessMemory
svc 0x6B
bx lr
SVC_END
SVC_BEGIN svcSetHardwareBreakPoint
svc 0x6C
bx lr
SVC_END
SVC_BEGIN svcGetDebugThreadParam
push {r0, r1, r4, r5}
ldr r0, [sp, #16]
svc 0x6D
pop {r4, r5}
stm r4, {r1, r2}
str r3, [r5]
pop {r4, r5}
bx lr
SVC_END
SVC_BEGIN svcMapProcessMemory
svc 0x71
bx lr
SVC_END
SVC_BEGIN svcUnmapProcessMemory
svc 0x72
bx lr
SVC_END
SVC_BEGIN svcCreateCodeSet
str r0, [sp, #-0x4]!
ldr r0, [sp, #4]
svc 0x73
ldr r2, [sp, #0]
str r1, [r2]
add sp, sp, #4
bx lr
SVC_END
SVC_BEGIN svcCreateProcess
str r0, [sp, #-0x4]!
svc 0x75
ldr r2, [sp, #0]
str r1, [r2]
add sp, sp, #4
bx lr
SVC_END
SVC_BEGIN svcTerminateProcess
svc 0x76
bx lr
SVC_END
SVC_BEGIN svcSetProcessResourceLimits
svc 0x77
bx lr
SVC_END
SVC_BEGIN svcCreateResourceLimit
push {r0}
svc 0x78
pop {r2}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcSetResourceLimitValues
svc 0x79
bx lr
SVC_END
SVC_BEGIN svcBackdoor
svc 0x7B
bx lr
SVC_END
SVC_BEGIN svcKernelSetState
svc 0x7C
bx lr
SVC_END
SVC_BEGIN svcQueryProcessMemory
push {r0, r1, r4-r6}
svc 0x7D
ldr r6, [sp]
stm r6, {r1-r4}
ldr r6, [sp, #4]
str r5, [r6]
add sp, sp, #8
pop {r4-r6}
bx lr
SVC_END

45
src/includes/svc_loader.s Normal file
View File

@@ -0,0 +1,45 @@
.arm
.align 4
.macro SVC_BEGIN name
.section .loader.\name, "ax", %progbits
.global \name
.type \name, %function
.align 2
.cfi_startproc
\name:
.endm
.macro SVC_END
.cfi_endproc
.endm
SVC_BEGIN svcOpenProcess
push {r0}
svc 0x33
pop {r2}
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcGetProcessId
str r0, [sp, #-0x4]!
svc 0x35
ldr r3, [sp], #4
str r1, [r3]
bx lr
SVC_END
SVC_BEGIN svcBreak
svc 0x3C
bx lr
SVC_END
SVC_BEGIN svcControlProcessMemory
push {r4-r5}
ldr r4, [sp, #0x8]
ldr r5, [sp, #0xC]
svc 0x70
pop {r4-r5}
bx lr
SVC_END

View File

@@ -0,0 +1,224 @@
#include "3ds/types.h"
#include "3ds/svc.h"
#include "3ds/synchronization.h"
static Handle arbiter;
Result __sync_init(void) {
return svcCreateAddressArbiter(&arbiter);
}
void __sync_fini(void) {
if (arbiter)
svcCloseHandle(arbiter);
}
Handle __sync_get_arbiter(void) {
return arbiter;
}
void LightLock_Init(LightLock* lock) {
do
__ldrex(lock);
while (__strex(lock, 1));
}
void LightLock_Lock(LightLock* lock) {
s32 val;
_begin:
do {
val = __ldrex(lock);
if (val < 0) {
// Add ourselves to the list of threads blocked on this lock
if (__strex(lock, val - 1))
goto _begin; // strex failed, try to lock again
_wait:
// Wait for a thread to wake us up
svcArbitrateAddress(arbiter, (u32)lock, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0);
// Try to lock again
do {
val = __ldrex(lock);
if (val < 0) {
// Lock is still locked - keep waiting
__clrex();
goto _wait;
}
} while (__strex(lock, -(val - 1)));
return;
}
} while (__strex(lock, -val));
}
int LightLock_TryLock(LightLock* lock) {
s32 val;
do {
val = __ldrex(lock);
if (val < 0) {
__clrex();
return 1; // Failure
}
} while (__strex(lock, -val));
return 0; // Success
}
void LightLock_Unlock(LightLock* lock) {
s32 val;
do
val = -__ldrex(lock);
while (__strex(lock, val));
if (val > 1)
// Wake up exactly one thread
svcArbitrateAddress(arbiter, (u32)lock, ARBITRATION_SIGNAL, 1, 0);
}
void RecursiveLock_Init(RecursiveLock* lock) {
LightLock_Init(&lock->lock);
lock->thread_tag = 0;
lock->counter = 0;
}
void RecursiveLock_Lock(RecursiveLock* lock) {
u32 tag = (u32)getThreadLocalStorage();
if (lock->thread_tag != tag) {
LightLock_Lock(&lock->lock);
lock->thread_tag = tag;
}
lock->counter++;
}
int RecursiveLock_TryLock(RecursiveLock* lock) {
u32 tag = (u32)getThreadLocalStorage();
if (lock->thread_tag != tag) {
if (LightLock_TryLock(&lock->lock))
return 1; // Failure
lock->thread_tag = tag;
}
lock->counter++;
return 0; // Success
}
void RecursiveLock_Unlock(RecursiveLock* lock) {
if (!--lock->counter) {
lock->thread_tag = 0;
LightLock_Unlock(&lock->lock);
}
}
static inline void LightEvent_SetState(LightEvent* event, int state) {
do
__ldrex(&event->state);
while (__strex(&event->state, state));
}
static inline int LightEvent_TryReset(LightEvent* event) {
do {
if (__ldrex(&event->state)) {
__clrex();
return 0;
}
} while (__strex(&event->state, -1));
return 1;
}
void LightEvent_Init(LightEvent* event, ResetType reset_type) {
LightLock_Init(&event->lock);
LightEvent_SetState(event, reset_type == RESET_STICKY ? -2 : -1);
}
void LightEvent_Clear(LightEvent* event) {
if (event->state == 1) {
LightLock_Lock(&event->lock);
LightEvent_SetState(event, -2);
LightLock_Unlock(&event->lock);
} else if (event->state == 0)
LightEvent_SetState(event, -1);
}
void LightEvent_Pulse(LightEvent* event) {
if (event->state == -2)
svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, -1, 0);
else if (event->state == -1)
svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, 1, 0);
else
LightEvent_Clear(event);
}
void LightEvent_Signal(LightEvent* event) {
if (event->state == -1) {
LightEvent_SetState(event, 0);
svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, 1, 0);
} else if (event->state == -2) {
LightLock_Lock(&event->lock);
LightEvent_SetState(event, 1);
svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, -1, 0);
LightLock_Unlock(&event->lock);
}
}
int LightEvent_TryWait(LightEvent* event) {
if (event->state == 1)
return 1;
return LightEvent_TryReset(event);
}
void LightEvent_Wait(LightEvent* event) {
for (;;) {
if (event->state == -2) {
svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0);
return;
}
if (event->state != -1) {
if (event->state == 1)
return;
if (event->state == 0 && LightEvent_TryReset(event))
return;
}
svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0);
}
}
void LightSemaphore_Init(LightSemaphore* semaphore, s16 initial_count, s16 max_count) {
semaphore->current_count = (s32)initial_count;
semaphore->num_threads_acq = 0;
semaphore->max_count = max_count;
}
void LightSemaphore_Acquire(LightSemaphore* semaphore, s32 count) {
s32 old_count;
s16 num_threads_acq;
do {
for (;;) {
old_count = __ldrex(&semaphore->current_count);
if (old_count > 0)
break;
__clrex();
do
num_threads_acq = (s16)__ldrexh((u16*)&semaphore->num_threads_acq);
while (__strexh((u16*)&semaphore->num_threads_acq, num_threads_acq + 1));
svcArbitrateAddress(arbiter, (u32)semaphore, ARBITRATION_WAIT_IF_LESS_THAN, count, 0);
do
num_threads_acq = (s16)__ldrexh((u16*)&semaphore->num_threads_acq);
while (__strexh((u16*)&semaphore->num_threads_acq, num_threads_acq - 1));
}
} while (__strex(&semaphore->current_count, old_count - count));
}
void LightSemaphore_Release(LightSemaphore* semaphore, s32 count) {
s32 old_count, new_count;
do {
old_count = __ldrex(&semaphore->current_count);
new_count = old_count + count;
if (new_count >= semaphore->max_count)
new_count = semaphore->max_count;
} while (__strex(&semaphore->current_count, new_count));
if (old_count <= 0 || semaphore->num_threads_acq > 0)
svcArbitrateAddress(arbiter, (u32)semaphore, ARBITRATION_SIGNAL, count, 0);
}

18
src/main.c Normal file
View File

@@ -0,0 +1,18 @@
#include "input.h"
#include "common.h"
#include "z3D/z3D.h"
#include "3ds/srv.h"
#include "3ds/services/irrst.h"
GlobalContext* gGlobalContext;
void before_GlobalContext_Update(GlobalContext* globalCtx) {
static u8 init = 0;
if (!init) {
srvInit();
irrstInit();
gGlobalContext = globalCtx;
init = 1;
}
Input_Update();
}

16
src/patches.s Normal file
View File

@@ -0,0 +1,16 @@
.arm
.section .patch_before_GlobalContext_Update
.global before_GlobalContext_Update_patch
before_GlobalContext_Update_patch:
bl hook_before_GlobalContext_Update
.section .patch_loader
.global loader_patch
loader_patch:
b hook_into_loader
.section .patch_CameraUpdate
.global CameraUpdate_patch
CameraUpdate_patch:
bl hook_CameraUpdate