diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..94e57a8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,44 @@ +name: CI +on: push +jobs: + build: + runs-on: ubuntu-latest + container: devkitpro/devkitppc:20220821 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup + run: | + apt-get update && apt-get install -y genisoimage nodejs build-essential gcc-arm-none-eabi python3-distutils python3-setuptools + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && python3 get-pip.py + pip3 install -r patches/scripts/requirements.txt + - name: Build cubeboot + run: cd entry; make clean && make + - name: Build apploader + run: | + git clone -b force-early-boot --single-branch https://github.com/OffBroadway/gc-boot-tools.git + cd gc-boot-tools + (cd ppc/apploader; make) + (cd mkgbi; make) + ls mkgbi/gbi.hdr + - name: Build GCLoader + run: | + mkdir boot-dir && cp cubeboot/cubeboot.dol boot-dir + genisoimage -R -J -G gc-boot-tools/mkgbi/gbi.hdr -no-emul-boot -b cubeboot.dol -o boot.iso boot-dir + - name: Build PicoBoot + run: | + git clone https://github.com/webhdx/PicoBoot.git + cd PicoBoot; env PICO_SDK_FETCH_FROM_GIT=1 cmake . + ./process_ipl.py ../entry/entry.dol src/ipl.h + make + - name: Rename Artifacts + run: | + mkdir -p dist/next + mv ./boot.iso dist/next/boot.iso + mv ./cubeboot/cubeboot.dol dist/next/cubeboot.dol + mv ./PicoBoot/picoboot.uf2 dist/next/cubeboot.uf2 + - name: Archive + uses: actions/upload-artifact@v3 + with: + name: dist + path: dist/next/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 267031e..f9f14b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.4] - Boot Held Button Programs + +### Details +This build includes custom program loading, PAL 480p and optional boot delays. + +Custom program loading by be of use if you boot homebrew from one of the existing defaults and need to specify an alternative. It can also be helpful if you prefer to use your cube exclusively for GBI without needing a loader. + +Boot Delays are a new optional feature that can delay cubeboot before the animation begins or after it finishes (and leave it on screen for an extended period). You may want to use preboot delay to wait for your TV to detect the video source. Additionally you may be interested in a postboot delay to emulate the load times you would usually see when booting a disc. + +Usage: +- Held button with standard names like `B.dol` and `START.dol` +- Held buttons with custom names like `button_x = test.dol` set in `cubeboot.ini` +- Set delays before the boot logo using `preboot_delay_ms = 500` (time in milliseconds) +- Set delays after the boot logo using `postboot_delay_ms = 3000` (time in milliseconds) +- PAL IPL 1.0 (DOL-001 PAL consoles) Force Progressive with `force_progressive = 1` set in `cubeboot.ini` +- Specify a custom default DOL file to boot into with `default_program = swiss.dol` set in `cubeboot.ini` + +Use-cases: +- Hold button to load alternative DOL files (this also loads associated `.cli` files) +- Set custom program per-button with `button_name = something.dol` +- When using GCVideo, you can set `preboot_delay_ms` to wait for your TV to sync to the input source +- The `postboot_delay_ms` setting exists exclusively for flair. It can help recover the feeling of waiting for a game to load +- If you do not want cubeboot to enumerate through names like `boot.dol` and `autoexec.dol` you can set your own default with the `default_program` setting + +Expected behavior: +- When you have both `Y.dol` and `test.dol` on the SD with `button_y = test.dol` in `cubeboot.ini`, this should boot `test.dol` +- The GCLoader SD card will only be used when booting directly from GCLoader (please submit an **Issue** if you have trouble using GCLoader with PicoBoot) + +This release includes the following enhancements: +- https://github.com/OffBroadway/flippyboot-ipl/issues/13 +- https://github.com/OffBroadway/flippyboot-ipl/issues/7 +- https://github.com/OffBroadway/flippyboot-ipl/issues/2 +- https://github.com/OffBroadway/flippyboot-ipl/issues/3 + +It also fixes the following bugs: +- https://github.com/OffBroadway/flippyboot-ipl/issues/5 + ## [0.1.3] - PAL Progressive Scan hotfix ### Details diff --git a/CREDIT.md b/CREDIT.md index e6ce327..883f7c4 100644 --- a/CREDIT.md +++ b/CREDIT.md @@ -13,6 +13,8 @@ `patches/source/usbgecko.c`: credit to swiss-gc
`patches/source/picolibc`: credit to picolibc and newlib
`patches/source/tinyprintf`: credit to tinyprintf
+`patches/source/time.c` credit to libogc
+`patches/source/arith64.c` credit to github.com/glitchub/arith64

