diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..21720d1 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,81 @@ +name: Build ipswitch.nro + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: + inputs: + version: + description: 'Release version. Leave it empty to avoid publishing a release.' + required: false + type: string + +jobs: + build: + runs-on: ubuntu-latest + container: + image: devkitpro/devkita64:latest + options: --user root + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Compilar e instalar liblz4 para Switch + # No usamos dkp-pacman: pkg.devkitpro.org devuelve 403 a las IPs de + # GitHub Actions (bloqueo conocido de su Cloudflare/WAF, no soportan + # pacman en CI). En su lugar compilamos lz4 desde su código fuente + # oficial en GitHub con el toolchain devkitA64 ya presente en la imagen. + run: | + export PATH="$DEVKITPRO/devkitA64/bin:$PATH" + export CC=aarch64-none-elf-gcc + export AR=aarch64-none-elf-gcc-ar + ARCH_FLAGS="-march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec" + + curl -L -o /tmp/lz4.tar.gz https://github.com/lz4/lz4/archive/refs/tags/v1.9.4.tar.gz + tar xf /tmp/lz4.tar.gz -C /tmp + cd /tmp/lz4-1.9.4/lib + + make liblz4.a CC="$CC" AR="$AR" CFLAGS="-O2 $ARCH_FLAGS -D__SWITCH__" + + PORTLIBS="$DEVKITPRO/portlibs/switch" + mkdir -p "$PORTLIBS/include" "$PORTLIBS/lib" + cp liblz4.a "$PORTLIBS/lib/" + cp lz4.h lz4hc.h lz4frame.h "$PORTLIBS/include/" + + - name: Build + run: make + env: + DEVKITPRO: /opt/devkitpro + DEVKITA64: /opt/devkitpro/devkitA64 + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ipswitch + path: '*.nro' + if-no-files-found: error + + release: + needs: build + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' && inputs.version != '' + permissions: + contents: write + + steps: + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: ipswitch + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: "v${{ inputs.version }}" + name: "ipswitch v${{ inputs.version }}" + files: '*.nro' + draft: false + prerelease: false diff --git a/Makefile b/Makefile index e91a2c8..edbd04f 100644 --- a/Makefile +++ b/Makefile @@ -31,18 +31,13 @@ include $(DEVKITPRO)/libnx/switch_rules # - /default_icon.jpg #--------------------------------------------------------------------------------- -GITREV := $(shell git rev-parse HEAD && rm -f $(notdir $(CURDIR)).nacp) - VERSION_MAJOR := 0 VERSION_MINOR := 2 -VERSION_MICRO := 1 -ifneq ($(GITREV),) -VERSION_MICRO := $(VERSION_MICRO)-$(GITREV) -endif +VERSION_MICRO := 3 APP_TITLE := IPSwitch APP_AUTHOR := 3096 -APP_VERSION := w${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO} +APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO} TARGET := $(notdir $(CURDIR)) BUILD := build @@ -204,4 +199,4 @@ $(OFILES_SRC) : $(HFILES_BIN) #--------------------------------------------------------------------------------------- endif -#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/source/config.c b/source/config.c index fda0210..863cdac 100644 --- a/source/config.c +++ b/source/config.c @@ -1,10 +1,10 @@ #include "config.h" #include "patch.h" -int checkRequirement() { +int checkRequirement(PadState* pad) { if(!isDirectory(ATMOS_DIR)) { - userConfirm("Do you even Atmosphere bro?"); + userConfirm("Do you even Atmosphere bro?", pad); return -1; } return 0; -} +} \ No newline at end of file diff --git a/source/config.h b/source/config.h index 7c08805..5ed4edc 100644 --- a/source/config.h +++ b/source/config.h @@ -7,6 +7,6 @@ #define LINE_MAX_SIZE 0x1000 // Return 0 if all checks passed -int checkRequirement(); +int checkRequirement(PadState* pad); -#endif +#endif \ No newline at end of file diff --git a/source/console.c b/source/console.c index 1242907..78d6414 100644 --- a/source/console.c +++ b/source/console.c @@ -123,31 +123,88 @@ void selectIndex(int* selection, StrList* str_list, int change) { printf(menu_buffer); } -u64 selectFromList(int* selection, StrList* str_list) { +u64 selectFromList(int* selection, StrList* str_list, PadState* pad) { if(str_list->size <= 0) { - if(userConfirm("Error: list is empty")) + if(userConfirm("Error: list is empty", pad)) return 0; else - return KEY_PLUS; + return HidNpadButton_Plus; } selectIndex(selection, str_list, 0); - u64 kDownPrevious = 0xFFFFFFFFFFFFFFFF; + static u64 stick_cooldown = 0; + while(appletMainLoop()) { - hidScanInput(); - u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO); - kDown = kDown&(0xFFFFFFFFFFFFFFFF-KEY_TOUCH); // "can't touch this" - - if (kDown & KEY_UP) + padUpdate(pad); + u64 kDown = padGetButtonsDown(pad); + + // Get analog stick positions + HidAnalogStickState analog_stick_l = padGetStickPos(pad, 0); + HidAnalogStickState analog_stick_r = padGetStickPos(pad, 1); + + bool direction_pressed = false; + + // Handle directional buttons first + if (kDown & HidNpadButton_Up) { selectIndex(selection, str_list, -1); - else if (kDown & KEY_DOWN) + stick_cooldown = 15; + direction_pressed = true; + } + else if (kDown & HidNpadButton_Down) { selectIndex(selection, str_list, 1); - else if (kDown & KEY_LEFT) + stick_cooldown = 15; + direction_pressed = true; + } + else if (kDown & HidNpadButton_Left) { selectIndex(selection, str_list, -5); - else if (kDown & KEY_RIGHT) + stick_cooldown = 15; + direction_pressed = true; + } + else if (kDown & HidNpadButton_Right) { selectIndex(selection, str_list, 5); - else if (kDown & KEY_A) { + stick_cooldown = 15; + direction_pressed = true; + } + // Check analog sticks only if cooldown expired and no button was pressed + else if (stick_cooldown == 0) { + const s32 STICK_THRESHOLD = 25000; + + if (analog_stick_l.y > STICK_THRESHOLD || analog_stick_r.y > STICK_THRESHOLD) { + selectIndex(selection, str_list, -1); + stick_cooldown = 15; + direction_pressed = true; + } + else if (analog_stick_l.y < -STICK_THRESHOLD || analog_stick_r.y < -STICK_THRESHOLD) { + selectIndex(selection, str_list, 1); + stick_cooldown = 15; + direction_pressed = true; + } + else if (analog_stick_l.x < -STICK_THRESHOLD || analog_stick_r.x < -STICK_THRESHOLD) { + selectIndex(selection, str_list, -5); + stick_cooldown = 15; + direction_pressed = true; + } + else if (analog_stick_l.x > STICK_THRESHOLD || analog_stick_r.x > STICK_THRESHOLD) { + selectIndex(selection, str_list, 5); + stick_cooldown = 15; + direction_pressed = true; + } + } + + // Decrease cooldown + if (stick_cooldown > 0) { + stick_cooldown--; + } + + // If we moved with direction, skip checking action buttons + if (direction_pressed) { + consoleUpdate(NULL); + continue; + } + + // Now check action buttons + if (kDown & HidNpadButton_A) { char* cur_str = str_list->str_list[*selection]; u16 str_tail = getStringTailU16(cur_str); if(str_tail == TOGGLE_ENABLED) { @@ -161,28 +218,44 @@ u64 selectFromList(int* selection, StrList* str_list) { return kDown; } } - else if(kDown > kDownPrevious) { + else if(kDown & HidNpadButton_B) { printf("\n"); return kDown; } - kDownPrevious = kDown; + else if(kDown & HidNpadButton_Plus) { + printf("\n"); + return kDown; + } + else if(kDown & HidNpadButton_Minus) { + printf("\n"); + return kDown; + } + else if(kDown & HidNpadButton_X) { + printf("\n"); + return kDown; + } + else if(kDown & HidNpadButton_Y) { + printf("\n"); + return kDown; + } + consoleUpdate(NULL); } return 0; } -bool userConfirm(const char * msg) { +bool userConfirm(const char * msg, PadState* pad) { printf("\n%s\nPress A to confirm, any other button to cancel\n", msg); - u64 kDownPrevious = hidKeysDown(CONTROLLER_P1_AUTO); + u64 kDownPrevious = padGetButtonsDown(pad); while(appletMainLoop()) { - hidScanInput(); - u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO); + padUpdate(pad); + u64 kDown = padGetButtonsDown(pad); if(kDown > kDownPrevious) { - if (kDown & KEY_A) + if (kDown & HidNpadButton_A) return true; else { printf("Canceled\n"); @@ -210,4 +283,4 @@ void printBytesAsHex(const u8* bytes, size_t size) { for(size_t i = 0; i < size; i++) { printf("%02X", bytes[i]); } -} +} \ No newline at end of file diff --git a/source/console.h b/source/console.h index 5549781..0dcdcee 100644 --- a/source/console.h +++ b/source/console.h @@ -20,9 +20,9 @@ u16 getStringTailU16(char* str); -u64 selectFromList(int* selection, StrList* str_list); +u64 selectFromList(int* selection, StrList* str_list, PadState* pad); -bool userConfirm(const char* msg); +bool userConfirm(const char* msg, PadState* pad); void printInProgress(const char* msg); void printDone(); diff --git a/source/main.c b/source/main.c index c06c393..f69655c 100644 --- a/source/main.c +++ b/source/main.c @@ -6,13 +6,20 @@ int main(int argc, char **argv) { consoleInit(NULL); - if (checkRequirement() == 0) { + // Configure input - 1 player using standard controller + padConfigureInput(1, HidNpadStyleSet_NpadStandard); + + // Initialize the default gamepad (Player 1 controller) + PadState pad; + padInitializeDefault(&pad); + + if (checkRequirement(&pad) == 0) { printf(CONSOLE_ESC(35;1m) - "Welcome to IPSwitch. Do no evil.\n\n" CONSOLE_ESC(m)); + "Welcome to IPSwitch\n\n" CONSOLE_ESC(m)); - mainMenu(); + mainMenu(&pad); } consoleExit(NULL); return 0; -} +} \ No newline at end of file diff --git a/source/menu.c b/source/menu.c index 557db97..c5a2e0f 100644 --- a/source/menu.c +++ b/source/menu.c @@ -1,6 +1,6 @@ #include "menu.h" -void mainMenu() { +void mainMenu(PadState* pad) { StrList* main_menu_list = getStrList(); addToStrList(main_menu_list, "Generate IPS by Patch Text"); //0 addToStrList(main_menu_list, "Toggle Patch Text Contents"); //1 @@ -13,29 +13,29 @@ void mainMenu() { "\n%s\n", CONSOLE_ESC( 1m) "Select an Operation or Press + to Quit:" CONSOLE_ESC(m)); - u64 kDown = selectFromList(&selection, main_menu_list); + u64 kDown = selectFromList(&selection, main_menu_list, pad); - if (kDown & KEY_A) { + if (kDown & HidNpadButton_A) { switch (selection) { case 0: - kDown = patchTextToIPSMenu(); + kDown = patchTextToIPSMenu(pad); break; case 1: - kDown = patchTextToggleMenu(); + kDown = patchTextToggleMenu(pad); break; default: break; } } - if (kDown & KEY_PLUS) break; + if (kDown & HidNpadButton_Plus) break; consoleUpdate(NULL); } freeStrList(main_menu_list); } -u64 patchTextSelect(PatchTextTarget* pchtxt_target) { +u64 patchTextSelect(PatchTextTarget* pchtxt_target, PadState* pad) { printf("\nReading contents from %s\n", IPSWITCH_DIR); StrList* pchtxt_list = getStrList(); @@ -94,6 +94,12 @@ u64 patchTextSelect(PatchTextTarget* pchtxt_target) { } closedir(dir); } + + // Sort the list alphabetically (A-Z) + if (pchtxt_list->size > 1) { + qsort(pchtxt_list->str_list, pchtxt_list->size, sizeof(String), + (int (*)(const void*, const void*))strcmp); + } u64 kDown = 0; // returns this later @@ -102,13 +108,13 @@ u64 patchTextSelect(PatchTextTarget* pchtxt_target) { printf("\n%s\n", CONSOLE_ESC(1m) "Press: A to Select Patch Text | B to Go Back | PLUS(+) to quit:" CONSOLE_ESC(m)); - kDown = selectFromList(&selection, pchtxt_list); + kDown = selectFromList(&selection, pchtxt_list, pad); - if (kDown & KEY_PLUS || kDown & KEY_B) { + if (kDown & HidNpadButton_Plus || kDown & HidNpadButton_B) { break; } - if (kDown & KEY_A) { + if (kDown & HidNpadButton_A) { // patch_txt_path strcpy(pchtxt_target->patch_txt_path, IPSWITCH_DIR); strcat(pchtxt_target->patch_txt_path, @@ -132,15 +138,15 @@ u64 patchTextSelect(PatchTextTarget* pchtxt_target) { return kDown; } -u64 patchTextToIPSMenu() { +u64 patchTextToIPSMenu(PadState* pad) { u64 kDown = 0; while (appletMainLoop()) { PatchTextTarget pchtxt_target; - kDown = patchTextSelect(&pchtxt_target); + kDown = patchTextSelect(&pchtxt_target, pad); - if (kDown & KEY_PLUS || kDown & KEY_B) break; + if (kDown & HidNpadButton_Plus || kDown & HidNpadButton_B) break; - if (kDown & KEY_A) { + if (kDown & HidNpadButton_A) { int rc = 0; rc = patchTextToIPS(&pchtxt_target); @@ -155,12 +161,12 @@ u64 patchTextToIPSMenu() { return kDown; } -u64 patchTextToggleMenu() { +u64 patchTextToggleMenu(PadState* pad) { u64 kDown = 0; PatchTextTarget pchtxt_target; - kDown = patchTextSelect(&pchtxt_target); + kDown = patchTextSelect(&pchtxt_target, pad); - if (kDown & KEY_A) { + if (kDown & HidNpadButton_A) { printf(CONSOLE_ESC(1m) "Press: A to Toggle a Patch | B to Save and Go Back\n" " X to Abort and Discard Changes | " @@ -182,9 +188,9 @@ u64 patchTextToggleMenu() { } int selection = 0; // dummy here - kDown = selectFromList(&selection, patch_str_list); + kDown = selectFromList(&selection, patch_str_list, pad); - if (kDown & KEY_B || kDown & KEY_Y) { + if (kDown & HidNpadButton_B || kDown & HidNpadButton_Y) { rc = writePchtxtFromStrList(&pchtxt_target, pchtxt, patch_str_list); @@ -195,7 +201,7 @@ u64 patchTextToggleMenu() { goto end; } - if (kDown & KEY_Y) { + if (kDown & HidNpadButton_Y) { rc = patchTextToIPS(&pchtxt_target); if (R_SUCCEEDED(rc)) @@ -212,4 +218,4 @@ u64 patchTextToggleMenu() { freeStrList(patch_str_list); } return kDown; -} +} \ No newline at end of file diff --git a/source/menu.h b/source/menu.h index a8e2f34..8bef0a5 100644 --- a/source/menu.h +++ b/source/menu.h @@ -13,12 +13,12 @@ #define IPSWITCH_DIR "/switch/ipswitch/" -u64 patchTextSelect(); +u64 patchTextSelect(PatchTextTarget* pchtxt_target, PadState* pad); -void mainMenu(); +void mainMenu(PadState* pad); -u64 patchTextToIPSMenu(); +u64 patchTextToIPSMenu(PadState* pad); -u64 patchTextToggleMenu(); +u64 patchTextToggleMenu(PadState* pad); -#endif +#endif \ No newline at end of file