From 19b8bb81c92ecbdb0ca4bfbdb3193f9b8564d7d7 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sun, 22 Nov 2020 12:59:55 -0500 Subject: [PATCH] Add the STR RPC to overlord and game code (#134) * work in progress streaming rpc, simple test is working * actually add the test * debug windows failure * windows fix maybe * windows 2 * use str-load-status * update types --- common/util/FileUtil.cpp | 8 + common/util/FileUtil.h | 1 + decompiler/config/all-types.gc | 123 +++++----- decompiler/data/StrFileReader.cpp | 16 +- game/common/overlord_common.h | 3 + game/common/str_rpc_types.h | 37 +++ game/fake_iso.txt | 2 + game/kernel/kdgo.cpp | 25 +- game/kernel/kdgo.h | 2 + game/kernel/ksound.cpp | 4 + game/overlord/fake_iso.cpp | 25 +- game/overlord/iso.cpp | 24 +- game/overlord/iso_api.cpp | 26 +- game/overlord/iso_api.h | 9 +- game/overlord/isocommon.h | 5 +- game/overlord/stream.cpp | 135 ++++++++++- game/overlord/stream.h | 1 + game/runtime.cpp | 3 +- game/sce/sif_ee.cpp | 1 + goal_src/engine/load/load-dgo.gc | 127 ++++++++++ goal_src/engine/ps2/rpc-h.gc | 227 ++++++++++++++++++ goal_src/engine/ui/text.gc | 1 + goal_src/goal-lib.gc | 5 +- goal_src/kernel-defs.gc | 14 +- goal_src/kernel/gstring.gc | 13 + goalc/compiler/compilation/Function.cpp | 5 +- .../with_game/test-game-text.gc | 33 ++- 27 files changed, 749 insertions(+), 126 deletions(-) create mode 100644 game/common/overlord_common.h create mode 100644 game/common/str_rpc_types.h diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index e846239387..a3df4e22b6 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -307,4 +307,12 @@ void MakeISOName(char* dst, const char* src) { } *dst_ptr = 0; } + +void assert_file_exists(const char* path, const char* error_message) { + if (!std::filesystem::exists(path)) { + fprintf(stderr, "File %s was not found: %s\n", path, error_message); + assert(false); + } +} + } // namespace file_util diff --git a/common/util/FileUtil.h b/common/util/FileUtil.h index a713d91562..eb612d0fd8 100644 --- a/common/util/FileUtil.h +++ b/common/util/FileUtil.h @@ -25,4 +25,5 @@ uint32_t crc32(const uint8_t* data, size_t size); uint32_t crc32(const std::vector& data); void MakeISOName(char* dst, const char* src); void ISONameFromAnimationName(char* dst, const char* src); +void assert_file_exists(const char* path, const char* error_message); } // namespace file_util diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index 3ae62714ff..c803ea3f97 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -10311,39 +10311,43 @@ ; ) ; ) -; ;; rpc-h -; (deftype rpc-buffer (basic) -; ((elt-size uint32 :offset-assert 4) -; (elt-count uint32 :offset-assert 8) -; (elt-used uint32 :offset-assert 12) -; (busy basic :offset-assert 16) -; (base uint32 :offset-assert 20) -; (data UNKNOWN :dynamic :offset-assert 32) -; ) -; :method-count-assert 9 -; :size-assert #x20 -; :flag-assert #x900000020 -; ) +;; rpc-h +(deftype rpc-buffer (basic) + ((elt-size uint32 :offset-assert 4) + (elt-count uint32 :offset-assert 8) + (elt-used uint32 :offset-assert 12) + (busy basic :offset-assert 16) + (base pointer :offset-assert 20) + (data uint8 :dynamic :offset 32) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + (:methods + (new (symbol type uint uint) rpc-buffer 0) + ) + ) -; ;; rpc-h -; (deftype rpc-buffer-pair (basic) -; ((buffer UNKNOWN 2 :offset-assert 4) -; (current basic :offset-assert 12) -; (last-recv-buffer uint32 :offset-assert 16) -; (rpc-port int32 :offset-assert 20) -; ) -; :method-count-assert 15 -; :size-assert #x18 -; :flag-assert #xf00000018 -; (:methods -; (dummy-9 () none 9) -; (dummy-10 () none 10) -; (dummy-11 () none 11) -; (dummy-12 () none 12) -; (dummy-13 () none 13) -; (dummy-14 () none 14) -; ) -; ) +;; rpc-h +(deftype rpc-buffer-pair (basic) + ((buffer rpc-buffer 2 :offset-assert 4) + (current rpc-buffer :offset-assert 12) + (last-recv-buffer pointer :offset-assert 16) + (rpc-port int32 :offset-assert 20) + ) + :method-count-assert 15 + :size-assert #x18 + :flag-assert #xf00000018 + (:methods + (new (symbol type uint uint int) rpc-buffer-pair 0) + (call (rpc-buffer-pair uint pointer uint) int 9) + (add-element (rpc-buffer-pair) pointer 10) + (decrement-elt-used (rpc-buffer-pair) int 11) + (sync (rpc-buffer-pair symbol) int 12) + (check-busy (rpc-buffer-pair) symbol 13) + (pop-last-received (rpc-buffer-pair) pointer 14) + ) + ) ; ;; path-h ; (deftype path-control (basic) @@ -10659,7 +10663,8 @@ (b1 uint32 :offset-assert 4) (b2 uint32 :offset-assert 8) (bt uint32 :offset-assert 12) - (name uint128 :offset-assert 16) + ;(name uint128 :offset-assert 16) + (name uint8 16 :offset-assert 16) (address uint32 :offset 4) ) :method-count-assert 9 @@ -10667,30 +10672,30 @@ :flag-assert #x900000020 ) -; ;; load-dgo -; (deftype load-chunk-msg (structure) -; ((rsvd uint16 :offset-assert 0) -; (result uint16 :offset-assert 2) -; (address uint32 :offset-assert 4) -; (section uint32 :offset-assert 8) -; (maxlen uint32 :offset-assert 12) -; (id uint32 :offset-assert 4) -; (basename UNKNOWN 48 :offset-assert 16) -; ) -; :method-count-assert 9 -; :size-assert #x40 -; :flag-assert #x900000040 -; ) +;; load-dgo +(deftype load-chunk-msg (structure) + ((rsvd uint16 :offset-assert 0) + (result uint16 :offset-assert 2) + (address uint32 :offset-assert 4) + (section uint32 :offset-assert 8) + (maxlen uint32 :offset-assert 12) + (id uint32 :offset 4) + (basename uint8 48 :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) -; ;; load-dgo -; (deftype dgo-header (structure) -; ((length uint32 :offset-assert 0) -; (rootname UNKNOWN 60 :offset-assert 4) -; ) -; :method-count-assert 9 -; :size-assert #x40 -; :flag-assert #x900000040 -; ) +;; load-dgo +(deftype dgo-header (structure) + ((length uint32 :offset-assert 0) + (rootname uint8 60 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) ; ;; ramdisk ; (deftype ramdisk-rpc-fill (structure) @@ -31160,7 +31165,7 @@ (define-extern string-strip-whitespace! function) (define-extern string=? function) (define-extern string-charp= function) (define-extern string->float function) @@ -32848,13 +32853,13 @@ (define-extern dgo-load-begin function) (define-extern dgo-load-continue function) (define-extern destroy-mem function) -(define-extern str-load function) +(define-extern str-load (function string int pointer int symbol)) ;;(define-extern *load-str-rpc* object) ;; unknown type ;;(define-extern load-chunk-msg object) ;; unknown type ;;(define-extern *dgo-name* object) ;; unknown type (define-extern str-ambient-play function) ;;(define-extern *load-str-lock* object) ;; unknown type -(define-extern str-load-status function) +(define-extern str-load-status (function (pointer int32) symbol)) (define-extern str-load-cancel function) (define-extern str-play-queue function) (define-extern str-ambient-stop function) diff --git a/decompiler/data/StrFileReader.cpp b/decompiler/data/StrFileReader.cpp index 2ad3c55d17..295171e673 100644 --- a/decompiler/data/StrFileReader.cpp +++ b/decompiler/data/StrFileReader.cpp @@ -6,27 +6,17 @@ #include #include #include "common/util/FileUtil.h" -#include "game/overlord/isocommon.h" +#include "game/common/overlord_common.h" +#include "game/common/str_rpc_types.h" #include "StrFileReader.h" -// up to 64 chunks per STR file. -constexpr int SECTOR_TABLE_SIZE = 64; - -// there is a 1 sector long header -struct StrFileHeader { - u32 sectors[SECTOR_TABLE_SIZE]; // start of chunk, in sectors. including this sector. - u32 sizes[SECTOR_TABLE_SIZE]; // size of chunk, in bytes. always an integer number of sectors. - u32 pad[512 - 128]; // all zero -}; -static_assert(sizeof(StrFileHeader) == SECTOR_SIZE, "Sector header size"); - StrFileReader::StrFileReader(const std::string& file_path) { auto data = file_util::read_binary_file(file_path); assert(data.size() >= SECTOR_SIZE); // must have at least the header sector assert(data.size() % SECTOR_SIZE == 0); // should be multiple of the sector size. int end_sector = int(data.size()) / SECTOR_SIZE; - auto* header = (StrFileHeader*)data.data(); + auto* header = (StrFileHeaderSector*)data.data(); bool got_zero = false; for (int i = 0; i < SECTOR_TABLE_SIZE; i++) { diff --git a/game/common/overlord_common.h b/game/common/overlord_common.h new file mode 100644 index 0000000000..885d3e6a31 --- /dev/null +++ b/game/common/overlord_common.h @@ -0,0 +1,3 @@ +#pragma once + +constexpr int SECTOR_SIZE = 0x800; // media sector size \ No newline at end of file diff --git a/game/common/str_rpc_types.h b/game/common/str_rpc_types.h new file mode 100644 index 0000000000..f64a82e044 --- /dev/null +++ b/game/common/str_rpc_types.h @@ -0,0 +1,37 @@ +#pragma once + +#include "common/common_types.h" +#include "game/common/overlord_common.h" + +constexpr int STR_RPC_ID = 0xdeb5; +constexpr int STR_RPC_CHANNEL = 4; + +struct RPC_Str_Cmd { + u16 rsvd; // 0, seems unused + u16 result; // 2, return code. see STR_RPC_RESULT_XXX + u32 ee_addr; // 4, GOAL address to load to. + s32 chunk_id; // 8, chunk ID for chunked file. Use -1 to load a non-chunked file, which gets the + // whole file. + u32 length; // 12, length that was actually loaded + char name[64]; // file name +}; + +constexpr int STR_RPC_RESULT_ERROR = 1; +constexpr int STR_RPC_RESULT_DONE = 0; + +// maximum number of chunks in a chunked file. +constexpr int SECTOR_TABLE_SIZE = 64; + +// the header of a chunked file +struct StrFileHeader { + u32 sectors[SECTOR_TABLE_SIZE]; // start of chunk, in sectors. including this sector. + u32 sizes[SECTOR_TABLE_SIZE]; // size of chunk, in bytes. always an integer number of sectors +}; + +// the first sector of a chunked file. +struct StrFileHeaderSector : StrFileHeader { + u32 pad[512 - 128]; // all zero +}; + +static_assert(sizeof(StrFileHeader) == 0x200, "Sector header size"); +static_assert(sizeof(StrFileHeaderSector) == SECTOR_SIZE, "Sector header size"); \ No newline at end of file diff --git a/game/fake_iso.txt b/game/fake_iso.txt index 7d1c74a0fe..c58a8b24d7 100644 --- a/game/fake_iso.txt +++ b/game/fake_iso.txt @@ -8,3 +8,5 @@ TEST.CGO resources/TEST.CGO TWEAKVAL.MUS resources/TWEAKVAL.MUS VAGDIR.AYB resources/VAGDIR.AYB SCREEN1.USA resources/SCREEN1.USA +0COMMON.TXT out/iso/0COMMON.TXT +0TEST.TXT out/iso/0TEST.TXT \ No newline at end of file diff --git a/game/kernel/kdgo.cpp b/game/kernel/kdgo.cpp index e44303a9ca..edd9200e6c 100644 --- a/game/kernel/kdgo.cpp +++ b/game/kernel/kdgo.cpp @@ -16,6 +16,7 @@ #include "game/common/ramdisk_rpc_types.h" #include "game/common/loader_rpc_types.h" #include "game/common/play_rpc_types.h" +#include "game/common/str_rpc_types.h" #include "third-party/spdlog/include/spdlog/spdlog.h" using namespace ee; @@ -49,6 +50,27 @@ s32 RpcCall(s32 rpcChannel, nullptr); } +// Terrible hack! Remove soon! + +namespace { +struct RpcCallArgCache { + s32 rpcChannel; + u32 fno; + u32 async; +} rpc_arg_cache; +} // namespace + +void RpcCall_wrapper_part1(s32 rpcChannel, u32 fno, u32 async) { + rpc_arg_cache.rpcChannel = rpcChannel; + rpc_arg_cache.fno = fno; + rpc_arg_cache.async = async; +} + +u64 RpcCall_wrapper_part2(u64 send_buff, s32 send_size, u64 recv_buff, s32 recv_size) { + return RpcCall_wrapper(rpc_arg_cache.rpcChannel, rpc_arg_cache.fno, rpc_arg_cache.async, + send_buff, send_size, recv_buff, recv_size); +} + /*! * GOAL Wrapper for RpcCall. */ @@ -59,6 +81,7 @@ u64 RpcCall_wrapper(s32 rpcChannel, s32 send_size, u64 recv_buff, s32 recv_size) { + fprintf(stderr, "size in c is %d\n", recv_size); return sceSifCallRpc(&cd[rpcChannel], fno, async, Ptr(send_buff).c(), send_size, Ptr(recv_buff).c(), recv_size, nullptr, nullptr); } @@ -126,7 +149,7 @@ u32 RpcBind(s32 channel, s32 id) { u32 InitRPC() { if (!RpcBind(PLAYER_RPC_CHANNEL, PLAYER_RPC_ID) && !RpcBind(LOADER_RPC_CHANNEL, LOADER_RPC_ID) && !RpcBind(RAMDISK_RPC_CHANNEL, RAMDISK_RPC_ID) && !RpcBind(DGO_RPC_CHANNEL, DGO_RPC_ID) && - !RpcBind(4, 0xdeb5) && !RpcBind(PLAY_RPC_CHANNEL, PLAY_RPC_ID)) { + !RpcBind(STR_RPC_CHANNEL, STR_RPC_ID) && !RpcBind(PLAY_RPC_CHANNEL, PLAY_RPC_ID)) { return 0; } printf("Entering endless loop ... please wait\n"); diff --git a/game/kernel/kdgo.h b/game/kernel/kdgo.h index 0135d7e555..a717d80254 100644 --- a/game/kernel/kdgo.h +++ b/game/kernel/kdgo.h @@ -29,4 +29,6 @@ u64 RpcCall_wrapper(s32 rpcChannel, u32 RpcBusy(s32 channel); void LoadDGOTest(); +void RpcCall_wrapper_part1(s32 rpcChannel, u32 fno, u32 async); +u64 RpcCall_wrapper_part2(u64 send_buff, s32 send_size, u64 recv_buff, s32 recv_size); #endif // JAK_V2_KDGO_H diff --git a/game/kernel/ksound.cpp b/game/kernel/ksound.cpp index 3a8eb60842..0f917bc499 100644 --- a/game/kernel/ksound.cpp +++ b/game/kernel/ksound.cpp @@ -25,4 +25,8 @@ void InitSoundScheme() { make_function_symbol_from_c("rpc-call", (void*)RpcCall_wrapper); make_function_symbol_from_c("rpc-busy?", (void*)RpcBusy); make_function_symbol_from_c("test-load-dgo-c", (void*)LoadDGOTest); + + // terrible hack! + make_function_symbol_from_c("rpc-call-p1", (void*)RpcCall_wrapper_part1); + make_function_symbol_from_c("rpc-call-p2", (void*)RpcCall_wrapper_part2); } \ No newline at end of file diff --git a/game/overlord/fake_iso.cpp b/game/overlord/fake_iso.cpp index db3843a480..6971de0c33 100644 --- a/game/overlord/fake_iso.cpp +++ b/game/overlord/fake_iso.cpp @@ -79,26 +79,10 @@ void fake_iso_init_globals() { int FS_Init(u8* buffer) { (void)buffer; - // get path to next/data/fake_iso.txt, the map file. - char fakeiso_path[512]; - strcpy(fakeiso_path, file_util::get_file_path({"game", "fake_iso.txt"}).c_str()); - - // open the map. - FILE* fp = fopen(fakeiso_path, "r"); - assert(fp); - fseek(fp, 0, SEEK_END); - size_t len = ftell(fp); - rewind(fp); - char* fakeiso = (char*)malloc(len + 1); - if (fread(fakeiso, len, 1, fp) != 1) { -#ifdef __linux__ - assert(false); -#endif - } - fakeiso[len] = '\0'; + auto config_str = file_util::read_text_file(file_util::get_file_path({"game", "fake_iso.txt"})); + const char* ptr = config_str.c_str(); // loop over lines - char* ptr = fakeiso; while (*ptr) { // newlines while (*ptr && *ptr == '\n') @@ -127,7 +111,7 @@ int FS_Init(u8* buffer) { } i = 0; - while (*ptr && (*ptr != '\n') && (*ptr != ' ') && i < 128) { + while (*ptr && (*ptr != '\n') && (*ptr != ' ') && (*ptr != EOF) && i < 128) { e->file_path[i] = *ptr; ptr++; i++; @@ -145,8 +129,6 @@ int FS_Init(u8* buffer) { sFiles[i].location = i; } - free(fakeiso); - // TODO load tweak music. return 0; @@ -201,6 +183,7 @@ static const char* get_file_path(FileRecord* fr) { */ uint32_t FS_GetLength(FileRecord* fr) { const char* path = get_file_path(fr); + file_util::assert_file_exists(path, "fake_iso FS_GetLength"); FILE* fp = fopen(path, "rb"); assert(fp); fseek(fp, 0, SEEK_END); diff --git a/game/overlord/iso.cpp b/game/overlord/iso.cpp index 2991140223..6380cc00e0 100644 --- a/game/overlord/iso.cpp +++ b/game/overlord/iso.cpp @@ -43,7 +43,7 @@ s32 str_thread; s32 play_thread; u8 gVagDir[VAGDIR_SIZE]; u32 gPlayPos; -RPC_Dgo_Cmd sRPCBuff[1]; // todo move... +static RPC_Dgo_Cmd sRPCBuff[1]; // todo move... DgoCommand scmd; void iso_init_globals() { @@ -171,16 +171,16 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { return 1; } - // thread_param.attr = TH_C; - // thread_param.initPriority = 97; - // thread_param.stackSize = 0x800; - // thread_param.option = 0; - // thread_param.entry = (void*)STRThread; - // strcpy(thread_param.name, "STRThread"); - // str_thread = CreateThread(&thread_param); - // if(str_thread <= 0) { - // return 1; - // } + thread_param.attr = TH_C; + thread_param.initPriority = 97; + thread_param.stackSize = 0x800; + thread_param.option = 0; + thread_param.entry = (void*)STRThread; + strcpy(thread_param.name, "STRThread"); + str_thread = CreateThread(&thread_param); + if (str_thread <= 0) { + return 1; + } // // thread_param.attr = TH_C; // thread_param.initPriority = 97; @@ -196,7 +196,7 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { // Start the threads! StartThread(iso_thread, 0); StartThread(dgo_thread, 0); - // StartThread(str_thread, 0); + StartThread(str_thread, 0); // StartThread(play_thread, 0); // wait for ISO Thread to initialize diff --git a/game/overlord/iso_api.cpp b/game/overlord/iso_api.cpp index 3ea3f37038..fa4395dc8a 100644 --- a/game/overlord/iso_api.cpp +++ b/game/overlord/iso_api.cpp @@ -7,7 +7,7 @@ using namespace iop; /*! * Load a File to IOP memory (blocking) */ -void LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) { +s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) { // printf("[OVERLORD] LoadISOFileToIOP %s, %d/%d bytes\n", file->name, length, file->size); spdlog::debug("[OVERLORD] LoadISOFileToIOP {}, {}/{} bytes", file->name, length, file->size); IsoCommandLoadSingle cmd; @@ -23,12 +23,14 @@ void LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) { if (cmd.status) { cmd.length_to_copy = 0; } + + return cmd.length_to_copy; } /*! * Load a File to IOP memory (blocking) */ -void LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) { +s32 LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) { // printf("[OVERLORD] LoadISOFileToEE %s, %d/%d bytes\n", file->name, length, file->size); spdlog::debug("[OVERLORD] LoadISOFileToEE {}, {}/{} bytes", file->name, length, file->size); IsoCommandLoadSingle cmd; @@ -44,4 +46,24 @@ void LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) { if (cmd.status) { cmd.length_to_copy = 0; } + + return cmd.length_to_copy; +} + +s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length, uint32_t offset) { + spdlog::debug("[OVERLORD] LoadISOFileChunkToEE {} : {} offset {}\n", file->name, length, offset); + IsoCommandLoadSingle cmd; + cmd.cmd_id = LOAD_TO_EE_OFFSET_CMD_ID; + cmd.messagebox_to_reply = 0; + cmd.thread_id = GetThreadId(); + cmd.file_record = file; + cmd.dest_addr = (u8*)(u64)dest_addr; + cmd.length = length; + cmd.offset = offset; + SendMbx(iso_mbx, &cmd); + SleepThread(); + if (cmd.status) { + cmd.length_to_copy = 0; + } + return cmd.length_to_copy; } diff --git a/game/overlord/iso_api.h b/game/overlord/iso_api.h index caaadb4953..59fcb427f1 100644 --- a/game/overlord/iso_api.h +++ b/game/overlord/iso_api.h @@ -1,10 +1,7 @@ #pragma once -#ifndef JAK_V2_ISO_API_H -#define JAK_V2_ISO_API_H #include "isocommon.h" -void LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length); -void LoadISOFileToEE(FileRecord* file, uint32_t ee_addr, uint32_t length); - -#endif // JAK_V2_ISO_API_H +s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length); +s32 LoadISOFileToEE(FileRecord* file, uint32_t ee_addr, uint32_t length); +s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length, uint32_t offset); diff --git a/game/overlord/isocommon.h b/game/overlord/isocommon.h index b5e29169fe..8c0e43693b 100644 --- a/game/overlord/isocommon.h +++ b/game/overlord/isocommon.h @@ -11,6 +11,7 @@ #include #include "common/common_types.h" #include "common/link_types.h" +#include "game/common/overlord_common.h" constexpr int PRI_STACK_LENGTH = 4; // number of queued commands per priority constexpr int N_PRIORITIES = 4; // number of priorities @@ -30,7 +31,6 @@ constexpr int LOAD_TO_IOP_CMD_ID = 0x101; // command to load to iop constexpr int LOAD_TO_EE_OFFSET_CMD_ID = 0x102; // command to load file to ee with offset. constexpr int LOAD_DGO_CMD_ID = 0x200; // command to load DGO -constexpr int SECTOR_SIZE = 0x800; // media sector size constexpr int MAX_ISO_FILES = 350; // maximum files on FS constexpr int MAX_OPEN_FILES = 16; // maximum number of open files at a time. @@ -51,7 +51,7 @@ struct FileRecord { */ struct LoadStackEntry { FileRecord* fr; - uint32_t location; + uint32_t location; // sectors. }; /*! @@ -185,5 +185,6 @@ extern IsoFs* isofs; extern s32 iso_mbx; void MakeISOName(char* dst, const char* src); +void ISONameFromAnimationName(char* dst, const char* src); #endif // JAK_V2_ISOCOMMON_H diff --git a/game/overlord/stream.cpp b/game/overlord/stream.cpp index 7e3397d80c..0fdc5acc4b 100644 --- a/game/overlord/stream.cpp +++ b/game/overlord/stream.cpp @@ -1,12 +1,141 @@ -#include -#include "stream.h" +/*! + * @file stream.cpp + * OVERLORD streaming driver. + * Supports loading a file directly to the EE, or loading chunks of a chunked file. + */ +#include +#include "stream.h" +#include "game/sce/iop.h" +#include "game/common/str_rpc_types.h" +#include "game/overlord/isocommon.h" +#include "game/overlord/iso_api.h" + +using namespace iop; + +static RPC_Str_Cmd sRPCBuf; +void* RPC_STR(unsigned int fno, void* _cmd, int y); + +/*! + * We cache the chunk file headers so we can avoid seeking to the chunk header each time we + * need to load another chunk, even if we load chunks out of order. + */ +struct CacheEntry { + // the record for the chunk file described. + FileRecord* fr = nullptr; + // counts down from INT32_MAX - 1 each time we have a cache miss. + s32 countdown = 0; + // the actual cached data. + StrFileHeader header; +}; + +// the actual header cache. +constexpr int STR_INDEX_CACHE_SIZE = 4; +CacheEntry sCache[STR_INDEX_CACHE_SIZE]; + +void stream_init_globals() { + memset(&sRPCBuf, 0, sizeof(RPC_Str_Cmd)); +} + +/*! + * Run the STR RPC handler. + */ u32 STRThread() { - assert(false); + sceSifQueueData dq; + sceSifServeData serve; + + CpuDisableIntr(); + sceSifInitRpc(0); + sceSifSetRpcQueue(&dq, GetThreadId()); + sceSifRegisterRpc(&serve, STR_RPC_ID, RPC_STR, &sRPCBuf, nullptr, nullptr, &dq); + CpuEnableIntr(); + sceSifRpcLoop(&dq); return 0; } u32 PLAYThread() { assert(false); return 0; +} + +/*! + * The STR RPC handler. + */ +void* RPC_STR(unsigned int fno, void* _cmd, int y) { + (void)fno; + (void)y; + auto* cmd = (RPC_Str_Cmd*)_cmd; + printf("RPC STR runs!\n"); + if (cmd->chunk_id < 0) { + // it's _not_ a stream file. So we just treat it like a normal load. + + // find the file with the given name + auto file_record = isofs->find(cmd->name); + if (file_record == nullptr) { + // file not found! + printf("[OVERLORD STR] Failed to find file %s for loading.\n", cmd->name); + cmd->result = STR_RPC_RESULT_ERROR; + } else { + // load directly to the EE + cmd->length = LoadISOFileToEE(file_record, cmd->ee_addr, cmd->length); + if (cmd->length) { + // successful load! + cmd->result = STR_RPC_RESULT_DONE; + } else { + // there was an error loading. + cmd->result = STR_RPC_RESULT_ERROR; + } + } + } else { + // it's a chunked file. These are only animations - these have a separate naming scheme. + char animation_iso_name[128]; + ISONameFromAnimationName(animation_iso_name, cmd->name); + auto file_record = isofs->find_in(animation_iso_name); + + if (!file_record) { + // didn't find the file + printf("[OVERLORD STR] Failed to find animation %s\n", cmd->name); + cmd->result = STR_RPC_RESULT_ERROR; + } else { + // found it! See if we've cached this animation's header. + int cache_entry = 0; + int oldest = INT32_MAX; + int oldest_idx = -1; + while (cache_entry < STR_INDEX_CACHE_SIZE && sCache[cache_entry].fr != file_record) { + sCache[cache_entry].countdown--; + if (sCache[cache_entry].countdown < oldest) { + oldest_idx = cache_entry; + oldest = sCache[cache_entry].countdown; + } + cache_entry++; + } + + if (cache_entry == STR_INDEX_CACHE_SIZE) { + // cache miss, we need to load the header to the header cache on the IOP + cache_entry = oldest_idx; + sCache[oldest_idx].fr = file_record; + sCache[oldest_idx].countdown = INT32_MAX - 1; + if (!LoadISOFileToIOP(file_record, &sCache[oldest_idx].header, sizeof(StrFileHeader))) { + printf("[OVERLORD STR] Failed to load chunk file header for animation %s\n", cmd->name); + cmd->result = 1; + return cmd; + } + } + + // load data, using the cached header to find the location of the chunk. + if (!LoadISOFileChunkToEE(file_record, cmd->ee_addr, + sCache[cache_entry].header.sizes[cmd->chunk_id], + sCache[cache_entry].header.sectors[cmd->chunk_id])) { + printf("[OVERLORD STR] Failed to load chunk %d for animation %s\n", cmd->chunk_id, + cmd->name); + cmd->result = 1; + } else { + // successful load! + cmd->length = sCache[cache_entry].header.sizes[cmd->chunk_id]; + cmd->result = 0; + } + } + } + printf("Command result %d\n", cmd->result); + return cmd; } \ No newline at end of file diff --git a/game/overlord/stream.h b/game/overlord/stream.h index a06ac74e66..70bb710f2b 100644 --- a/game/overlord/stream.h +++ b/game/overlord/stream.h @@ -6,5 +6,6 @@ #include "common/common_types.h" u32 STRThread(); u32 PLAYThread(); +void stream_init_globals(); #endif // JAK_V2_STREAM_H diff --git a/game/runtime.cpp b/game/runtime.cpp index 0bbee5e4e2..37bfbf2587 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -44,6 +44,7 @@ #include "game/overlord/iso_cd.h" #include "game/overlord/overlord.h" #include "game/overlord/srpc.h" +#include "game/overlord/stream.h" #include "common/goal_constants.h" @@ -183,7 +184,7 @@ void iop_runner(SystemThreadInterface& iface) { // soundcommon srpc_init_globals(); // ssound - // stream + stream_init_globals(); iface.initialization_complete(); diff --git a/game/sce/sif_ee.cpp b/game/sce/sif_ee.cpp index 5fbe9de35a..b9e93571b5 100644 --- a/game/sce/sif_ee.cpp +++ b/game/sce/sif_ee.cpp @@ -85,6 +85,7 @@ s32 sceSifCallRpc(sceSifClientData* bd, assert(!end_para); assert(mode == 1); // async iop->kernel.sif_rpc(bd->rpcd.id, fno, mode, send, ssize, recv, rsize); + iop->signal_run_iop(); return 0; } diff --git a/goal_src/engine/load/load-dgo.gc b/goal_src/engine/load/load-dgo.gc index 4dcd38ef1c..a6b47b2880 100644 --- a/goal_src/engine/load/load-dgo.gc +++ b/goal_src/engine/load/load-dgo.gc @@ -5,3 +5,130 @@ ;; name in dgo: load-dgo ;; dgos: GAME, ENGINE +(deftype load-dgo-msg (structure) + ((rsvd uint16 :offset-assert 0) + (result uint16 :offset-assert 2) + (b1 uint32 :offset-assert 4) + (b2 uint32 :offset-assert 8) + (bt uint32 :offset-assert 12) + ;(name uint128 :offset-assert 16) + (name uint8 16 :offset-assert 16) + (address uint32 :offset 4) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +#| +struct RPC_Dgo_Cmd { + uint16_t rsvd; + uint16_t result; + uint32_t buffer1; + uint32_t buffer2; + uint32_t buffer_heap_top; + char name[16]; +}; +|# + +(deftype load-chunk-msg (structure) + ((rsvd uint16 :offset-assert 0) + (result uint16 :offset-assert 2) + (address uint32 :offset-assert 4) + (section uint32 :offset-assert 8) + (maxlen uint32 :offset-assert 12) + (id uint32 :offset 4) + (basename uint8 48 :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +#| +struct RPC_Str_Cmd { + u16 rsvd; // 0, seems unused + u16 result; // 2, return code. see STR_RPC_RESULT_XXX + u32 ee_addr; // 4, GOAL address to load to. + s32 chunk_id; // 8, chunk ID for chunked file. Use -1 to load a non-chunked file, which gets the + // whole file. + u32 length; // 12, length that was actually loaded + char name[64]; // file name +}; +|# + +(deftype dgo-header (structure) + ((length uint32 :offset-assert 0) + (rootname uint8 60 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) + +#| +struct DgoHeader { + u32 object_count; + char name[60]; +}; +|# + +(define-extern *load-dgo-rpc* rpc-buffer-pair) +(when (= 0 (the int *load-dgo-rpc*)) + ;; we need to allocate the rpc buffers + (set! *load-dgo-rpc* (new 'global 'rpc-buffer-pair (the uint 32) (the uint 1) 3)) ;; todo, constants + (define *load-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 1) 4)) ;; todo, constants + (define *play-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 2) 5)) + (define *load-str-lock* '#f) + (define *que-str-lock* '#f) + (define *dgo-name* (new 'global 'string 64 (the string '#f))) + ) + +(defun str-load ((name string) (chunk-id int) (address pointer) (len int)) + "Begin a streaming load if possible! + We must be able to grab the lock, and no streaming load in progress. + Return if we actually start the load." + ;; call method 13 + (when (or (check-busy *load-str-rpc*) + *load-str-lock* + ) + (return-from #f '#f) + ) + ;; ok, we are good to start a load. begin by adding an element to the RPC buffer + (let ((cmd (the load-chunk-msg (add-element *load-str-rpc*)))) + (set! (-> cmd result) 666) + (set! (-> cmd address) address) + (set! (-> cmd section) chunk-id) + (set! (-> cmd maxlen) len) + (charp<-string (-> cmd basename) name) + (call *load-str-rpc* (the uint 0) (the pointer cmd) (the uint 32)) + (set! *load-str-lock* '#t) + (set! *que-str-lock* '#t) + '#t + ) + ) + +(defun str-load-status ((length-out (pointer int32))) + "Check the status of the str load. + The 'busy status indicates it is still going + The 'error status indicates the load failed. + The 'complete status means the load is finished, and length-out contains the loaded length." + + ;; still going.. + (if (check-busy *load-str-rpc*) + (return-from #f 'busy) + ) + + ;; not busy, we can free the lock + (set! *load-str-lock* '#f) + (set! *que-str-lock* '#t) + ;; grab the response + (let ((response (the load-chunk-msg (pop-last-received *load-str-rpc*)))) + (if (= 1 (-> response result)) + (return-from #f 'error) + ) + ;; no error! + (set! (-> length-out) (the int (-> response maxlen))) + 'complete + ) + ) \ No newline at end of file diff --git a/goal_src/engine/ps2/rpc-h.gc b/goal_src/engine/ps2/rpc-h.gc index e074ce0144..5824c81c6b 100644 --- a/goal_src/engine/ps2/rpc-h.gc +++ b/goal_src/engine/ps2/rpc-h.gc @@ -5,3 +5,230 @@ ;; name in dgo: rpc-h ;; dgos: GAME, ENGINE +;; an RPC buffer is a container of elements to send to the IOP. +;; each element is size elt-size, and there are maximum of elt-count elements +;; it is possible to use fewer elements than elt-count. +;; the buffer is 64-byte aligned. +(deftype rpc-buffer (basic) + ((elt-size uint32 :offset-assert 4) + (elt-count uint32 :offset-assert 8) + (elt-used uint32 :offset-assert 12) + (busy basic :offset-assert 16) ;; are we being sent currently? + (base pointer :offset-assert 20) ;; 64-byte aligned buffer of elts. + ;; I suspect this was 16-byte aligned for DMA purposes. + (data uint8 :dynamic :offset 32) + ) + (:methods + (new (symbol type uint uint) rpc-buffer 0) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(defmethod new rpc-buffer ((allocation symbol) (type-to-make type) (elt-size uint) (elt-count uint)) + "Create a new rpc-buffer with room for elt-count elements of elt-size. + The element array is 64-byte aligned." + ;; we make room for a buffer of size elt-size * elt-count that is _64 byte_ aligned. + (let ((obj (object-new (the int (+ (-> type-to-make size) 63 (* elt-size elt-count)))))) + (set! (-> obj elt-size) elt-size) + (set! (-> obj elt-count) elt-count) + (set! (-> obj elt-used) 0) + (set! (-> obj busy) '#f) + ;(set! (-> obj base) (logand -64 (+ (the uint obj) 28 63))) + ;; base is the 64-byte aligned buffer. + (set! (-> obj base) (the pointer (logand -64 (+ (the uint (-> obj data)) 63)))) + obj + ) + ) + +;; An RPC buffer pair is a pair of two buffers that implement double buffering. +;; The "current" buffer is the one being loaded on the EE. +;; The other is referred to as the active buffer. +;; This also supports receiving data, though it just gives you a plain pointer. +(deftype rpc-buffer-pair (basic) + ((buffer rpc-buffer 2 :offset-assert 4) ;; the two buffers + (current rpc-buffer :offset-assert 12) ;; the buffer being loaded + (last-recv-buffer pointer :offset-assert 16) ;; the last reply + (rpc-port int32 :offset-assert 20) ;; the RPC port number + ) + :method-count-assert 15 + :size-assert #x18 + :flag-assert #xf00000018 + (:methods + (new (symbol type uint uint int) rpc-buffer-pair 0) + (call (rpc-buffer-pair uint pointer uint) int 9) + (add-element (rpc-buffer-pair) pointer 10) + (decrement-elt-used (rpc-buffer-pair) int 11) + (sync (rpc-buffer-pair symbol) int 12) + (check-busy (rpc-buffer-pair) symbol 13) + (pop-last-received (rpc-buffer-pair) pointer 14) + ) + ) + +(defmethod new rpc-buffer-pair ((allocation symbol) (type-to-make type) (elt-size uint) (elt-count uint) (rpc-port int)) + "Create a new rpc-buffer-pair" + (let ((obj (object-new))) + (set! (-> obj buffer 0) (new 'global 'rpc-buffer elt-size elt-count)) + (set! (-> obj buffer 1) (new 'global 'rpc-buffer elt-size elt-count)) + (set! (-> obj current) (-> obj buffer 0)) + (set! (-> obj last-recv-buffer) (the pointer '#f)) + (set! (-> obj rpc-port) rpc-port) + obj + ) + ) + +;; method 12 +(defmethod sync rpc-buffer-pair ((obj rpc-buffer-pair) (print-stall-warning symbol)) + "Wait for the in progress RPC to complete." + (let ((active-buffer (if (= (-> obj buffer 0) (-> obj current)) + (-> obj buffer 1) + (-> obj buffer 0)) + ) + ) + + (when (-> active-buffer busy) + ;; the flag is set, meaning we should check. + (cond + ((!= 0 (rpc-busy? (-> obj rpc-port))) + ;; we're busy + (if print-stall-warning + (format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> obj rpc-port)) + ) + (while (!= 0 (rpc-busy? (-> obj rpc-port))) + ;; real game has a bunch of nops + (+ 1 2 3) + ) + ) + (else + ;; not actually busy, clear the flag! + (set! (-> active-buffer busy) '#f) + (set! (-> active-buffer elt-used) 0) + ) + ) + ) + + ) + 0 + ) + +;; method 13 +(defmethod check-busy rpc-buffer-pair ((obj rpc-buffer-pair)) + "Is the currently running RPC still busy?" + (let ((active-buffer (if (= (-> obj buffer 0) (-> obj current)) + (-> obj buffer 1) + (-> obj buffer 0) + ))) + (when (-> active-buffer busy) + (if (!= 0 (rpc-busy? (-> obj rpc-port))) + (return-from #f '#t) + ) + (set! (-> active-buffer busy) '#f) + (set! (-> active-buffer elt-count) 0) + ) + ) + '#f + ) + +(defmacro hack-rpc-call (a0 a1 a2 a3 a4 a5 a6) + `(begin + (rpc-call-p1 ,a0 ,a1 ,a2) + (rpc-call-p2 ,a3 ,a4 ,a5 ,a6) + ) + ) + +;; method 9 +(defmethod call rpc-buffer-pair ((obj rpc-buffer-pair) (fno uint) (recv-buff pointer) (recv-size uint)) + "Call an RPC. This is an async RPC. Use check-busy or sync to see if it's done." + (when (!= 0 (-> obj current elt-used)) + ;; when we have used elements + (format 0 "call rpc-buffer-pair with ~D elts~%" (-> obj current elt-used)) + + ;; make sure the previous buffer is done + (let ((active-buffer (if (= (-> obj buffer 0) (-> obj current)) + (-> obj buffer 1) + (-> obj buffer 0)))) + (when (-> active-buffer busy) + ;; we think the active buffer may be busy. + ;; first lets just do a simple check + (cond + ((!= 0 (rpc-busy? (-> obj rpc-port))) + ;; busy! print an error and stall! + (format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> obj rpc-port)) + (while (!= 0 (rpc-busy? (-> obj rpc-port))) + (+ 1 2 3) + ) + ) + (else + ;; not busy. + (set! (-> active-buffer busy) '#f) + (set! (-> active-buffer elt-used) 0) + ) + ) + ) + ;; now we've cleared the last RPC call, we can do another + (let ((current-buffer (-> obj current))) + ;; rpc_channel, fno, async, send_buff, send_size, recv_buff, recv_size + (format 0 "recv-size is ~D~%" recv-size) + (hack-rpc-call (-> obj rpc-port) + fno + (the uint 1) + (the uint (-> current-buffer base)) + (the int (* (-> current-buffer elt-used) (-> current-buffer elt-size))) + (the uint recv-buff) + (the int recv-size) + ) + (set! (-> current-buffer busy) '#t) + (set! (-> obj last-recv-buffer) recv-buff) + (set! (-> obj current) active-buffer) + ) + ) + ) + 0 + ) + + +;; method 14 +(defmethod pop-last-received rpc-buffer-pair ((obj rpc-buffer-pair)) + (let ((result (-> obj last-recv-buffer))) + (set! (-> obj last-recv-buffer) (the rpc-buffer '#f)) + result + ) + ) + +;; method 10 +(defmethod add-element rpc-buffer-pair ((obj rpc-buffer-pair)) + "Add an element, and return a pointer to the element. + If we are out of elements, flush by doing an RPC call. + DANGER: this uses all arguments of 0. If you want something else, flush it yourself. + If we're RPC 0 and we do this auto-flush, print a warning. + " + (let ((current-buffer (-> obj current))) + (when (= (-> current-buffer elt-count) (-> current-buffer elt-used)) + ;; oops, we're full. + (if (= 0 (-> obj rpc-port)) + ;; if we're RPC 0, this is evidently a situation to warn about. + (format 0 "WARNING: too many sound commands queued~%") + ) + ;; otherwise we just flush + ;; seems kinda dangerous. these could be the wrong parameters... + (call obj (the uint 0) (the pointer 0) (the uint 0)) + ;; update the current-buffer. + (set! current-buffer (-> obj current)) + ) + (let ((result (&+ (-> current-buffer base) (* (-> current-buffer elt-size) (-> current-buffer elt-used))))) + (+! (-> current-buffer elt-used) 1) + result + ) + ) + ) + + +;; method 11 +(defmethod decrement-elt-used rpc-buffer-pair ((obj rpc-buffer-pair)) + "If elt-used > 0, decrease it by one." + (when (> (-> obj current elt-used) 0) + (-! (-> obj current elt-used) 1) + ) + 0 + ) \ No newline at end of file diff --git a/goal_src/engine/ui/text.gc b/goal_src/engine/ui/text.gc index 0f8e65b8f2..0fb173e8e6 100644 --- a/goal_src/engine/ui/text.gc +++ b/goal_src/engine/ui/text.gc @@ -53,3 +53,4 @@ ;; todo print-game-text ;; todo disable-level-text-file-loading ;; todo enable-level-text-file-loading + \ No newline at end of file diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index bd6aa02549..aee952fb94 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -352,6 +352,9 @@ `(logand #xfffffff0 (+ (the-as integer ,value) 15)) ) +(defmacro align64 (value) + `(logand -64 (+ (the-as int ,value) 63)) + ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; TYPE STUFF @@ -410,7 +413,7 @@ (defmacro object-new (&rest sz) (if (null? sz) `(the ,(current-method-type) ((method object new) allocation type-to-make (the int (-> type-to-make size)))) - `(the ,(current-method-type)((method object new) allocation type-to-make ,@sz)) + `(the ,(current-method-type) ((method object new) allocation type-to-make ,@sz)) ) ) diff --git a/goal_src/kernel-defs.gc b/goal_src/kernel-defs.gc index cfaa8d9eec..858a0aa97b 100644 --- a/goal_src/kernel-defs.gc +++ b/goal_src/kernel-defs.gc @@ -76,7 +76,7 @@ (define-extern kmalloc (function kheap int int string)) (define-extern new-dynamic-structure (function kheap type int structure)) (define-extern method-set! (function type int function none)) ;; may actually return function. -(define-extern link (function pointer string int kheap int pointer)) +(define-extern link (function pointer pointer int kheap int pointer)) (define-extern dgo-load (function string kheap int int none)) (define-extern link-begin (function pointer string int kheap int int)) (define-extern link-resume (function int)) @@ -145,4 +145,14 @@ ;; *stack-size* ;; *kernel-boot-message* ;; *kernel-boot-mode* -;; *kernel-boot-level* \ No newline at end of file +;; *kernel-boot-level* + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; ksound - InitSoundScheme +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-extern rpc-call (function int uint uint uint int uint int uint)) +(define-extern rpc-busy? (function int uint)) +;; hack! remove! +(define-extern rpc-call-p1 (function int uint uint none)) +(define-extern rpc-call-p2 (function uint int uint int uint)) \ No newline at end of file diff --git a/goal_src/kernel/gstring.gc b/goal_src/kernel/gstring.gc index 87d698ba95..c509ffb6da 100644 --- a/goal_src/kernel/gstring.gc +++ b/goal_src/kernel/gstring.gc @@ -57,4 +57,17 @@ ) ) ) + ) + +(defun charp<-string ((dst (pointer uint8)) (src-string string)) + "Copy a GOAL string into a character array." + (let ((src (-> src-string data))) + (while (!= 0 (-> src)) + (set! (-> dst) (-> src)) + (&+! dst 1) + (&+! src 1) + ) + (set! (-> dst) 0) + 0 + ) ) \ No newline at end of file diff --git a/goalc/compiler/compilation/Function.cpp b/goalc/compiler/compilation/Function.cpp index d99b1e9603..c82d3824b4 100644 --- a/goalc/compiler/compilation/Function.cpp +++ b/goalc/compiler/compilation/Function.cpp @@ -441,10 +441,11 @@ Val* Compiler::compile_real_function_call(const goos::Object& form, } for (uint32_t i = 0; i < args.size(); i++) { if (method_type_name.empty()) { - typecheck(form, function->type().get_arg(i), args.at(i)->type(), "function argument"); + typecheck(form, function->type().get_arg(i), args.at(i)->type(), + fmt::format("function argument {}", i)); } else { typecheck(form, function->type().get_arg(i).substitute_for_method_call(method_type_name), - args.at(i)->type(), "function argument"); + args.at(i)->type(), fmt::format("function argument {}", i)); } } } diff --git a/test/goalc/source_templates/with_game/test-game-text.gc b/test/goalc/source_templates/with_game/test-game-text.gc index 9fcac6f8a0..461745555e 100644 --- a/test/goalc/source_templates/with_game/test-game-text.gc +++ b/test/goalc/source_templates/with_game/test-game-text.gc @@ -1,7 +1,38 @@ (start-test "game-text") -(let ((text (the game-text-info (load "$ISO/0TEST.TXT" *common-text-heap*)))) +(defun hack-load () + ;(declare (inline)) + (str-load "0TEST.TXT" + -1 + (the pointer (align64 (-> *common-text-heap* current))) + ; note, this max size is probably wrong because of the alignment. + (the int (&- (-> *common-text-heap* top) (-> *common-text-heap* current))) + ) + + + (sync *load-str-rpc* '#t) + + (let* ((got-length (new 'stack 'array 'int32 1)) + (status (str-load-status got-length))) + ;(format #t "Status is ~A, length ~D~%" status (-> got-length)) + + ;; failed! + (if (!= status 'complete) + (return-from #f #f) + ) + + (link (the pointer (align64 (-> *common-text-heap* current))) + (-> "test" data) + (-> got-length) + *common-text-heap* + 0 + ) + ) + + ) + +(let ((text (the game-text-info (hack-load)))) (format 0 "~I~%" text) (expect-true (= #x123 (-> text data 0 id))) (expect-true (= #x456 (-> text data 1 id)))