@@ -27,3 +29,11 @@ `cubeboot/source/pcg_basic.h` credit to pcg-random.org
`cubeboot/source/ini.c` credit to github.com/rxi/ini
`cubeboot/source/ini.h` credit to github.com/rxi/ini
+ +`cubeboot/include/elf_abi.h` credit to swiss-gc
+`cubeboot/include/gcm.h` credit to gc-boot-tools
+ +
+ +`NTSC 480p on PAL IPLs` credit to Nintendont (terrible workaround I know)
+`NTSC 480p on PAL IPLs` credit to swiss-gc (Fix logo animation speed)
diff --git a/cubeboot/Makefile b/cubeboot/Makefile index f81380f..1818faa 100644 --- a/cubeboot/Makefile +++ b/cubeboot/Makefile @@ -111,6 +111,7 @@ $(BUILD): $(PATCHES) #--------------------------------------------------------------------------------- $(PATCHES): + @[ -d $(DATA) ] || mkdir -p $(DATA) @$(MAKE) --no-print-directory -C $(dir $(PROJDIR))/$(basename $(notdir $@)) @cp $(dir $(PROJDIR))/$(basename $(notdir $@))/$(notdir $@) $@ @$(OBJCOPY) --remove-section .comment --remove-section .gnu.attributes $@ diff --git a/cubeboot/include/gcm.h b/cubeboot/include/gcm.h new file mode 100644 index 0000000..dae815c --- /dev/null +++ b/cubeboot/include/gcm.h @@ -0,0 +1,129 @@ +/* + * gcm.h + * + * GameCube Master + * Copyright (C) 2005-2006 The GameCube Linux Team + * Copyright (C) 2005,2006 Albert Herranz + * + * 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 2 + * of the License, or (at your option) any later version. + * + */ + +#ifndef __GCM_H +#define __GCM_H + +#include + +#define GCM_MAGIC 0xc2339f3d + +#define GCM_OPENING_BNR "opening.bnr" + +struct gcm_disk_info { + char game_code[4]; + char maker_code[2]; + char disk_id; + char version; + char audio_streaming; + char stream_buffer_size; + char unused_1[18]; + uint32_t magic; /* 0xc2339f3d */ +} __attribute__ ((__packed__)); + +struct gcm_disk_layout { + uint32_t dol_offset; + uint32_t fst_offset; + uint32_t fst_size; + uint32_t fst_max_size; + uint32_t user_offset; + uint32_t user_size; + uint32_t disk_size; + char unused_3[4]; +} __attribute__ ((__packed__)); + +/* "boot.bin" */ +struct gcm_disk_header { + struct gcm_disk_info info; + char game_name[992]; + uint32_t debug_monitor_offset; + uint32_t debug_monitor_address; + char unused_2[24]; + struct gcm_disk_layout layout; +} __attribute__ ((__packed__)); + +/* "bi2.bin" */ +struct gcm_disk_header_info { + uint32_t debug_monitor_size; + uint32_t simulated_memory_size; + uint32_t argument_offset; + uint32_t debug_flag; + uint32_t track_location; + uint32_t track_size; + uint32_t country_code; + uint32_t unknown_1; +} __attribute__ ((__packed__)); + +/* "appldr.bin" */ +struct gcm_apploader_header { + char date[10]; + char padding_1[6]; + uint32_t entry_point; + uint32_t size; + uint32_t trailer_size; + uint32_t unknown_1; +} __attribute__ ((__packed__)); + +struct gcm_file_entry { + union { + uint8_t flags; + struct { + uint32_t fname_offset; + uint32_t file_offset; + uint32_t file_length; + } file; + struct { + uint32_t fname_offset; + uint32_t parent_directory_offset; + uint32_t this_directory_offset; + } dir; + struct { + uint32_t zeroed_1; + uint32_t zeroed_2; + uint32_t num_entries; + } root_dir; + }; +} __attribute__ ((__packed__)); + +struct gcm_system_area { + struct gcm_disk_header dh; + struct gcm_disk_header_info dhi; + + struct gcm_apploader_header al_header; + void *al_image; + off_t al_size; /* aligned to 32 bytes */ + + void *fst_image; + off_t fst_size; /* aligned to 32 bytes */ + + void *bnr_image; + off_t bnr_size; /* aligned to 32 bytes */ +}; + + +#define DI_SECTOR_SIZE 2048 +#define DI_ALIGN 31 + +#define SYSTEM_AREA_SIZE (16*DI_SECTOR_SIZE) + +/* + * + */ +static inline int di_align_size(int size) +{ + return ((size + 31) & ~31); +} + +#endif /* __GCM_H */ + diff --git a/cubeboot/source/const.h b/cubeboot/source/const.h new file mode 100644 index 0000000..b1ca43b --- /dev/null +++ b/cubeboot/source/const.h @@ -0,0 +1 @@ +#define MAX_BUTTONS 13 diff --git a/cubeboot/source/halt.c b/cubeboot/source/halt.c index bf3b2ad..a94b52d 100644 --- a/cubeboot/source/halt.c +++ b/cubeboot/source/halt.c @@ -23,6 +23,7 @@ void prog_halt(char *msg) { console_init(exception_xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); // CON_EnableGecko(EXI_CHANNEL_1, false); #endif + printf("\n====\nHALT\n====\n"); printf(msg); #ifdef VIDEO_ENABLE VIDEO_WaitVSync(); diff --git a/cubeboot/source/loader.c b/cubeboot/source/loader.c index bc4aa11..f7b1a23 100644 --- a/cubeboot/source/loader.c +++ b/cubeboot/source/loader.c @@ -3,14 +3,17 @@ #include #include "sd.h" +#include "settings.h" +#include "state.h" #include "crc32.h" #include "print.h" #include "halt.h" +#include "loader.h" #include "boot/sidestep.h" -char *swiss_paths[] = { +char *boot_paths[] = { "/BOOT.DOL", "/BOOT2.DOL", "/IGR.DOL", // used by swiss-gc @@ -22,15 +25,26 @@ extern const void _start; extern const void _edata; u8 *dol_buf; -u32 *bs2done = (u32*)0x81700000; bool check_load_program() { // check if we can even load files if (!is_device_mounted()) return false; bool found_file = false; - for (int f = 0; f < (sizeof(swiss_paths) / sizeof(char *)); f++) { - char *path = swiss_paths[f]; + + if (settings.default_program != NULL) { + char *path = settings.default_program; + int size = get_file_size(path); + if (size == SD_FAIL) { + iprintf("Failed to open file: %s\n", path); + return false; + } else { + return true; + } + } + + for (int f = 0; f < (sizeof(boot_paths) / sizeof(char *)); f++) { + char *path = boot_paths[f]; int size = get_file_size(path); if (size == SD_FAIL) { iprintf("Failed to open file: %s\n", path); @@ -44,48 +58,126 @@ bool check_load_program() { return found_file; } -void load_program() { - // load program - for (int f = 0; f < (sizeof(swiss_paths) / sizeof(char *)); f++) { - char *path = swiss_paths[f]; +bool load_cli_file(char *path, cli_params *params) { + char cli_path[255]; + strcpy(cli_path, path); - iprintf("Reading %s\n", path); + int path_length = strlen(cli_path); + cli_path[path_length - 3] = 'c'; + cli_path[path_length - 2] = 'l'; + cli_path[path_length - 1] = 'i'; - int size = get_file_size(path); - if (size == SD_FAIL) { - iprintf("Failed to open file: %s\n", path); - continue; - } + // always set program name + params->argv[params->argc] = path; + params->argc++; - dol_buf = memalign(32, size); - if (!dol_buf) { - dol_buf = (u8*)0x81300000; - } + iprintf("Reading CLI %s\n", cli_path); + + int size = get_file_size(cli_path); + if (size == SD_FAIL) { + iprintf("Failed to open file: %s\n", cli_path); + return false; + } - if (load_file_buffer(path, dol_buf) != SD_OK) { - iprintf("Failed to DOL read file: %s\n", path); - dol_buf = NULL; + char *cli_buf; + if (load_file_dynamic(cli_path, (void*)&cli_buf) != SD_OK) { + iprintf("Failed to CLI read file: %s\n", cli_path); + return false; + } + + // First argument is at the beginning of the file + if (cli_buf[0] != '\r' && cli_buf[0] != '\n') { + params->argv[params->argc] = cli_buf; + params->argc++; + } + + // Search for the others after each newline + for (int i = 0; i < size; i++) { + if (cli_buf[i] == '\r' || cli_buf[i] == '\n') + { + cli_buf[i] = '\0'; + } + else if (cli_buf[i - 1] == '\0') + { + params->argv[params->argc] = cli_buf + i; + params->argc++; + if (params->argc >= MAX_NUM_ARGV) + { + kprintf("Reached max of %i args.\n", MAX_NUM_ARGV); + break; + } } + } + + iprintf("Found %i CLI args\n", params->argc); + + return true; +} + +bool load_program(char *path, cli_params *params) { + iprintf("Reading %s\n", path); + + int size = get_file_size(path); + if (size == SD_FAIL) { + iprintf("Failed to open file: %s\n", path); + return false; + } + + dol_buf = memalign(32, size); + if (!dol_buf) { + dol_buf = (u8*)0x81300000; + } + + if (load_file_buffer(path, dol_buf) != SD_OK) { + iprintf("Failed to DOL read file: %s\n", path); + dol_buf = NULL; + return false; + } + + iprintf("Loaded DOL into %p\n", dol_buf); + + // try to get cli args + load_cli_file(path, params); + + return true; +} - iprintf("Loaded DOL into %p\n", dol_buf); - break; +void boot_program(char *alternative_path) { + cli_params params = { .argc = 0 }; + memset(params.argv, 0, sizeof(char*)); + + if (alternative_path != NULL) { + load_program(alternative_path, ¶ms); + } else if (settings.default_program != NULL) { + load_program(settings.default_program, ¶ms); + } else { + for (int f = 0; f < (sizeof(boot_paths) / sizeof(char *)); f++) { + char *path = boot_paths[f]; + if (load_program(path, ¶ms)) { + break; + } + } } if (dol_buf == NULL) { - prog_halt("No program loaded!\n"); + if (alternative_path != NULL) { + char buf[128]; + sprintf(buf, "No program loaded! (path=%s)\n", alternative_path); + prog_halt(buf); + } else { + prog_halt("No program loaded!\n"); + } + return; } iprintf("Program loaded...\n"); - *bs2done = 0x0; + state->boot_code = 0x0; iprintf("BOOTING\n"); #ifdef VIDEO_ENABLE VIDEO_WaitVSync(); #endif - // No stack - we need it all - AR_Init(NULL, 0); - - DOLtoARAM(dol_buf, 0, NULL); + DOLtoARAM(dol_buf, params.argc, params.argv); } diff --git a/cubeboot/source/loader.h b/cubeboot/source/loader.h index 6b1fa14..4693f19 100644 --- a/cubeboot/source/loader.h +++ b/cubeboot/source/loader.h @@ -1,5 +1,13 @@ #include +#define MAX_NUM_ARGV 1024 + +typedef struct cli_params_t { + int argc; + char *argv[MAX_NUM_ARGV]; +} cli_params; + bool check_load_program(); -void load_program(); -void load_current_program(); +bool load_program(char *path, cli_params *params); +bool load_cli_file(char *path, cli_params *params); +void boot_program(char *path); diff --git a/cubeboot/source/main.c b/cubeboot/source/main.c index 76a123e..b32f19e 100644 --- a/cubeboot/source/main.c +++ b/cubeboot/source/main.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -28,6 +29,7 @@ #include "helpers.h" #include "halt.h" +#include "state.h" #include "settings.h" #include "logo.h" @@ -35,7 +37,6 @@ #include "loader.h" static u32 prog_entrypoint, prog_dst, prog_src, prog_len; -static u32 *bs2done = (u32*)0x81700000; #define BS2_BASE_ADDR 0x81300000 static void (*bs2entry)(void) = (void(*)(void))BS2_BASE_ADDR; @@ -54,7 +55,7 @@ void *xfb; GXRModeObj *rmode; void __SYS_PreInit() { - if (*bs2done == 0xCAFEBEEF) return; + if (state->boot_code == 0xCAFEBEEF) return; SYS_SetArenaHi((void*)BS2_BASE_ADDR); @@ -63,7 +64,10 @@ void __SYS_PreInit() { } int main() { -//// elf world + u64 startts, endts; + + startts = ticks_to_millisecs(gettime()); + Elf32_Ehdr* ehdr; Elf32_Shdr* shdr; @@ -95,6 +99,7 @@ int main() { #ifdef VIDEO_ENABLE VIDEO_Init(); // rmode = VIDEO_GetPreferredMode(NULL); + // rmode = &TVNtsc480IntDf; u32 tvmode = VIDEO_GetCurrentTvMode(); switch (tvmode) { case VI_NTSC: @@ -143,25 +148,65 @@ int main() { iprintf("Could not find an inserted SD card\n"); } + if (is_device_mounted()) { + load_settings(); + } + // check if we have a bootable dol if (check_load_program()) { can_load_dol = true; } - if (is_device_mounted()) { - load_settings(); - } - - iprintf("Checkup, done=%08x\n", *bs2done); - if (*bs2done == 0xCAFEBEEF) { + iprintf("Checkup, done=%08x\n", state->boot_code); + if (state->boot_code == 0xCAFEBEEF) { iprintf("He's alive! The doc's alive! He's in the old west, but he's alive!!\n"); #ifdef VIDEO_ENABLE VIDEO_WaitVSync(); #endif - // load program - load_program(); + // check for a held button + int held_max_index = -1; + u64 held_current_max = 0; + for (int i = 0; i < MAX_BUTTONS; i++) { + if (state->held_buttons[i].status == 0) continue; + + u64 ts = state->held_buttons[i].timestamp; + u32 ms = ticks_to_millisecs(ts); + + if (ms > held_current_max) { + held_max_index = i; + held_current_max = ms; + } + + iprintf("HELD: %s\n", buttons_names[i]); + } + + // only boot when held > 300ms + if (held_current_max < 300) { + held_max_index = -1; + } + + if (held_max_index != -1) { + char *button_name = buttons_names[held_max_index]; + iprintf("MAX HELD: %s\n", button_name); + char buf[64]; + + char *filename = settings.boot_buttons[held_max_index]; + if (filename == NULL) { + filename = &buf[0]; + strcpy(filename, button_name); + strcat(filename, ".dol"); + } + + if (*button_name == '_') { + filename = NULL; + } + + boot_program(filename); + } else { + boot_program(NULL); + } } if(settings.fallback_enabled) { @@ -317,6 +362,9 @@ int main() { set_patch_value(symshdr, syment, symstringdata, "cube_text_tex", (u32)image_data); set_patch_value(symshdr, syment, symstringdata, "force_progressive", settings.progressive_enabled); + set_patch_value(symshdr, syment, symstringdata, "preboot_delay_ms", settings.preboot_delay_ms); + set_patch_value(symshdr, syment, symstringdata, "postboot_delay_ms", settings.postboot_delay_ms); + // while(1); unmount_current_device(); @@ -346,6 +394,11 @@ int main() { iprintf("DONE\n"); + endts = ticks_to_millisecs(gettime()); + + u64 runtime = endts - startts; + iprintf("Runtime = %llu\n", runtime); + __lwp_thread_stopmultitasking(bs2entry); __builtin_unreachable(); diff --git a/cubeboot/source/print.c b/cubeboot/source/print.c index 1018f4a..829d309 100644 --- a/cubeboot/source/print.c +++ b/cubeboot/source/print.c @@ -29,7 +29,7 @@ int iprintf(const char *fmt, ...) { return length; } -#else +#elif defined(CONSOLE_ENABLE) || defined(GECKO_PRINT_ENABLE) int iprintf(const char *fmt, ...) { va_list args; unsigned long length; @@ -47,4 +47,6 @@ int iprintf(const char *fmt, ...) { return length; } +#else +#define iprintf(...) #endif diff --git a/cubeboot/source/sd.c b/cubeboot/source/sd.c index b5b3d20..6dc3991 100644 --- a/cubeboot/source/sd.c +++ b/cubeboot/source/sd.c @@ -1,16 +1,22 @@ #include +#include #include #include #include #include "gcode.h" +#include "gcm.h" #include "uff.h" #include "sd.h" #include "print.h" +#include "halt.h" +#include "helpers.h" +#ifndef USE_FAT_PFF #include "fatfs/diskio.h" +#endif #define countof(a) (sizeof(a)/sizeof(a[0])) @@ -22,12 +28,45 @@ static const DISC_INTERFACE *current_device = NULL; static int current_device_index = -1; static bool is_mounted = FALSE; +// static struct gcm_system_area *low_mem = (struct gcm_system_area*)0x80000000; + +gcodecmdblk blk; +gcodedrvinfo drive_info __attribute__((aligned(32))); +static int has_drive = -1; + +static void drive_info_callback(s32 result, gcodecmdblk *blk) { + if(result >= 0) { + has_drive = 1; + } else { + has_drive = 0; + } +} + + // check for inserted static int check_available_devices() { for (int i = countof(drivers) - 1; i >= 0; i--) { const DISC_INTERFACE *driver = drivers[i]; const char *dev_name = dev_names[i]; + // // skip GCLoader if we did not boot from ODE + // if (driver->ioType == DEVICE_TYPE_GAMECUBE_GCODE && low_mem->dhi.country_code != 0x03) + // continue; + + // skip ODE to speed up loading + if (driver->ioType == DEVICE_TYPE_GAMECUBE_GCODE) { + GCODE_Init(); + GCODE_InquiryAsync(&blk, &drive_info, drive_info_callback); + + // wait until done (1ms max) + for (int i = 0; i < 10; i++) { + if (has_drive >= 0) break; + udelay(100); // 100 microseconds + } + + if (drive_info.rel_date != 0x20196c64) continue; + } + iprintf("Trying mount %s\n", dev_name); if (i < MAX_DRIVE) sdgecko_setSpeed(i, EXI_SPEED32MHZ); diff --git a/cubeboot/source/sd.h b/cubeboot/source/sd.h index 3c5db17..039f8c2 100644 --- a/cubeboot/source/sd.h +++ b/cubeboot/source/sd.h @@ -7,14 +7,6 @@ typedef enum { SD_BUF_ERROR = -5 // Not enough room for output } sd_error_code; -// // Swiss: Device number. -// typedef enum { -// DEV_SDA = 0, -// DEV_SDB, -// DEV_SDC, -// DEV_MAX -// } DeviceNumber; - int mount_available_device(); int unmount_current_device(); const char *get_current_dev_name(); diff --git a/cubeboot/source/settings.c b/cubeboot/source/settings.c index c05c895..a691a76 100644 --- a/cubeboot/source/settings.c +++ b/cubeboot/source/settings.c @@ -12,6 +12,22 @@ settings_t settings; +char *buttons_names[] = { + "left", // LEFT 0x0001 + "right", // RIGHT 0x0002 + "down", // DOWN 0x0004 + "up", // UP 0x0008 + "z", // Z 0x0010 + "r", // R 0x0020 + "l", // L 0x0040 + "_", // origin 0x0080 (NOT USED) + "_", // A 0x0100 (NOT ALLOWED) + "b", // B 0x0200 + "x", // X 0x0400 + "y", // Y 0x0800 + "start", // START 0x1000 +}; + void load_settings() { memset(&settings, 0, sizeof(settings)); int config_size = get_file_size("/cubeboot.ini"); @@ -50,6 +66,13 @@ void load_settings() { settings.cube_logo = (char*)cube_logo; } + // default program + const char *default_program = ini_get(conf, "", "default_program"); + if (default_program != NULL) { + iprintf("Found default_program = %s\n", default_program); + settings.default_program = (char*)default_program; + } + // fallback enable int fallback_enabled = 0; if (!ini_sget(conf, "", "force_fallback", "%d", &fallback_enabled)) { @@ -68,5 +91,44 @@ void load_settings() { settings.progressive_enabled = progressive_enabled; } - free(config_buf); + // preboot delay + u32 preboot_delay_ms = 0; + if (!ini_sget(conf, "", "preboot_delay_ms", "%u", &preboot_delay_ms)) { + settings.preboot_delay_ms = 0; + } else { + iprintf("Found preboot_delay_ms = %u\n", preboot_delay_ms); + settings.preboot_delay_ms = preboot_delay_ms; + } + + // postboot delay + u32 postboot_delay_ms = 0; + if (!ini_sget(conf, "", "postboot_delay_ms", "%u", &postboot_delay_ms)) { + settings.postboot_delay_ms = 0; + } else { + iprintf("Found postboot_delay_ms = %u\n", postboot_delay_ms); + settings.postboot_delay_ms = postboot_delay_ms; + } + + // button presses + for (int i = 0; i < (sizeof(buttons_names) / sizeof(char *)); i++) { + char *button_name = buttons_names[i]; + + // ignore disabled buttons + if (*button_name == '_') { + continue; + } + + char button_config_name[255]; + sprintf(button_config_name, "button_%s", button_name); + + const char *dol_path = ini_get(conf, "", button_config_name); + if (dol_path != NULL) { + iprintf("Found %s = %s\n", button_config_name, dol_path); + + settings.boot_buttons[i] = (char*)dol_path; + } + } + + // // must stay allocated!! + // free(config_buf); } diff --git a/cubeboot/source/settings.h b/cubeboot/source/settings.h index c239186..57099d3 100644 --- a/cubeboot/source/settings.h +++ b/cubeboot/source/settings.h @@ -1,12 +1,19 @@ #include +#include "const.h" + typedef struct settings { u32 cube_color; char *cube_logo; u32 fallback_enabled; u32 progressive_enabled; + u32 preboot_delay_ms; + u32 postboot_delay_ms; + char *default_program; + char *boot_buttons[MAX_BUTTONS]; } settings_t; +extern char *buttons_names[]; extern settings_t settings; void load_settings(); diff --git a/cubeboot/source/state.c b/cubeboot/source/state.c new file mode 100644 index 0000000..772bc09 --- /dev/null +++ b/cubeboot/source/state.c @@ -0,0 +1,3 @@ +#include "state.h" + +cubeboot_state *state = (cubeboot_state*)0x81700000; diff --git a/cubeboot/source/state.h b/cubeboot/source/state.h new file mode 100644 index 0000000..f6d422e --- /dev/null +++ b/cubeboot/source/state.h @@ -0,0 +1,19 @@ +#include +#include "const.h" + +#define MAX_BUTTONS 13 + +#define BUTTON_UP 0 +#define BUTTON_DOWN 1 + +typedef struct cubeboot_state_t { + u32 boot_code; + u16 padding; + u16 last_buttons; + struct { + u32 status; + u64 timestamp; + } held_buttons[MAX_BUTTONS]; +} cubeboot_state; + +extern cubeboot_state *state; diff --git a/entry/source/main.c b/entry/source/main.c index 10e4f9e..d2fa894 100644 --- a/entry/source/main.c +++ b/entry/source/main.c @@ -9,8 +9,6 @@ #include -// #define DEBUG - #ifdef DEBUG #define gprintf usb_OSReport #else @@ -25,7 +23,7 @@ void _memcpy(void* dest, const void* src, int count) { *tmp++ = *s++; } -extern void _memset(void* s, int c, int count); +extern void _memset(void* s, int c, int count); // void _memset(void* s, int c, int count) { // char* xs = (char*)s; diff --git a/patches/Makefile b/patches/Makefile index dcd7c75..bdbb0bd 100644 --- a/patches/Makefile +++ b/patches/Makefile @@ -44,7 +44,7 @@ LINKER_SCRIPTS += $(PROJDIR)/linker/link_ntsc12_101.ld LINKER_SCRIPTS += $(PROJDIR)/linker/link_pal10.ld LINKER_SCRIPTS += $(PROJDIR)/linker/link_pal11.ld LINKER_SCRIPTS += $(PROJDIR)/linker/link_pal12.ld -LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map $(foreach script,$(LINKER_SCRIPTS),-T $(script)) -Wl,--gc-keep-exported -nolibc -nostdlib -nodefaultlibs +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map $(foreach script,$(LINKER_SCRIPTS),-T $(script)) -Wl,--gc-keep-exported -Wl,--nmagic -nolibc -nostdlib -nodefaultlibs #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project diff --git a/patches/include/structs.h b/patches/include/structs.h index a1980cb..9fa65bc 100644 --- a/patches/include/structs.h +++ b/patches/include/structs.h @@ -2,6 +2,7 @@ #include #include #include +#include typedef struct state_t { s32 unk0; @@ -15,20 +16,20 @@ typedef struct state_t { u16 unk5; s16 unk6; - s16 unk7; - s16 unk8; + s16 cube_side_frames; + s16 cube_corner_frames; s16 unk9; s16 unk10; u8 unk11; u8 unk12; u8 unk13; - u8 unk14; - + u8 cube_anim_done; u8 unk15; u8 unk16; - u8 unk17; - u8 unk18; + + u8 fall_anim_frames; + u8 fall_delay_frames; f32 unk19; f32 unk20; @@ -36,14 +37,14 @@ typedef struct state_t { f32 unk22; u8 unk23; - u8 unk24; - u8 unk25; + u8 up_anim_frames; + u8 up_delay_frames; u8 unk26; f32 unk27; u8 unk28; - u8 unk29; - u8 unk30; + u8 move_anim_frames; + u8 move_delay_frames; f32 unk31; f32 unk32; @@ -51,14 +52,20 @@ typedef struct state_t { f32 unk34; f32 unk35; - u8 unk36; + u8 done_delay_frames; u8 unk37; u8 unk38; - u8 unk39; - u8 unk40; + + u8 logo_hold_frames; + u8 logo_delay_frames; u8 big_size; GXColor color; + + s16 unk41; // maybe audio related + s16 audio_cue_frames_a; + s16 audio_cue_frames_b; + s16 audio_cue_frames_c; } state; typedef struct mat_t { @@ -159,3 +166,9 @@ typedef struct model_t { void *unk1; void *unk2; } model; + +typedef struct bios_pad_t { + PADStatus pad; + u16 unk0; // maybe buttons_down + u16 unk1; // maybe buttons_up +} bios_pad; diff --git a/patches/include/util.h b/patches/include/util.h index 34a6966..57b867c 100644 --- a/patches/include/util.h +++ b/patches/include/util.h @@ -121,6 +121,10 @@ inline void dump_color(char *line, GXColorS10 *input) { rgb_color rgb_temp; u32 hsl_temp; +#ifndef DEBUG + (void)hsl_temp; +#endif + if (input == NULL) { OSReport("COLOR: %s = NULL\n", line); return; diff --git a/patches/linker/link_ntsc10.ld b/patches/linker/link_ntsc10.ld index 6ddf83a..f3ccdea 100644 --- a/patches/linker/link_ntsc10.ld +++ b/patches/linker/link_ntsc10.ld @@ -8,6 +8,7 @@ ntsc10_cube_init = 0x813105b8; ntsc10_OSReport = 0x8133491c; ntsc10_rmode = 0x8135dde0; +ntsc10_pad_status = 0x8145f14c; ntsc10_cube_state = 0x81465728; ntsc10_cube_model = 0x814657f8; diff --git a/patches/linker/link_ntsc11.ld b/patches/linker/link_ntsc11.ld index 529c428..df873d0 100644 --- a/patches/linker/link_ntsc11.ld +++ b/patches/linker/link_ntsc11.ld @@ -14,6 +14,7 @@ ntsc11_cube_init = 0x813107b0; ntsc11_OSReport = 0x8135a344; ntsc11_rmode = 0x8137d9f0; +ntsc11_pad_status = 0x8148370c; ntsc11_cube_state = 0x81489e58; ntsc11_cube_model = 0x81489f28; diff --git a/patches/linker/link_ntsc12_001.ld b/patches/linker/link_ntsc12_001.ld index 1a1319a..07566a0 100644 --- a/patches/linker/link_ntsc12_001.ld +++ b/patches/linker/link_ntsc12_001.ld @@ -8,6 +8,7 @@ ntsc12_001_cube_init = 0x81310b48; ntsc12_001_OSReport = 0x81300520; ntsc12_001_rmode = 0x8137ecb8; +ntsc12_001_pad_status = 0x81484cec; ntsc12_001_cube_state = 0x8148b438; ntsc12_001_cube_model = 0x8148b508; diff --git a/patches/linker/link_ntsc12_101.ld b/patches/linker/link_ntsc12_101.ld index 41284a0..877ad05 100644 --- a/patches/linker/link_ntsc12_101.ld +++ b/patches/linker/link_ntsc12_101.ld @@ -8,6 +8,7 @@ ntsc12_101_cube_init = 0x81310b60; ntsc12_101_OSReport = 0x81300520; ntsc12_101_rmode = 0x8137f138; +ntsc12_101_pad_status = 0x8148518c; ntsc12_101_cube_state = 0x8148b8d8; ntsc12_101_cube_model = 0x8148b9a8; diff --git a/patches/linker/link_pal10.ld b/patches/linker/link_pal10.ld index 9d23495..ccef09b 100644 --- a/patches/linker/link_pal10.ld +++ b/patches/linker/link_pal10.ld @@ -8,6 +8,7 @@ pal10_cube_init = 0x81310ef0; pal10_OSReport = 0x8135d924; pal10_rmode = 0x81380fd0; +pal10_pad_status = 0x814af60c; pal10_cube_state = 0x814b5d58; pal10_cube_model = 0x814b5e28; diff --git a/patches/linker/link_pal11.ld b/patches/linker/link_pal11.ld index c02741a..0b97e82 100644 --- a/patches/linker/link_pal11.ld +++ b/patches/linker/link_pal11.ld @@ -8,6 +8,7 @@ pal11_cube_init = 0x813106dc; pal11_OSReport = 0x8135a264; pal11_rmode = 0x8137d910; +pal11_pad_status = 0x8147e3cc; pal11_cube_state = 0x81484b18; pal11_cube_model = 0x81484be8; diff --git a/patches/linker/link_pal12.ld b/patches/linker/link_pal12.ld index 2d5ccac..a382545 100644 --- a/patches/linker/link_pal12.ld +++ b/patches/linker/link_pal12.ld @@ -8,6 +8,7 @@ pal12_cube_init = 0x81311030; pal12_OSReport = 0x81300520; pal12_rmode = 0x81382470; +pal12_pad_status = 0x814b0dcc; pal12_cube_state = 0x814b7518; pal12_cube_model = 0x814b75e8; diff --git a/patches/scripts/patch-size.py b/patches/scripts/patch-size.py index f607cb0..58f4074 100755 --- a/patches/scripts/patch-size.py +++ b/patches/scripts/patch-size.py @@ -58,6 +58,21 @@ # for i in range(elf.num_sections()): # sect = elf.get_section(i) +# string polyfill +class S: + s: str + + def __init__(self, s): + self.s = s + + def removeprefix(self, prefix): + if not self.s.startswith(prefix): return self.s + return self.s[len(prefix):] + + def removesuffix(self, suffix): + if not self.s.endswith(suffix): return self.s + return self.s[:-len(suffix)] + for sect in elf.iter_sections(): if not isinstance(sect, elffile.SymbolTableSection): continue @@ -65,14 +80,14 @@ addrs = [] for sym in sect.iter_symbols(): if sym.name.startswith('_addr_'): - addr = sym.name.removeprefix('_addr_') + addr = S(sym.name).removeprefix('_addr_') if addr in addrs: continue addrs.append(addr) lines.append(f'{sym.name} = 0x{addr};\n') elif '_VER_' in sym.name and sym.name.endswith("_address"): addr = sym['st_value'] - symbol_name = sym.name.removesuffix("_address") + symbol_name = S(sym.name).removesuffix("_address") if addr not in patch_map: patch_map[addr] = [] patch_map[addr].append(symbol_name) @@ -107,8 +122,8 @@ if not sect.name.startswith('.patch.'): continue - name = sect.name.removeprefix('.patch.') - symbol_name = name.removesuffix('_func') + name = S(sect.name).removeprefix('.patch.') + symbol_name = S(name).removesuffix('_func') size_name = symbol_name + '_size' size = sect.data_size diff --git a/patches/source/arith64.c b/patches/source/arith64.c new file mode 100644 index 0000000..69e4355 --- /dev/null +++ b/patches/source/arith64.c @@ -0,0 +1,269 @@ +// GCC 32/64-bit integer arithmetic support for 32-bit systems that can't link +// to libgcc. + +// Function prototypes and descriptions are taken from +// https://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html. + +// This file may be #include'd by another file, so we try not to pollute the +// namespace and we don't import any headers. + +// All functions must be resolvable by the linker and therefore can't be inline +// or static, even if they're #included into the file where they'll be used. + +// For best performance we try to avoid branching. This makes the code a little +// weird in places. + +// See https://github.com/glitchub/arith64 for more information. +// This software is released as-is into the public domain, as described at +// https://unlicense.org. Do whatever you like with it. + +#define arith64_u64 unsigned long long int +#define arith64_s64 signed long long int +#define arith64_u32 unsigned int +#define arith64_s32 int + +typedef union +{ + arith64_u64 u64; + arith64_s64 s64; + struct + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + arith64_u32 hi; arith64_u32 lo; +#else + arith64_u32 lo; arith64_u32 hi; +#endif + } u32; + struct + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + arith64_s32 hi; arith64_s32 lo; +#else + arith64_s32 lo; arith64_s32 hi; +#endif + } s32; +} arith64_word; + +// extract hi and lo 32-bit words from 64-bit value +#define arith64_hi(n) (arith64_word){.u64=n}.u32.hi +#define arith64_lo(n) (arith64_word){.u64=n}.u32.lo + +// Negate a if b is negative, via invert and increment. +#define arith64_neg(a, b) (((a) ^ ((((arith64_s64)(b)) >= 0) - 1)) + (((arith64_s64)(b)) < 0)) +#define arith64_abs(a) arith64_neg(a, a) + +// Return the absolute value of a. +// Note LLINT_MIN cannot be negated. +arith64_s64 __absvdi2(arith64_s64 a) +{ + return arith64_abs(a); +} + +// Return the result of shifting a left by b bits. +arith64_s64 __ashldi3(arith64_s64 a, int b) +{ + arith64_word w = {.s64 = a}; + + b &= 63; + + if (b >= 32) + { + w.u32.hi = w.u32.lo << (b - 32); + w.u32.lo = 0; + } else if (b) + { + w.u32.hi = (w.u32.lo >> (32 - b)) | (w.u32.hi << b); + w.u32.lo <<= b; + } + return w.s64; +} + +// Return the result of arithmetically shifting a right by b bits. +arith64_s64 __ashrdi3(arith64_s64 a, int b) +{ + arith64_word w = {.s64 = a}; + + b &= 63; + + if (b >= 32) + { + w.s32.lo = w.s32.hi >> (b - 32); + w.s32.hi >>= 31; // 0xFFFFFFFF or 0 + } else if (b) + { + w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b); + w.s32.hi >>= b; + } + return w.s64; +} + +// These functions return the number of leading 0-bits in a, starting at the +// most significant bit position. If a is zero, the result is undefined. +int __clzsi2(arith64_u32 a) +{ + int b, n = 0; + b = !(a & 0xffff0000) << 4; n += b; a <<= b; + b = !(a & 0xff000000) << 3; n += b; a <<= b; + b = !(a & 0xf0000000) << 2; n += b; a <<= b; + b = !(a & 0xc0000000) << 1; n += b; a <<= b; + return n + !(a & 0x80000000); +} + +int __clzdi2(arith64_u64 a) +{ + int b, n = 0; + b = !(a & 0xffffffff00000000ULL) << 5; n += b; a <<= b; + b = !(a & 0xffff000000000000ULL) << 4; n += b; a <<= b; + b = !(a & 0xff00000000000000ULL) << 3; n += b; a <<= b; + b = !(a & 0xf000000000000000ULL) << 2; n += b; a <<= b; + b = !(a & 0xc000000000000000ULL) << 1; n += b; a <<= b; + return n + !(a & 0x8000000000000000ULL); +} + +// These functions return the number of trailing 0-bits in a, starting at the +// least significant bit position. If a is zero, the result is undefined. +int __ctzsi2(arith64_u32 a) +{ + int b, n = 0; + b = !(a & 0x0000ffff) << 4; n += b; a >>= b; + b = !(a & 0x000000ff) << 3; n += b; a >>= b; + b = !(a & 0x0000000f) << 2; n += b; a >>= b; + b = !(a & 0x00000003) << 1; n += b; a >>= b; + return n + !(a & 0x00000001); +} + +int __ctzdi2(arith64_u64 a) +{ + int b, n = 0; + b = !(a & 0x00000000ffffffffULL) << 5; n += b; a >>= b; + b = !(a & 0x000000000000ffffULL) << 4; n += b; a >>= b; + b = !(a & 0x00000000000000ffULL) << 3; n += b; a >>= b; + b = !(a & 0x000000000000000fULL) << 2; n += b; a >>= b; + b = !(a & 0x0000000000000003ULL) << 1; n += b; a >>= b; + return n + !(a & 0x0000000000000001ULL); +} + +// Calculate both the quotient and remainder of the unsigned division of a and +// b. The return value is the quotient, and the remainder is placed in variable +// pointed to by c (if it's not NULL). +arith64_u64 __divmoddi4(arith64_u64 a, arith64_u64 b, arith64_u64 *c) +{ + if (b > a) // divisor > numerator? + { + if (c) *c = a; // remainder = numerator + return 0; // quotient = 0 + } + if (!arith64_hi(b)) // divisor is 32-bit + { + if (b == 0) // divide by 0 + { + volatile char x = 0; x = 1 / x; // force an exception + } + if (b == 1) // divide by 1 + { + if (c) *c = 0; // remainder = 0 + return a; // quotient = numerator + } + if (!arith64_hi(a)) // numerator is also 32-bit + { + if (c) // use generic 32-bit operators + *c = arith64_lo(a) % arith64_lo(b); + return arith64_lo(a) / arith64_lo(b); + } + } + + // let's do long division + char bits = __clzdi2(b) - __clzdi2(a) + 1; // number of bits to iterate (a and b are non-zero) + arith64_u64 rem = a >> bits; // init remainder + a <<= 64 - bits; // shift numerator to the high bit + arith64_u64 wrap = 0; // start with wrap = 0 + while (bits-- > 0) // for each bit + { + rem = (rem << 1) | (a >> 63); // shift numerator MSB to remainder LSB + a = (a << 1) | (wrap & 1); // shift out the numerator, shift in wrap + wrap = ((arith64_s64)(b - rem - 1) >> 63); // wrap = (b > rem) ? 0 : 0xffffffffffffffff (via sign extension) + rem -= b & wrap; // if (wrap) rem -= b + } + if (c) *c = rem; // maybe set remainder + return (a << 1) | (wrap & 1); // return the quotient +} + +// Return the quotient of the signed division of a and b. +arith64_s64 __divdi3(arith64_s64 a, arith64_s64 b) +{ + arith64_u64 q = __divmoddi4(arith64_abs(a), arith64_abs(b), (void *)0); + return arith64_neg(q, a^b); // negate q if a and b signs are different +} + +// Return the index of the least significant 1-bit in a, or the value zero if a +// is zero. The least significant bit is index one. +int __ffsdi2(arith64_u64 a) +{ + return a ? __ctzdi2(a) + 1 : 0; +} + +// Return the result of logically shifting a right by b bits. +arith64_u64 __lshrdi3(arith64_u64 a, int b) +{ + arith64_word w = {.u64 = a}; + + b &= 63; + + if (b >= 32) + { + w.u32.lo = w.u32.hi >> (b - 32); + w.u32.hi = 0; + } else if (b) + { + w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b); + w.u32.hi >>= b; + } + return w.u64; +} + +// Return the remainder of the signed division of a and b. +arith64_s64 __moddi3(arith64_s64 a, arith64_s64 b) +{ + arith64_u64 r; + __divmoddi4(arith64_abs(a), arith64_abs(b), &r); + return arith64_neg(r, a); // negate remainder if numerator is negative +} + +// Return the number of bits set in a. +int __popcountsi2(arith64_u32 a) +{ + // collect sums into two low bytes + a = a - ((a >> 1) & 0x55555555); + a = ((a >> 2) & 0x33333333) + (a & 0x33333333); + a = (a + (a >> 4)) & 0x0F0F0F0F; + a = (a + (a >> 16)); + // add the bytes, return bottom 6 bits + return (a + (a >> 8)) & 63; +} + +// Return the number of bits set in a. +int __popcountdi2(arith64_u64 a) +{ + // collect sums into two low bytes + a = a - ((a >> 1) & 0x5555555555555555ULL); + a = ((a >> 2) & 0x3333333333333333ULL) + (a & 0x3333333333333333ULL); + a = (a + (a >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + a = (a + (a >> 32)); + a = (a + (a >> 16)); + // add the bytes, return bottom 7 bits + return (a + (a >> 8)) & 127; +} + +// Return the quotient of the unsigned division of a and b. +arith64_u64 __udivdi3(arith64_u64 a, arith64_u64 b) +{ + return __divmoddi4(a, b, (void *)0); +} + +// Return the remainder of the unsigned division of a and b. +arith64_u64 __umoddi3(arith64_u64 a, arith64_u64 b) +{ + arith64_u64 r; + __divmoddi4(a, b, &r); + return r; +} diff --git a/patches/source/const.h b/patches/source/const.h new file mode 100644 index 0000000..b1ca43b --- /dev/null +++ b/patches/source/const.h @@ -0,0 +1 @@ +#define MAX_BUTTONS 13 diff --git a/patches/source/main.c b/patches/source/main.c index 8c609f6..41fb0b7 100644 --- a/patches/source/main.c +++ b/patches/source/main.c @@ -7,6 +7,8 @@ #include "os.h" #include "usbgecko.h" +#include "state.h" +#include "time.h" #define __attribute_used__ __attribute__((used)) // #define __attribute_himem__ __attribute__((used, section(".himem"))) @@ -18,6 +20,7 @@ #define CUBE_TEX_WIDTH 84 #define CUBE_TEX_HEIGHT 84 +#define STATE_WAIT_LOAD 0x0f #define STATE_START_GAME 0x10 #define STATE_NO_DISC 0x12 @@ -32,13 +35,16 @@ __attribute_data__ u32 start_game = 0; __attribute_data__ u8 *cube_text_tex = NULL; __attribute_data__ u32 force_progressive = 0; -__attribute_used__ u32 bs2tick() { - if (start_game) { - return STATE_START_GAME; - } +__attribute_data__ static cubeboot_state local_state; +__attribute_data__ static cubeboot_state *global_state = (cubeboot_state*)0x81700000; - return STATE_NO_DISC; -} +// used if we are switching to 60Hz on a PAL IPL +__attribute_data__ static int fix_pal_ntsc = 0; + +// used for optional delays +__attribute_data__ u32 preboot_delay_ms = 0; +__attribute_data__ u32 postboot_delay_ms = 0; +__attribute_data__ u64 completed_time = 0; // used to start game __attribute_reloc__ u32 (*PADSync)(); @@ -54,6 +60,7 @@ __attribute_reloc__ void (*cube_init)(); __attribute_reloc__ void (*main)(); __attribute_reloc__ GXRModeObj *rmode; +__attribute_reloc__ bios_pad *pad_status; __attribute_reloc__ model *bg_outer_model; __attribute_reloc__ model *bg_inner_model; @@ -193,6 +200,11 @@ __attribute_used__ void mod_cube_text() { void *img_ptr = (void*)((u8*)gc_text_tex + gc_text_tex->offset); u32 img_size = wd * ht; +#ifndef DEBUG + (void)img_ptr; + (void)img_size; +#endif + OSReport("CUBE TEXT TEX: %dx%d[%d] (type=%d) @ %p\n", wd, ht, img_size, gc_text_tex->format, img_ptr); OSReport("PTR = %08x\n", (u32)cube_text_tex); OSReport("ORIG_PTR_PARTS = %08x, %08x\n", (u32)gc_text_tex, gc_text_tex->offset); @@ -213,11 +225,36 @@ __attribute_used__ void mod_cube_text() { } } + +__attribute_used__ void mod_cube_anim() { + if (fix_pal_ntsc) { + cube_state->cube_side_frames = 10; + cube_state->cube_corner_frames = 16; + cube_state->fall_anim_frames = 5; + cube_state->fall_delay_frames = 16; + cube_state->up_anim_frames = 18; + cube_state->up_delay_frames = 7; + cube_state->move_anim_frames = 10; + cube_state->move_delay_frames = 20; + cube_state->done_delay_frames = 40; + cube_state->logo_hold_frames = 60; + cube_state->logo_delay_frames = 5; + cube_state->audio_cue_frames_b = 7; + cube_state->audio_cue_frames_c = 6; + } +} + __attribute_used__ void pre_cube_init() { cube_init(); mod_cube_colors(); mod_cube_text(); + mod_cube_anim(); + + // delay before boot animation (to wait for GCVideo) + if (preboot_delay_ms) { + udelay(preboot_delay_ms * 1000); + } } __attribute_used__ void pre_main() { @@ -225,11 +262,27 @@ __attribute_used__ void pre_main() { if (force_progressive) { OSReport("Patching video mode to Progressive Scan\n"); - rmode->viTVMode |= VI_PROGRESSIVE; + fix_pal_ntsc = rmode->viTVMode >> 2 != VI_NTSC; + if (fix_pal_ntsc) { + rmode->fbWidth = 592; + rmode->efbHeight = 226; + rmode->xfbHeight = 448; + rmode->viXOrigin = 40; + rmode->viYOrigin = 16; + rmode->viWidth = 640; + rmode->viHeight = 448; + } + + rmode->viTVMode = VI_TVMODE_NTSC_PROG; rmode->xfbMode = VI_XFBMODE_SF; - static u8 progressive_vfilter[7] = {0, 0, 21, 22, 21, 0, 0}; - memcpy(rmode->vfilter, progressive_vfilter, sizeof(progressive_vfilter)); + rmode->vfilter[0] = 0; + rmode->vfilter[1] = 0; + rmode->vfilter[2] = 21; + rmode->vfilter[3] = 22; + rmode->vfilter[4] = 21; + rmode->vfilter[5] = 0; + rmode->vfilter[6] = 0; } // can't boot dol @@ -245,9 +298,55 @@ __attribute_used__ void pre_main() { __builtin_unreachable(); } +__attribute_used__ u32 get_tvmode() { + return rmode->viTVMode; +} + +__attribute_used__ u32 bs2tick() { + // TODO: move this check to PADRead in main loop + if (pad_status->pad.button != local_state.last_buttons) { + for (int i = 0; i < MAX_BUTTONS; i++) { + u16 bitmask = 1 << i; + u16 pressed = (pad_status->pad.button & bitmask) >> i; + + // button changed state + if (local_state.held_buttons[i].status != pressed) { + if (pressed) { + local_state.held_buttons[i].timestamp = gettime(); + } else { + local_state.held_buttons[i].timestamp = 0; + } + } + + local_state.held_buttons[i].status = pressed; + } + } + local_state.last_buttons = pad_status->pad.button; + + if (!completed_time && cube_state->cube_anim_done) { + OSReport("FINISHED\n"); + completed_time = gettime(); + } + + if (start_game) { + if (postboot_delay_ms) { + u64 elapsed = diff_msec(completed_time, gettime()); + if (completed_time > 0 && elapsed > postboot_delay_ms) { + return STATE_START_GAME; + } else { + return STATE_WAIT_LOAD; + } + } + return STATE_START_GAME; + } + + return STATE_NO_DISC; +} + __attribute_used__ void bs2start() { OSReport("DONE\n"); - *(u32*)0x81700000 = 0xCAFEBEEF; + memcpy(global_state, &local_state, sizeof(cubeboot_state)); + global_state->boot_code = 0xCAFEBEEF; while (!PADSync()); OSDisableInterrupts(); diff --git a/patches/source/patch.s b/patches/source/patch.s index 724a094..782edc1 100644 --- a/patches/source/patch.s +++ b/patches/source/patch.s @@ -23,4 +23,6 @@ patch_inst_pal "_replace_report" 0x8135d924 0x8135a264 0x81300520 b usb_OSRepor patch_inst_ntsc "_patch_cube_init" 0x8130e9b4 0x8130ebac 0x8130ef20 0x8130ef38 bl pre_cube_init patch_inst_pal "_patch_cube_init" 0x8130f2c8 0x8130ead8 0x8130f408 bl pre_cube_init +patch_inst_pal "_fix_video_mode_init" 0x81300520 0x81300520 0x81300610 bl get_tvmode + patch_inst_global "_patch_pre_main" 0x81300090 bl pre_main diff --git a/patches/source/state.h b/patches/source/state.h new file mode 100644 index 0000000..881d321 --- /dev/null +++ b/patches/source/state.h @@ -0,0 +1,16 @@ +#include + +#define MAX_BUTTONS 13 + +#define BUTTON_UP 0 +#define BUTTON_DOWN 1 + +typedef struct cubeboot_state_t { + u32 boot_code; + u16 padding; + u16 last_buttons; + struct { + u32 status; + u64 timestamp; + } held_buttons[MAX_BUTTONS]; +} cubeboot_state; diff --git a/patches/source/time.c b/patches/source/time.c new file mode 100644 index 0000000..db48e08 --- /dev/null +++ b/patches/source/time.c @@ -0,0 +1,68 @@ +#include +#include "time.h" + +u32 gettick(void) { + u32 result; + __asm__ __volatile__ ( + "mftb %0\n" + : "=r" (result) + ); + return result; +} + +u64 gettime(void) { + u32 tmp; + union uulc { + u64 ull; + u32 ul[2]; + } v; + + __asm__ __volatile__( + "1: mftbu %0\n\ + mftb %1\n\ + mftbu %2\n\ + cmpw %0,%2\n\ + bne 1b\n" + : "=r" (v.ul[0]), "=r" (v.ul[1]), "=&r" (tmp) + ); + return v.ull; +} + +u32 diff_sec(u64 start,u64 end) { + u64 diff; + + diff = diff_ticks(start,end); + return ticks_to_secs(diff); +} + +u32 diff_msec(u64 start,u64 end) { + u64 diff; + + diff = diff_ticks(start,end); + return ticks_to_millisecs(diff); +} + +u32 diff_usec(u64 start,u64 end) { + u64 diff; + + diff = diff_ticks(start,end); + return ticks_to_microsecs(diff); +} + +u32 diff_nsec(u64 start,u64 end) { + u64 diff; + + diff = diff_ticks(start,end); + return ticks_to_nanosecs(diff); +} + +// this function spins till timeout is reached +void udelay(unsigned us) { + unsigned long long start, end; + start = gettime(); + while (1) { + end = gettime(); + if (diff_usec(start,end) >= us) + break; + } +} diff --git a/patches/source/time.h b/patches/source/time.h new file mode 100644 index 0000000..a93a259 --- /dev/null +++ b/patches/source/time.h @@ -0,0 +1,52 @@ +#include + + +#if defined(HW_RVL) + #define TB_BUS_CLOCK 243000000u + #define TB_CORE_CLOCK 729000000u +#elif defined(HW_DOL) + #define TB_BUS_CLOCK 162000000u + #define TB_CORE_CLOCK 486000000u +#endif +#define TB_TIMER_CLOCK (TB_BUS_CLOCK/4000) //4th of the bus frequency + +#define TB_SECSPERMIN 60 +#define TB_MINSPERHR 60 +#define TB_MONSPERYR 12 +#define TB_DAYSPERYR 365 +#define TB_HRSPERDAY 24 +#define TB_SECSPERDAY (TB_SECSPERMIN*TB_MINSPERHR*TB_HRSPERDAY) +#define TB_SECSPERNYR (365*TB_SECSPERDAY) + +#define TB_MSPERSEC 1000 +#define TB_USPERSEC 1000000 +#define TB_NSPERSEC 1000000000 +#define TB_NSPERMS 1000000 +#define TB_NSPERUS 1000 +#define TB_USPERTICK 10000 + +#define ticks_to_cycles(ticks) ((((u64)(ticks)*(u64)((TB_CORE_CLOCK*2)/TB_TIMER_CLOCK))/2)) +#define ticks_to_secs(ticks) (((u64)(ticks)/(u64)(TB_TIMER_CLOCK*1000))) +#define ticks_to_millisecs(ticks) (((u64)(ticks)/(u64)(TB_TIMER_CLOCK))) +#define ticks_to_microsecs(ticks) ((((u64)(ticks)*8)/(u64)(TB_TIMER_CLOCK/125))) +#define ticks_to_nanosecs(ticks) ((((u64)(ticks)*8000)/(u64)(TB_TIMER_CLOCK/125))) + +#define tick_microsecs(ticks) ((((u64)(ticks)*8)/(u64)(TB_TIMER_CLOCK/125))%TB_USPERSEC) +#define tick_nanosecs(ticks) ((((u64)(ticks)*8000)/(u64)(TB_TIMER_CLOCK/125)))%TB_NSPERSEC) + +#define secs_to_ticks(sec) ((u64)(sec)*(TB_TIMER_CLOCK*1000)) +#define millisecs_to_ticks(msec) ((u64)(msec)*(TB_TIMER_CLOCK)) +#define microsecs_to_ticks(usec) (((u64)(usec)*(TB_TIMER_CLOCK/125))/8) +#define nanosecs_to_ticks(nsec) (((u64)(nsec)*(TB_TIMER_CLOCK/125))/8000) + +#define diff_ticks(tick0,tick1) (((u64)(tick1)<(u64)(tick0))?((u64)-1-(u64)(tick0)+(u64)(tick1)):((u64)(tick1)-(u64)(tick0))) + +u32 gettick(void); +u64 gettime(void); + +u32 diff_sec(u64 start,u64 end); +u32 diff_msec(u64 start,u64 end); +u32 diff_usec(u64 start,u64 end); +u32 diff_nsec(u64 start,u64 end); + +void udelay(unsigned us);