Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,4 @@
*.nro
*.nso
*.nsp
build/
dist/

.vscode
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "application/libs/borealis"]
path = application/libs/borealis
url = git@github.com:natinusala/borealis.git
[submodule "sysmodule/vendor/ulog"]
path = sysmodule/vendor/ulog
url = git@github.com:ShawnFeng0/ulog.git
53 changes: 38 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,53 @@
# SplitNX
# SplitNX #

LiveSplit Server plugin for autosplitting on the Nintendo Switch!

Simply `make dist` and add the files within the dist folder to your SD card. Must run with CFW (Use Atmosphere or Kosmos or it will probably not work).
Includes both basic autosplitting from memory and manual splitting with controller.
All controls require you to hold all 4 shoulder buttons then press:
A - Split/Start
B - Undo Split
X - Skip Split
Y - Reset
## Usage ##

### Splitting With a Controller ###

This is the easiest way to use splitting for newcomers, and is always on.
Certain button combinations will trigger splits for livesplit. These
key-combos currently can not be changed.

The autosplitting is read in from `/split/splitter.txt` on your SD card which has the following format:
To trigger hold all 4 "Shoulder Buttons" (ZL/L/R/ZR), and then press one of:

First line: IP of computer
- `+`: Attempt to connect to livesplit server
- `-`: Reload configuration file.
- `A`: Split/Start
- `B`: Undo
- `X`: Skip
- `Y`: Reset
- Left on the DPAD: write first memory address value in autosplitter to log file.

Second line: Port of LiveSplit Server
### Splitting Based on Memory Values ###

Every line after that will correspond to a memory autosplit corresponding with the index of the split in LiveSplit (so the 3rd line will describe autosplitting for the first split, and so on):
Similar to how autosplit scripts work it is possible to automatically split
based on the condition of a piece of memory. The format is described below,
but we don't describe how to identify these values as that's highly dependent
on each game.

TODO(xxx): document file which just contains memory autosplit.

<!--
[Offset Address from Heap start] [operator] [size] [value]

Example:
`0x61BC93B6 >= u32 1001` will check whether or not the unsigned 32 bit integer at HEAP + 0x61BC93B6 is greater or equal to the value of 1001. If so it will split for that split index.

Hook in auto-splitters?
-->

## Building ##

### SysModule ###

This is super rough, just wanted to get something working for now, and I may or may not improve it later. If anyone wants to improve it then by all means go for it. Would be great to get it to hook into the already implemented AutoSplitters for LiveSplit but I have no clue how to do that.
The sysmodule is the actual thing always running on your switch, listening for
keypresses, and communicating with LiveSplit.

Any questions feel free to ask.
Inside the `sysmodule` directory:

Example of it running: https://youtu.be/K0eXAhT2AB4
1. Ensure you have `switch-mpg123` installed: `dkp-pacman -S switch-mpg123`
2. Run `make dist`.
3. Copy the files within the `dist` folder to your SD Card.
4. Boot up your switch with CFW (Atmosphere/Kosmos).
3 changes: 3 additions & 0 deletions sysmodule/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/build/
/out/
/dist/
51 changes: 40 additions & 11 deletions sysmodule/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ include $(TOPDIR)/../switch_rules
TARGET := $(notdir $(CURDIR))
BUILD := build
OUTDIR := out
SOURCES := source
SOURCES := source vendor/ulog/src
DATA := data
INCLUDES := include
INCLUDES := include vendor/ulog/include
ROMFS := romfs
APP_TITLEID := 2200000000000100
APP_TITLE := SplitNX
Expand Down Expand Up @@ -88,6 +88,8 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \

export DEPSDIR := $(CURDIR)/$(BUILD)

