Files
massimilianodelliubaldini 4c2e1a8a90 Remove 128 character buffer causing long filenames to crash gk (#3771)
Several users have reported that ArchipelaGOAL is not launching
properly, even when using the OpenGOAL Launcher. The window pops up with
a black screen, and then quits. The only way they can run it is if they
double click gk.exe.

This comes down to the Launcher providing gk.exe with the `config_path`
parameter, which leads the program to find `archipelagoal-settings.gc`.
Here is an example from me:

```
D:\Applications\Games\OpenGOAL\features\jak1\mods\JakMods\_settings\archipelagoal\OpenGOAL\jak1\settings/Mods/archipelagoal-settings.gc
```

If a user's base OpenGOAL install directory is long enough, this path
becomes longer than 128 characters. This overflows the character buffer
in `kopen` which is used to open file streams. If you're only slightly
over the limit like myself, at 135 characters, you may not have noticed
a problem. But some users have paths a little longer, like 168
characters, and they report the issue is consistent.

Water111 suggested we remove the 128 character buffer and use the
filename data directly. This fix requires no changes to the Launcher,
just to the kernel, and every mod could stand to benefit from this fix.
2024-11-28 17:14:09 -05:00

644 lines
23 KiB
C++

#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
#include "kmachine.h"
#include <string>
#include "common/log/log.h"
#include "common/symbols.h"
#include "common/util/FileUtil.h"
#include "common/util/string_util.h"
#include "game/external/discord_jak1.h"
#include "game/graphics/display.h"
#include "game/graphics/gfx.h"
#include "game/graphics/sceGraphicsInterface.h"
#include "game/kernel/common/fileio.h"
#include "game/kernel/common/kboot.h"
#include "game/kernel/common/kdgo.h"
#include "game/kernel/common/kdsnetm.h"
#include "game/kernel/common/kernel_types.h"
#include "game/kernel/common/klink.h"
#include "game/kernel/common/kmachine.h"
#include "game/kernel/common/kmalloc.h"
#include "game/kernel/common/kprint.h"
#include "game/kernel/common/kscheme.h"
#include "game/kernel/common/ksocket.h"
#include "game/kernel/common/ksound.h"
#include "game/kernel/common/memory_layout.h"
#include "game/kernel/jak1/kboot.h"
#include "game/kernel/jak1/kdgo.h"
#include "game/kernel/jak1/klisten.h"
#include "game/kernel/jak1/kscheme.h"
#include "game/kernel/jak1/ksound.h"
#include "game/sce/deci2.h"
#include "game/sce/libcdvd_ee.h"
#include "game/sce/libdma.h"
#include "game/sce/libgraph.h"
#include "game/sce/sif_ee.h"
#include "game/sce/stubs.h"
using namespace ee;
namespace jak1 {
/*!
* Initialize global variables based on command line parameters. Not called in retail versions,
* but it is present in the ELF.
* DONE
* Modified to use std::string, and removed call to fflush.
*/
void InitParms(int argc, const char* const* argv) {
// Modified default settings:
if (argc == 1) {
DiskBoot = 1;
isodrv = fakeiso;
modsrc = 0;
reboot_iop = 0;
DebugSegment = 0;
MasterDebug = 0;
}
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
// DVD Settings
// ----------------------------
// the "cd" mode uses the DVD drive for everything. This is how the game runs in retail
if (arg == "-cd") {
Msg(6, "dkernel: cd mode\n");
isodrv = iso_cd; // use the actual DVD drive for data files
modsrc = 1; // use the DVD drive data for IOP modules
reboot_iop = 1; // Reboot the IOP (load new IOP runtime)
}
// the "cddata" uses the DVD drive for everything but IOP modules.
if (arg == "-cddata") {
Msg(6, "dkernel: cddata mode\n");
isodrv = iso_cd; // tell IOP to use actual DVD drive for data files
modsrc = 0; // don't use DVD drive for IOP modules
reboot_iop = 0; // no need to reboot the IOP
}
// the "deviso" mode is one of two modes for testing without the need for DVDs
if (arg == "-deviso") {
Msg(6, "dkernel: deviso mode\n");
isodrv = deviso; // IOP deviso mode
modsrc = 0; // no IOP module loading (there's no DVD to load from!)
reboot_iop = 0;
}
// the "fakeiso" mode is the other of two modes for testing without the need for DVDs
if (arg == "-fakeiso") {
Msg(6, "dkernel: fakeiso mode\n");
isodrv = fakeiso; // IOP fakeeiso mode
modsrc = 0; // no IOP module loading (there's no DVD to load from!)
reboot_iop = 0;
}
// an added mode to allow booting without a KERNEL.CGO for testing
if (arg == "-nokernel") {
Msg(6, "dkernel: no kernel mode\n");
MasterUseKernel = false;
}
// an added mode to allow booting without sound for testing
if (arg == "-nosound") {
Msg(6, "dkernel: no sound mode\n");
masterConfig.disable_sound = true;
}
// GOAL Settings
// ----------------------------
// the "demo" mode is used to pass the message "demo" to the gkernel in the DebugBootMessage
// (instead of play)
if (arg == "-demo") {
Msg(6, "dkernel: demo mode\n");
kstrcpy(DebugBootMessage, "demo");
}
// the "boot" mode is used to set GOAL up for running the game in retail mode
if (arg == "-boot") {
Msg(6, "dkernel: boot mode\n");
MasterDebug = 0;
DiskBoot = 1;
DebugSegment = 0;
}
// the "debug" mode is used to set GOAL up for debugging/development
if (arg == "-debug") {
Msg(6, "dkernel: debug mode\n");
MasterDebug = 1;
DebugSegment = 1;
}
// the "debug-mem" mode is used to set up GOAL in debug mode, but not to load debug-segments
if (arg == "-debug-mem") {
Msg(6, "dkernel: debug-mem mode\n");
MasterDebug = 1;
DebugSegment = 0;
}
// the "-level [level-name]" mode is used to inform the game to boot a specific level
// the default level is "#f".
if (arg == "-level") {
i++;
std::string levelName = argv[i];
Msg(6, "dkernel: level %s\n", levelName.c_str());
kstrcpy(DebugBootLevel, levelName.c_str());
}
}
}
/*!
* Initialize the I/O Processor
* Removed calls to exit(0) if loading modules fails.
*/
void InitIOP() {
// before doing anything with the I/O Processor, we need to set up SIF RPC
sceSifInitRpc(0);
if ((isodrv == iso_cd) || modsrc || reboot_iop) {
// we will need the DVD drive to bring up the IOP
InitCD();
}
if (!reboot_iop) {
// reboot with development IOP kernel
lg::debug("Rebooting IOP...");
while (!sceSifRebootIop("host0:/usr/local/sce/iop/modules/ioprp221.img")) {
lg::debug("Failed, retrying");
}
while (!sceSifSyncIop()) {
lg::debug("Syncing...");
}
} else {
// reboot with IOP kernel off of the disk
// reboot with development IOP kernel
lg::debug("Rebooting IOP...");
while (!sceSifRebootIop("cdrom0:\\DRIVERS\\IOPRP221.IMG;1")) {
lg::debug("Failed, retrying");
}
while (!sceSifSyncIop()) {
lg::debug("Syncing...");
}
}
// now that the IOP is booted with the correct kernel, we need to connect SIF RPC again
sceSifInitRpc(0);
// if we plan to get files off of the DVD drive, we get ready to load files again.
// resetting the file system may not be needed here, but it does not hurt.
if ((isodrv == iso_cd) || modsrc) {
InitCD();
sceFsReset();
}
// we begin putting together a boot command for OVERLORD, the IOP driver, which must know the data
// source and the name of the boot splash screen of the game.
char overlord_boot_command[256];
char* cmd = overlord_boot_command;
kstrcpy(cmd, init_types[(int)isodrv]);
cmd = cmd + strlen(cmd) + 1;
kstrcpy(cmd, "SCREEN1.USA");
cmd = cmd + strlen(cmd) + 1;
if (masterConfig.disable_sound) {
kstrcpy(cmd, "-nosound");
cmd = cmd + strlen(cmd) + 1;
}
int total_len = cmd - overlord_boot_command;
if (modsrc == fakeiso) {
// load from network
if (sceSifLoadModule("host0:/usr/local/sce/iop/modules/sio2man.irx", 0, nullptr) < 0) {
MsgErr("loading sio2man.irx failed\n");
}
if (sceSifLoadModule("host0:/usr/local/sce/iop/modules/padman.irx", 0, nullptr) < 0) {
MsgErr("loading padman.irx failed\n");
}
if (sceSifLoadModule("host0:/usr/local/sce/iop/modules/libsd.irx", 0, nullptr) < 0) {
MsgErr("loading libsd.irx failed\n");
}
if (sceSifLoadModule("host0:/usr/local/sce/iop/modules/mcman.irx", 0, nullptr) < 0) {
MsgErr("loading mcman.irx failed\n");
}
if (sceSifLoadModule("host0:/usr/local/sce/iop/modules/mcserv.irx", 0, nullptr) < 0) {
MsgErr("loading mcserv.irx failed\n");
}
if (sceSifLoadModule("host0:/usr/home/src/989snd10/iop/989snd.irx", 0, nullptr) < 0) {
MsgErr("loading 989snd.irx failed\n");
}
sceSifLoadModule("host0:/usr/home/src/989snd10/iop/989ERR.IRX", 0, nullptr);
lg::debug("Initializing CD library...");
auto rv = sceSifLoadModule("host0:binee/overlord.irx", total_len, overlord_boot_command);
if (rv < 0) {
MsgErr("loading overlord.irx failed\n");
}
} else {
// load from DVD drive
if (sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\SIO2MAN.IRX;1", 0, nullptr) < 0) {
MsgErr("loading sio2man.irx failed\n");
}
if (sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\PADMAN.IRX;1", 0, nullptr) < 0) {
MsgErr("loading padman.irx failed\n");
}
if (sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\LIBSD.IRX;1", 0, nullptr) < 0) {
MsgErr("loading libsd.irx failed\n");
}
if (sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\MCMAN.IRX;1", 0, nullptr) < 0) {
MsgErr("loading mcman.irx failed\n");
}
if (sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\MCSERV.IRX;1", 0, nullptr) < 0) {
MsgErr("loading mcserv.irx failed\n");
}
if (sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\989SND.IRX;1", 0, nullptr) < 0) {
MsgErr("loading 989snd.irx failed\n");
}
lg::debug("Initializing CD library in ISO_CD mode...");
auto rv =
sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\OVERLORD.IRX;1", total_len, overlord_boot_command);
if (rv < 0) {
MsgErr("loading overlord.irx failed\n");
}
}
auto rv = sceMcInit();
if (rv < 0) {
MsgErr("MC driver init failed %d\n", rv);
} else {
lg::info("InitIOP OK");
}
}
AutoSplitterBlock g_auto_splitter_block_jak1;
/*!
* Initialize GOAL Runtime. This is the main initialization which is called before entering
* the GOAL kernel dispatch loop (KernelCheckAndDispatch).
* TODO finish up things which are commented.
*/
int InitMachine() {
u32 debug_heap_end = (0xffffffff - DEBUG_HEAP_SPACE_FOR_STACK + 1) & 0x7ffffff;
// initialize the global heap
u32 global_heap_size = GLOBAL_HEAP_END - HEAP_START;
float size_mb = ((float)global_heap_size) / (float)(1 << 20);
lg::info("gkernel: global heap 0x{:08x} to 0x{:08x} (size {:.3f} MB)", HEAP_START,
GLOBAL_HEAP_END, size_mb);
kinitheap(kglobalheap, Ptr<u8>(HEAP_START), global_heap_size);
// initialize the debug heap, if appropriate
if (MasterDebug) {
u32 debug_heap_size = debug_heap_end - DEBUG_HEAP_START;
kinitheap(kdebugheap, Ptr<u8>(DEBUG_HEAP_START), debug_heap_size);
float debug_size_mb = ((float)debug_heap_size) / (float)(1 << 20);
float gap_size_mb = ((float)DEBUG_HEAP_START - GLOBAL_HEAP_END) / (float)(1 << 20);
lg::info("gkernel: debug heap 0x{:08x} to 0x{:08x} (size {:.3f} MB, gap {:.3f} MB)",
DEBUG_HEAP_START, debug_heap_end, debug_size_mb, gap_size_mb);
} else {
// if no debug, we make the kheapinfo structure NULL so GOAL knows not to use it.
kdebugheap.offset = 0;
}
init_output(); // GOAL input/output buffer setup
jak1::InitIOP(); // start IOP/OVERLORD, loading our legal splash screen
// sceGsResetPath(); // reset VIF1, VU1, GIF
InitVideo(); // display legal splash screen
// FlushCache(WRITEBACK_DCACHE);
// FlushCache(INVALIDATE_ICACHE);
// sceGsSyncV(0); // wait for it to show up on the screen
//
// if(scePadInit(0) != 1) { // init controllers
// MsgErr("dkernel: !init pad\n");
// }
if (MasterDebug) { // connect to GOAL compiler
InitGoalProto();
} else {
// shut down the deci2 stuff, we don't need it.
ee::sceDeci2Disable();
}
lg::info("InitSound");
InitSound(); // do nothing!
lg::info("InitRPC");
InitRPC(); // connect to IOP
reset_output(); // reset output buffers
clear_print();
s32 goal_status = InitHeapAndSymbol(); // init GOAL runtime, load kernel and engine
if (goal_status < 0) {
return goal_status;
}
// TODO - better place to put this?
// TODO - yes, see jak2's code!
g_auto_splitter_block_jak1.pointer_to_symbol =
(u64)g_ee_main_mem + intern_from_c("*autosplit-info-jak1*")->value;
lg::info("InitListenerConnect");
InitListenerConnect();
lg::info("InitCheckListener");
InitCheckListener();
Msg(6, "kernel: machine started\n");
return 0;
}
/*!
* Shutdown the runtime.
*/
int ShutdownMachine() {
StopIOP();
CloseListener();
ShutdownSound();
ShutdownGoalProto();
Msg(6, "kernel: machine shutdown\n");
return 0;
}
/*!
* Open a file-stream. Name is a GOAL string. Mode is a GOAL symbol. Use 'read for readonly
* and anything else for write only.
*/
u64 kopen(u64 fs, u64 name, u64 mode) {
auto file_stream = Ptr<FileStream>(fs).c();
file_stream->mode = mode;
file_stream->name = name;
file_stream->flags = 0;
lg::print("****** CALL TO kopen() ******\n");
// sprintf(buffer, "host:%s", Ptr<String>(name)->data());
if (!strcmp(info(Ptr<Symbol>(mode))->str->data(), "read")) {
file_stream->file = sceOpen(Ptr<String>(name)->data(), SCE_RDONLY);
} else {
// 0x602
file_stream->file = sceOpen(Ptr<String>(name)->data(), SCE_TRUNC | SCE_CREAT | SCE_WRONLY);
}
return fs;
}
void PutDisplayEnv(u32 ptr) {
// we can mostly ignore this, except for one value that sets the 'blackout' amount.
u8 alp = Ptr<u8>(ptr).c()[1];
auto* renderer = Gfx::GetCurrentRenderer();
if (renderer) {
renderer->set_pmode_alp(alp / 255.f);
}
}
void update_discord_rpc(u32 discord_info) {
if (gDiscordRpcEnabled) {
DiscordRichPresence rpc;
char state[128];
char large_image_key[128];
char large_image_text[128];
char small_image_key[128];
char small_image_text[128];
auto info = discord_info ? Ptr<DiscordInfo>(discord_info).c() : NULL;
if (info) {
int cells = (int)*Ptr<float>(info->fuel).c();
int orbs = (int)*Ptr<float>(info->money_total).c();
int scout_flies = (int)*Ptr<float>(info->buzzer_total).c();
int deaths = *Ptr<int>(info->deaths).c();
float time = *Ptr<float>(info->time_of_day).c();
auto cutscene = Ptr<Symbol>(info->cutscene)->value;
auto ogreboss = Ptr<Symbol>(info->ogreboss)->value;
auto plantboss = Ptr<Symbol>(info->plantboss)->value;
auto racer = Ptr<Symbol>(info->racer)->value;
auto flutflut = Ptr<Symbol>(info->flutflut)->value;
char* status = Ptr<String>(info->status).c()->data();
char* level = Ptr<String>(info->level).c()->data();
const char* full_level_name =
get_full_level_name(level_names, level_name_remap, Ptr<String>(info->level).c()->data());
memset(&rpc, 0, sizeof(rpc));
if (!indoors(indoor_levels, level)) {
char level_with_tod[128];
strcpy(level_with_tod, level);
strcat(level_with_tod, "-");
strcat(level_with_tod, time_of_day_str(time));
strcpy(large_image_key, level_with_tod);
} else {
strcpy(large_image_key, level);
}
strcpy(large_image_text, full_level_name);
if (!strcmp(full_level_name, "unknown")) {
strcpy(large_image_key, full_level_name);
strcpy(large_image_text, level);
}
rpc.largeImageKey = large_image_key;
if (!strcmp(level, "finalboss")) {
strcpy(state, "Fighting Final Boss");
} else if (plantboss != offset_of_s7()) {
strcpy(state, "Fighting Dark Eco Plant");
rpc.largeImageKey = "plant-boss";
strcpy(large_image_text, "Dark Eco Plant");
} else if (ogreboss != offset_of_s7()) {
strcpy(state, "Fighting Klaww");
rpc.largeImageKey = "ogreboss";
strcpy(large_image_text, "Klaww");
} else if (!strcmp(level, "title")) {
strcpy(state, "On title screen");
rpc.largeImageKey = "title";
strcpy(large_image_text, "Title screen");
} else if (!strcmp(level, "intro")) {
strcpy(state, "Intro");
} else if (cutscene != offset_of_s7()) {
strcpy(state, "Watching a cutscene");
strcpy(large_image_text, fmt::format("Cells: {} | Orbs: {} | Flies: {} | Deaths: {}",
std::to_string(cells), std::to_string(orbs),
std::to_string(scout_flies), std::to_string(deaths))
.c_str());
} else {
strcpy(state, fmt::format("Cells: {} | Orbs: {} | Flies: {}", std::to_string(cells),
std::to_string(orbs), std::to_string(scout_flies))
.c_str());
strcat(large_image_text, fmt::format(" | Deaths: {}", std::to_string(deaths)).c_str());
}
rpc.largeImageText = large_image_text;
rpc.state = state;
if (racer != offset_of_s7()) {
strcpy(small_image_key, "target-racer");
strcpy(small_image_text, "Driving A-Grav Zoomer");
} else if (flutflut != offset_of_s7()) {
strcpy(small_image_key, "flutflut");
strcpy(small_image_text, "Riding on Flut Flut");
} else {
if (!indoors(indoor_levels, level)) {
strcpy(small_image_key, time_of_day_str(time));
strcpy(small_image_text, "Time of day: ");
strcat(small_image_text, get_time_of_day(time).c_str());
} else {
strcpy(small_image_key, "");
strcpy(small_image_text, "");
}
}
rpc.smallImageKey = small_image_key;
rpc.smallImageText = small_image_text;
rpc.startTimestamp = gStartTime;
rpc.details = status;
rpc.partySize = 0;
rpc.partyMax = 0;
Discord_UpdatePresence(&rpc);
}
} else {
Discord_ClearPresence();
}
}
void pc_set_levels(u32 l0, u32 l1) {
if (!Gfx::GetCurrentRenderer()) {
return;
}
std::string l0s = Ptr<String>(l0).c()->data();
std::string l1s = Ptr<String>(l1).c()->data();
std::vector<std::string> levels;
if (l0s != "none" && l0s != "#f") {
levels.push_back(l0s);
}
if (l1s != "none" && l1s != "#f") {
levels.push_back(l1s);
}
Gfx::GetCurrentRenderer()->set_levels(levels);
}
void InitMachine_PCPort() {
// PC Port added functions
init_common_pc_port_functions(
make_function_symbol_from_c,
[](const char* name) {
const auto result = intern_from_c(name);
InternFromCInfo info{};
info.offset = result.offset;
return info;
},
make_string_from_c);
// Game specific functions
// Called from the game thread at each frame to tell the PC rendering code which levels to start
// loading. The loader internally handles locking.
make_function_symbol_from_c("__pc-set-levels", (void*)pc_set_levels);
make_function_symbol_from_c("pc-discord-rpc-update", (void*)update_discord_rpc);
// setup string constants
// TODO - these may be able to be moved into `init_common_pc_port_functions` but it's trickier
// since they are accessing the Ptr's value
auto user_dir_path = file_util::get_user_config_dir();
intern_from_c("*pc-user-dir-base-path*")->value =
make_string_from_c(user_dir_path.string().c_str());
auto settings_path = file_util::get_user_settings_dir(g_game_version);
intern_from_c("*pc-settings-folder*")->value = make_string_from_c(settings_path.string().c_str());
intern_from_c("*pc-settings-built-sha*")->value = make_string_from_c(build_revision().c_str());
}
/*!
* Final initialization of the system after the kernel is loaded.
* This is called from InitHeapAndSymbol at the very end.
* Exports the last of the functions written in C to the GOAL symbol table
* If DiskBooting, will load the GAME CGO, containing the engine, and calls "play", the function
* which should prepare the game engine.
*/
void InitMachineScheme() {
make_function_symbol_from_c("put-display-env", (void*)PutDisplayEnv); // used in drawable
make_function_symbol_from_c("syncv", (void*)sceGsSyncV); // used in drawable
make_function_symbol_from_c("sync-path", (void*)sceGsSyncPath); // used
make_function_symbol_from_c("reset-path", (void*)sceGsResetPath); // used in dma
make_function_symbol_from_c("reset-graph", (void*)sceGsResetGraph); // used
make_function_symbol_from_c("dma-sync", (void*)sceDmaSync); // used
make_function_symbol_from_c("gs-put-imr", (void*)sceGsPutIMR); // unused
make_function_symbol_from_c("gs-get-imr", (void*)sceGsGetIMR); // unused
make_function_symbol_from_c("gs-store-image", (void*)sceGsExecStoreImage); // used
make_function_symbol_from_c("flush-cache", (void*)FlushCache); // used
make_function_symbol_from_c("cpad-open", (void*)CPadOpen); // used
make_function_symbol_from_c("cpad-get-data", (void*)CPadGetData); // used
make_function_symbol_from_c("install-handler", (void*)InstallHandler); // used
make_function_symbol_from_c("install-debug-handler", (void*)InstallDebugHandler); // used
make_function_symbol_from_c("file-stream-open", (void*)kopen); // used
make_function_symbol_from_c("file-stream-close", (void*)kclose); // used
make_function_symbol_from_c("file-stream-length", (void*)klength); // used
make_function_symbol_from_c("file-stream-seek", (void*)kseek); // unused
make_function_symbol_from_c("file-stream-read", (void*)kread); // used
make_function_symbol_from_c("file-stream-write", (void*)kwrite); // used
make_function_symbol_from_c("scf-get-language", (void*)DecodeLanguage); // used
make_function_symbol_from_c("scf-get-time", (void*)DecodeTime); // used
make_function_symbol_from_c("scf-get-aspect", (void*)DecodeAspect); // used
make_function_symbol_from_c("scf-get-volume", (void*)DecodeVolume); // used
make_function_symbol_from_c("scf-get-territory", (void*)DecodeTerritory); // used
make_function_symbol_from_c("scf-get-timeout", (void*)DecodeTimeout); // used
make_function_symbol_from_c("scf-get-inactive-timeout", (void*)DecodeInactiveTimeout); // used
make_function_symbol_from_c("dma-to-iop", (void*)dma_to_iop); // unused
make_function_symbol_from_c("kernel-shutdown", (void*)jak1::KernelShutdown); // used TODO jak1
make_function_symbol_from_c("aybabtu", (void*)sceCdMmode); // used
InitMachine_PCPort();
InitSoundScheme();
intern_from_c("*stack-top*")->value = 0x07ffc000;
intern_from_c("*stack-base*")->value = 0x07ffffff;
intern_from_c("*stack-size*")->value = 0x4000;
if (DiskBoot) {
intern_from_c("*kernel-boot-message*")->value = intern_from_c(DebugBootMessage).offset;
intern_from_c("*kernel-boot-mode*")->value = intern_from_c("boot").offset; // or debug-boot
intern_from_c("*kernel-boot-level*")->value = intern_from_c(DebugBootLevel).offset;
}
// todo remove MasterUseKernel
if (DiskBoot && MasterUseKernel) {
*EnableMethodSet = (*EnableMethodSet) + 1;
load_and_link_dgo_from_c("game", kglobalheap,
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
0x400000, true);
*EnableMethodSet = (*EnableMethodSet) - 1;
using namespace jak1_symbols;
kernel_packages->value =
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE).cast<u32>()),
make_string_from_c("engine"), kernel_packages->value);
kernel_packages->value =
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE).cast<u32>()),
make_string_from_c("art"), kernel_packages->value);
kernel_packages->value =
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE).cast<u32>()),
make_string_from_c("common"), kernel_packages->value);
lg::info("calling play");
call_goal_function_by_name("play");
}
}
} // namespace jak1
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(__clang__)
#pragma clang diagnostic pop
#endif