Skip to content
Merged
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
33 changes: 31 additions & 2 deletions shared/bitflags.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
/*
bitflags.h - definition of bit flags

Copyright (C) 2025 Retro Rewind Team

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 <https://www.gnu.org/licenses/>.
*/

#ifndef SHARED_BITFLAGS_H
#define SHARED_BITFLAGS_H

/*
These bitflags are a one-way street - the channel can pass them to the game,
but the game cannot set anything to pass back to the channel.

This is because the routine that loads the channel causes all unused memory to be zeroed.
Therefore, for things like crashed or loaded from RR, we cannot use these flags
and instead must pass these values around as ephemeral files that the channel checks for
and then deletes once read.
*/

#define RRC_BITFLAGS_SAVEGAME (1 << 0)
#define RRC_BITFLAGS_LOADED_FROM_RR (1 << 1) // set by pulsar
#define RRC_BITFLAGS_RR_CRASHED (1 << 2) // set by pulsar
// 1 << 1 and 1 << 2 currently unused

#define RRC_BITFLAGS_MY_STUFF_CTGP (1 << 3)
#define RRC_BITFLAGS_MY_STUFF_RR (1 << 4)
#define RRC_BITFLAGS_MY_STUFF_CTGP_MUSIC (1 << 5)
Expand Down
81 changes: 81 additions & 0 deletions source/crash.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

/*
crash.c - crash handler when the main game throws an exception

Copyright (C) 2025 Retro Rewind Team

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 <https://www.gnu.org/licenses/>.
*/

#include "crash.h"
#include "console.h"
#include "prompt.h"
#include "util.h"
#include "time.h"
#include "settingsfile.h"

void rrc_crash_handle(void *xfb, struct rrc_settingsfile *settings)
{
char *lines[] = {
"- - - Retro Rewind crashed! - - -",
"---------------------------------",
"This could have been caused by faulty My Stuff",
"files, online cheaters, a bug in the pack,",
"or a corrupted installation.",
};
rrc_prompt_1_option(xfb, lines, 5, "Next");

bool my_stuff_enabled = (settings->my_stuff != RRC_SETTINGSFILE_MY_STUFF_DEFAULT);

if (my_stuff_enabled)
{
char *lines2[] = {
"- - - Retro Rewind crashed! - - -",
"---------------------------------",
"It appears that you have My Stuff enabled.",
"Before reporting the crash, please try disabling it",
"and seeing if the crash still occurs."
};
rrc_prompt_1_option(xfb, lines2, 5, "OK");
}
else
{
char *lines2[] = {
"- - - Retro Rewind crashed! - - -",
"---------------------------------",
"If the crash is consistent, try reinstalling",
"Retro Rewind. Make sure to precisely follow the",
"instructions found on https://rwfc.net/downloads,",
"and do not manually delete any files.",
"",
"A crash file was written to sd:/RetroRewind6/Crash.pul.",
"If you continue to experience issues, please report it",
"along with this file to our Discord server:",
"https://discord.gg/retrorewind",
};
rrc_prompt_1_option(xfb, lines2, 11, "OK");
}
}

bool rrc_launched_after_crash()
{
FILE *f = fopen(RRC_CRASH_FILE_PATH, "r");
if (f)
{
fclose(f);
remove(RRC_CRASH_FILE_PATH);
return true;
}
return false;
}
41 changes: 41 additions & 0 deletions source/crash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
crash.h - crash handler when the main game throws an exception

Copyright (C) 2025 Retro Rewind Team

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 <https://www.gnu.org/licenses/>.
*/

#ifndef RRC_CRASH_H
#define RRC_CRASH_H

#include "settingsfile.h"

#define RRC_CRASH_FILE_PATH "/RetroRewindChannel/.crash"

/*
Run the post-crash handler.
Prompts the user about the crash and asks them if they want to upload
RetroRewind6/Crash.pul to the servers (if any such file exists).
Then clears the flag and returns.
*/
void rrc_crash_handle(void *xfb, struct rrc_settingsfile *settings);

