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
This commit is contained in:
water111 2020-11-22 12:59:55 -05:00 committed by GitHub
parent 460ec874bb
commit 19b8bb81c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 749 additions and 126 deletions

View file

@ -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

View file

@ -25,4 +25,5 @@ uint32_t crc32(const uint8_t* data, size_t size);
uint32_t crc32(const std::vector<uint8_t>& 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

View file

@ -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-get-flag!! function)
(define-extern charp<-string function)
(define-extern charp<-string (function (pointer uint8) string int))
(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)

View file

@ -6,27 +6,17 @@
#include <cassert>
#include <cstring>
#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++) {

View file

@ -0,0 +1,3 @@
#pragma once
constexpr int SECTOR_SIZE = 0x800; // media sector size

View file

@ -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");

View file

@ -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

View file

@ -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<u8>(send_buff).c(), send_size,
Ptr<u8>(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");

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -11,6 +11,7 @@
#include <string>
#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

View file

@ -1,12 +1,141 @@
#include <assert.h>
#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 <cassert>
#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;
}

View file

@ -6,5 +6,6 @@
#include "common/common_types.h"
u32 STRThread();
u32 PLAYThread();
void stream_init_globals();
#endif // JAK_V2_STREAM_H

View file

@ -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();

View file

@ -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;
}

View file

@ -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
)
)

View file

@ -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
)

View file

@ -53,3 +53,4 @@
;; todo print-game-text
;; todo disable-level-text-file-loading
;; todo enable-level-text-file-loading

View file

@ -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))
)
)

View file

@ -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*
;; *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))

View file

@ -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
)
)

View file

@ -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));
}
}
}

View file

@ -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)))