HFILES := $(foreach dir,$(INCLUDES),$(notdir $(wildcard $(dir)/*.h)))
HPPFILES := $(foreach dir,$(INCLUDES),$(notdir $(wildcard $(dir)/*.hpp)))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
Expand All @@ -107,12 +109,12 @@ else
endif
#---------------------------------------------------------------------------------

export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))

export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)

Expand Down Expand Up @@ -160,28 +162,55 @@ ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif

.PHONY: $(BUILD) clean all
.PHONY: $(BUILD) clean all style

#---------------------------------------------------------------------------------
all: $(BUILD)

$(BUILD):
@[ -d $@ ] || mkdir -p $@
@[ -d $(CURDIR)/$(OUTDIR) ] || mkdir -p $(CURDIR)/$(OUTDIR)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile

#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
@rm -fr $(BUILD) $(OUTDIR) $(CURDIR)/dist/ $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
@rm -fr $(BUILD) $(OUTDIR) $(CURDIR)/dist/ $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif

#---------------------------------------------------------------------------------
style:
@for src in $(CFILES) ; do \
echo "Formatting $$src..." ; \
if [ -f "source/$$src" ]; then clang-format-11 -style=webkit -i "source/$$src" ; fi ; \
done
@for src in $(CPPFILES) ; do \
echo "Formatting $$src..." ; \
clang-format-11 -style=webkit -i "source/$$src" ; \
done
@for src in $(HFILES) ; do \
echo "Formatting $$src..." ; \
clang-format-11 -style=webkit -i "include/$$src" ; \
done
@for src in $(HPPFILES) ; do \
echo "Formatting $$src..." ; \
clang-format-11 -style=webkit -i "include/$$src" ; \
done
@echo "Done"

#---------------------------------------------------------------------------------
dist: all $(OUTPUT).nsp
@[ -d $(CURDIR)/dist/ ] || mkdir -p $(CURDIR)/dist/atmosphere/contents/$(APP_TITLEID)/flags
@cp -f $(OUTPUT).nsp $(CURDIR)/dist/atmosphere/contents/$(APP_TITLEID)/exefs.nsp
@touch $(CURDIR)/dist/atmosphere/contents/$(APP_TITLEID)/flags/boot2.flag
@echo '{"name": "SplitNX-Sysmodule", "tid": "$(APP_TITLEID)", "requires_reboot": true}' > $(CURDIR)/dist/atmosphere/contents/$(APP_TITLEID)/toolbox.json

#---------------------------------------------------------------------------------
else
.PHONY: all
.PHONY: all style

DEPENDS := $(OFILES:.o=.d)

Expand Down
34 changes: 17 additions & 17 deletions sysmodule/include/dmntcht.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,33 +62,33 @@ Result dmntchtInitialize(void);
void dmntchtExit(void);
Service* dmntchtGetServiceSession(void);

Result dmntchtHasCheatProcess(bool *out);
Result dmntchtGetCheatProcessEvent(Event *event);
Result dmntchtGetCheatProcessMetadata(DmntCheatProcessMetadata *out_metadata);
Result dmntchtHasCheatProcess(bool* out);
Result dmntchtGetCheatProcessEvent(Event* event);
Result dmntchtGetCheatProcessMetadata(DmntCheatProcessMetadata* out_metadata);
Result dmntchtForceOpenCheatProcess(void);

Result dmntchtGetCheatProcessMappingCount(u64 *out_count);
Result dmntchtGetCheatProcessMappings(MemoryInfo *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtReadCheatProcessMemory(u64 address, void *buffer, size_t size);
Result dmntchtWriteCheatProcessMemory(u64 address, const void *buffer, size_t size);
Result dmntchtQueryCheatProcessMemory(MemoryInfo *mem_info, u64 address);
Result dmntchtGetCheatProcessMappingCount(u64* out_count);
Result dmntchtGetCheatProcessMappings(MemoryInfo* buffer, u64 max_count, u64 offset, u64* out_count);
Result dmntchtReadCheatProcessMemory(u64 address, void* buffer, size_t size);
Result dmntchtWriteCheatProcessMemory(u64 address, const void* buffer, size_t size);
Result dmntchtQueryCheatProcessMemory(MemoryInfo* mem_info, u64 address);
Result dmntchtPauseCheatProcess(void);
Result dmntchtResumeCheatProcess(void);

Result dmntchtGetCheatCount(u64 *out_count);
Result dmntchtGetCheats(DmntCheatEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtGetCheatById(DmntCheatEntry *out_cheat, u32 cheat_id);
Result dmntchtGetCheatCount(u64* out_count);
Result dmntchtGetCheats(DmntCheatEntry* buffer, u64 max_count, u64 offset, u64* out_count);
Result dmntchtGetCheatById(DmntCheatEntry* out_cheat, u32 cheat_id);
Result dmntchtToggleCheat(u32 cheat_id);
Result dmntchtAddCheat(DmntCheatDefinition *cheat, bool enabled, u32 *out_cheat_id);
Result dmntchtAddCheat(DmntCheatDefinition* cheat, bool enabled, u32* out_cheat_id);
Result dmntchtRemoveCheat(u32 cheat_id);
Result dmntchtReadStaticRegister(u64 *out, u8 which);
Result dmntchtReadStaticRegister(u64* out, u8 which);
Result dmntchtWriteStaticRegister(u8 which, u64 value);
Result dmntchtResetStaticRegisters();

Result dmntchtGetFrozenAddressCount(u64 *out_count);
Result dmntchtGetFrozenAddresses(DmntFrozenAddressEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtGetFrozenAddress(DmntFrozenAddressEntry *out, u64 address);
Result dmntchtEnableFrozenAddress(u64 address, u64 width, u64 *out_value);
Result dmntchtGetFrozenAddressCount(u64* out_count);
Result dmntchtGetFrozenAddresses(DmntFrozenAddressEntry* buffer, u64 max_count, u64 offset, u64* out_count);
Result dmntchtGetFrozenAddress(DmntFrozenAddressEntry* out, u64 address);
Result dmntchtEnableFrozenAddress(u64 address, u64 width, u64* out_value);
Result dmntchtDisableFrozenAddress(u64 address);

#ifdef __cplusplus
Expand Down
42 changes: 21 additions & 21 deletions sysmodule/include/service_guard.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
extern "C" {
#endif

#include <switch/types.h>
#include <switch/result.h>
#include <switch/kernel/mutex.h>
#include <switch/sf/service.h>
#include <switch/result.h>
#include <switch/services/sm.h>
#include <switch/sf/service.h>
#include <switch/types.h>

typedef struct ServiceGuard {
Mutex mutex;
Expand Down Expand Up @@ -39,24 +39,24 @@ NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
mutexUnlock(&g->mutex);
}

#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
\
static ServiceGuard g_##name##Guard; \
NX_INLINE Result _##name##Initialize _paramdecl; \
static void _##name##Cleanup(void); \
\
Result name##Initialize _paramdecl \
{ \
Result rc = 0; \
if (serviceGuardBeginInit(&g_##name##Guard)) \
rc = _##name##Initialize _parampass; \
return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
} \
\
void name##Exit(void) \
{ \
serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
}
#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
\
static ServiceGuard g_##name##Guard; \
NX_INLINE Result _##name##Initialize _paramdecl; \
static void _##name##Cleanup(void); \
\
Result name##Initialize _paramdecl \
{ \
Result rc = 0; \
if (serviceGuardBeginInit(&g_##name##Guard)) \
rc = _##name##Initialize _parampass; \
return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
} \
\
void name##Exit(void) \
{ \
serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
}

#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())

Expand Down
13 changes: 5 additions & 8 deletions sysmodule/include/splitter.hpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
#pragma once

#include <vector>
#include <string>
#include <vector>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <switch.h>


bool doOperator(u64 param1, u64 param2, std::string op);
u64 readMemory(u64 address, size_t size, std::string type);

struct split
{
struct split {
u64 address;
u64 value;
size_t size;
Expand All @@ -24,8 +22,7 @@ struct split
bool valid = false;
};

class Splitter
{
class Splitter {
public:
Splitter(std::string filename);
void Reload(std::string filename);
Expand All @@ -37,7 +34,7 @@ class Splitter
void Skip();
void SetLoading(bool);

void test_it();
void debug_first_mem();

private:
bool connected;
Expand Down
Loading