/*
Checks for the necessary ephemeral file that indicates whether we were launched after a crash.
If it exists, deletes it and returns true. Otherwise, returns false.
*/
bool rrc_launched_after_crash();

#endif
35 changes: 35 additions & 0 deletions source/ephfile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
ephfile.c - Routines for handling of ephemeral files

Copyright (C) 2025 Retro Rewind Team

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 <https://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <gccore.h>

#include "ephfile.h"

bool rrc_launched_from_rr()
{
FILE *f = fopen(RRC_LAUNCHED_FROM_RR_FILE_PATH, "r");
if (f)
{
fclose(f);
remove(RRC_LAUNCHED_FROM_RR_FILE_PATH);
return true;
}
return false;
}
27 changes: 27 additions & 0 deletions source/ephfile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
ephfile.h - Routines for handling of ephemeral files

Copyright (C) 2025 Retro Rewind Team

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 <https://www.gnu.org/licenses/>.
*/

#ifndef RRC_EPHFILE_H
#define RRC_EPHFILE_H

#define RRC_LAUNCHED_FROM_RR_FILE_PATH "/RetroRewindChannel/.lfrr"

bool rrc_launched_from_rr();

#endif
7 changes: 6 additions & 1 deletion source/loader/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
#include "../settingsfile.h"
#include <bitflags.h>

bool rrc_signature_written()
{
return *((u32 *)RRC_SIGNATURE_ADDRESS) == RRC_SIGNATURE_VALUE;
}

/**
* Patches the DVD functions in the game DOL to immediately jump to custom DVD functions implemented in runtime-ext.
* Also allocates trampolines containing the first 4 overwritten instructions + backjump to the original function,
Expand Down Expand Up @@ -203,7 +208,7 @@ void rrc_loader_load(struct rrc_dol *dol, struct rrc_settingsfile *settings, voi
DCFlushRange((void *)0x80000000, 0x01800000);

// Signature, used by Pulsar to tell that we've loaded via the new channel instead of Riivolution.
*(u32 *)RRC_SIGNATURE_ADDRESS = 0xDEADBEEF;
*(u32 *)RRC_SIGNATURE_ADDRESS = RRC_SIGNATURE_VALUE;
*(u32 *)RRC_RUNTIME_EXT_ABI_VERSION_ADDRESS = RRC_RUNTIME_EXT_ABI_VERSION;

u8 bitflags = 0;
Expand Down
4 changes: 4 additions & 0 deletions source/loader/loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
#define RRC_BI2_SIZE 0x2000
#define RRC_PATCH_COPY_ADDRESS 0x80900000
#define RRC_SIGNATURE_ADDRESS 0x817ffff8
#define RRC_SIGNATURE_VALUE 0xDEADBEEF

bool rrc_signature_written();

#define RRC_RUNTIME_EXT_ABI_VERSION_ADDRESS 0x817ffffc
// Must be kept in sync with runtime-ext/base.ld's PROVIDE and pulsar
#define RRC_RR_BITFLAGS 0x817ffff0
Expand Down
88 changes: 51 additions & 37 deletions source/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <sys/statvfs.h>
#include <errno.h>
#include <dirent.h>
#include <bitflags.h>

#include "loader/disc_loader.h"
#include "shutdown.h"
Expand All @@ -51,6 +52,8 @@
#include "exception.h"
#include "sd.h"
#include "pad.h"
#include "crash.h"
#include "ephfile.h"

/* 100ms */
#define DISKCHECK_DELAY 100000
Expand Down Expand Up @@ -104,6 +107,7 @@ int main(int argc, char **argv)
RRC_ASSERTEQ(res, 1, "PAD_Init");
res = WPAD_Init();
RRC_ASSERTEQ(res, WPAD_ERR_NONE, "WPAD_Init");

bool first_open = false;

FILE *afd = fopen("sd:/RetroRewindChannel/accept.txt", "r");
Expand Down Expand Up @@ -152,6 +156,48 @@ int main(int argc, char **argv)
fclose(afd);
}

rrc_con_update("Load settings", 5);
struct rrc_settingsfile stored_settings;
struct rrc_result settingsfile_res = rrc_settingsfile_parse(&stored_settings);
if (rrc_result_is_error(settingsfile_res))
{
char *lines[] = {
rrc_result_strerror(settingsfile_res),
(char *)rrc_result_context(settingsfile_res),
"It may be possible to fix this by recreating the file.",
"Recreate now?",
};

enum rrc_prompt_result prompt_res = rrc_prompt_yes_no(xfb, lines, 4);
rrc_result_free(settingsfile_res);

if (prompt_res == RRC_PROMPT_RESULT_YES)
{
settingsfile_res = rrc_settingsfile_create();
if (rrc_result_is_error(settingsfile_res))
{
char *lines[] = {
"Failed to recreate settings file.",
rrc_result_strerror(settingsfile_res),
(char *)rrc_result_context(settingsfile_res),
"Defaults will be used with no changes on the SD card.",
};
rrc_prompt_1_option(xfb, lines, 4, "OK");
}
rrc_result_free(settingsfile_res);
}

// `rrc_settingsfile_parse()` always initializes the settingsfile, so even in case of an error here,
// it is initialized with defaults and we can continue with that.
}

bool crashed = false;
if(rrc_launched_after_crash())
{
crashed = true;
rrc_crash_handle(xfb, &stored_settings);
}

rrc_con_update("Initialise DVD", 10);
int fd = rrc_di_init();
RRC_ASSERT(fd != 0, "rrc_di_init");
Expand Down Expand Up @@ -196,42 +242,9 @@ int main(int argc, char **argv)
rrc_dbg_printf("FST offset: %d\n", data_header->fst_offset << 2);
rrc_dbg_printf("FST size: %d\n", data_header->fst_size << 2);

rrc_con_update("Load settings", 20);
struct rrc_settingsfile stored_settings;
struct rrc_result settingsfile_res = rrc_settingsfile_parse(&stored_settings);
if (rrc_result_is_error(settingsfile_res))
{
char *lines[] = {
rrc_result_strerror(settingsfile_res),
(char *)rrc_result_context(settingsfile_res),
"It may be possible to fix this by recreating the file.",
"Recreate now?",
};
enum rrc_prompt_result prompt_res = rrc_prompt_yes_no(xfb, lines, 4);
rrc_result_free(settingsfile_res);

if (prompt_res == RRC_PROMPT_RESULT_YES)
{
settingsfile_res = rrc_settingsfile_create();
if (rrc_result_is_error(settingsfile_res))
{
char *lines[] = {
"Failed to recreate settings file.",
rrc_result_strerror(settingsfile_res),
(char *)rrc_result_context(settingsfile_res),
"Defaults will be used with no changes on the SD card.",
};
rrc_prompt_1_option(xfb, lines, 4, "OK");
}
rrc_result_free(settingsfile_res);
}

// `rrc_settingsfile_parse()` always initializes the settingsfile, so even in case of an error here,
// it is initialized with defaults and we can continue with that.
}

// Check for updates if the user enabled that setting.
if (stored_settings.auto_update)
bool loaded_from_rr = rrc_launched_from_rr();
// Check for updates if the user enabled that setting. Skip if we loaded from the game since this will have already ran.
if (stored_settings.auto_update && !loaded_from_rr && !crashed)
{

int update_count;
Expand Down Expand Up @@ -262,7 +275,8 @@ int main(int argc, char **argv)
{
break;
}
else if (rrc_pad_b_pressed(pad) || first_open)
// Only allow auto launch if this is a successful first boot
else if (rrc_pad_b_pressed(pad) || first_open || loaded_from_rr || crashed)
{
struct rrc_result r;
int out = rrc_settings_display(xfb, &stored_settings, &r);
Expand Down
Loading
Loading