jak2: overlord rework (#2544)

Fixes #2545
Fixes #2546
Fixes #2547
Fixes #2548
Fixes #2549
Fixes #2550
Fixes #2551
Fixes #2552
Fixes #2553
Fixes #2554
Fixes #2555
Fixes #2556
Fixes #2557
Fixes #2558
Fixes #2559
Fixes #2560
Fixes #2561
Fixes #2562
Fixes #2563
Fixes #2564
Fixes #2565
Fixes #2567
Fixes #2566
Fixes #2568
Fixes #2569
Fixes #2570
Fixes #2522
Fixes #2571

---------

Co-authored-by: water <awaterford111445@gmail.com>
Co-authored-by: Hat Kid <6624576+Hat-Kid@users.noreply.github.com>
Co-authored-by: ManDude <7569514+ManDude@users.noreply.github.com>
This commit is contained in:
Tyler Wilding
2023-04-29 15:13:57 -05:00
committed by GitHub
parent 2feb231105
commit 5e987cc0e2
121 changed files with 10961 additions and 3877 deletions
+2 -1
View File
@@ -34,6 +34,7 @@ void walk_tree(TSTreeCursor* cursor, std::string& output, const std::string& sou
uint32_t start = ts_node_start_byte(curr_node);
uint32_t end = ts_node_end_byte(curr_node);
const char* type = ts_node_type(curr_node);
(void)type;
// TODO - if it's a string literal, take out any newlines and reflow the string to the
// line-length
const auto contents = source_code.substr(start, end - start);
@@ -109,7 +110,7 @@ void format_code(const std::string& source,
return;
}
const std::string curr_node_type = ts_node_type(curr_node);
for (int i = 0; i < ts_node_child_count(curr_node); i++) {
for (size_t i = 0; i < ts_node_child_count(curr_node); i++) {
auto child_node = ts_node_child(curr_node, i);
// If we are opening a list, peek at the first element in the list
// this is so we can properly handle indentation based on different forms
@@ -59,6 +59,10 @@ void GlobalProfiler::instant_event(const char* name) {
event(name, ProfNode::INSTANT);
}
void GlobalProfiler::root_event() {
instant_event("ROOT");
}
void GlobalProfiler::begin_event(const char* name) {
event(name, ProfNode::BEGIN);
}
+1
View File
@@ -24,6 +24,7 @@ class GlobalProfiler {
void clear();
void set_enable(bool en);
void dump_to_json(const std::string& path);
void root_event();
private:
std::atomic_bool m_enabled = false;
+22
View File
@@ -373,6 +373,19 @@ std::string convert_to_unix_path_separators(const std::string& path) {
#endif
}
/*!
* Convert an animation name to ISO name.
* The animation name is a bunch of dash separated words.
* The resulting ISO name has the same first two chars as the animation name, and one char from each
* remaining word. Once there are no more words but remaining chars in the ISO name, the ith extra
* char is the i+1 th char of the last word. A word ending in a number (or just a number) is turned
* into the number. The word "resolution" becomes z. The word "accept" becomes y. The word "reject"
* becomes n. Other words become the first char of the word. The result is uppercased and the file
* extension is STR Examples (animation name and disc file name, not ISO name):
* green-sagecage-outro-beat-boss-enough-cells -> GRSOBBEC.STR
* swamp-tetherrock-swamprockexplode-4 -> SWTS4.STR
* minershort-resolution-1-orbs -> MIZ1ORBS.STR
*/
void ISONameFromAnimationName(char* dst, const char* src) {
// The Animation Name is a bunch of words separated by dashes
@@ -457,6 +470,15 @@ void ISONameFromAnimationName(char* dst, const char* src) {
strcpy(dst + 8, "STR");
}
/*!
* Convert file name to "ISO Name"
* ISO names are upper case and 12 bytes long.
* xxxxxxxxyyy0
*
* x - uppercase letter of file name, or space
* y - uppercase letter of file extension, or space
* 0 - null terminator (\0, not the character zero)
*/
void MakeISOName(char* dst, const char* src) {
int i = 0;
const char* src_ptr = src;
+1 -1
View File
@@ -307,7 +307,7 @@ std::string GameTextFontBank::convert_game_to_utf8(const char* in) const {
in++;
}
replace_to_utf8(temp);
for (int i = 0; i < temp.length(); ++i) {
for (size_t i = 0; i < temp.length(); ++i) {
auto c = temp.at(i);
if (c == '\n') {
result += "\\n";
+1 -1
View File
@@ -532,7 +532,7 @@ std::string decompile_sparticle_launcher_by_id(const std::vector<LinkedWord>& wo
std::string decompile_sparticle_flags(const std::vector<LinkedWord>& words,
const TypeSystem& ts,
const std::string& field_name,
const std::string& /*field_name*/,
const std::string& flag_name) {
assert_spec_flag_int_no_rand(words, flag_name);
+36 -14
View File
@@ -108,20 +108,42 @@ set(RUNTIME_SOURCE
mips2c/jak2_functions/merc_blend_shape.cpp
mips2c/jak2_functions/ripple.cpp
mips2c/jak2_functions/squid.cpp
overlord/dma.cpp
overlord/fake_iso.cpp
overlord/iso.cpp
overlord/iso_api.cpp
overlord/iso_cd.cpp
overlord/iso_queue.cpp
overlord/isocommon.cpp
overlord/overlord.cpp
overlord/ramdisk.cpp
overlord/sbank.cpp
overlord/soundcommon.cpp
overlord/srpc.cpp
overlord/ssound.cpp
overlord/stream.cpp
overlord/common/dma.cpp
overlord/common/iso.cpp
overlord/common/iso_api.cpp
overlord/common/isocommon.cpp
overlord/common/overlord.cpp
overlord/common/sbank.cpp
overlord/common/soundcommon.cpp
overlord/common/srpc.cpp
overlord/common/fake_iso.cpp
overlord/common/ssound.cpp
overlord/jak1/dma.cpp
overlord/jak1/fake_iso.cpp
overlord/jak1/iso.cpp
overlord/jak1/iso_api.cpp
overlord/jak1/iso_queue.cpp
overlord/jak1/isocommon.cpp
overlord/jak1/overlord.cpp
overlord/jak1/ramdisk.cpp
overlord/jak1/srpc.cpp
overlord/jak1/ssound.cpp
overlord/jak1/stream.cpp
overlord/jak2/dma.cpp
overlord/jak2/iso.cpp
overlord/jak2/iso_cd.cpp
overlord/jak2/iso_api.cpp
overlord/jak2/iso_queue.cpp
overlord/jak2/overlord.cpp
overlord/jak2/pages.cpp
overlord/jak2/list.cpp
overlord/jak2/streamlist.cpp
overlord/jak2/streamlfo.cpp
overlord/jak2/ssound.cpp
overlord/jak2/srpc.cpp
overlord/jak2/stream.cpp
overlord/jak2/vag.cpp
overlord/jak2/spustreams.cpp
graphics/gfx.cpp
graphics/jak2_texture_remap.cpp
graphics/display.cpp
+1 -1
View File
@@ -18,6 +18,6 @@ enum class Language {
struct GameLaunchOptions {
GameVersion game_version = GameVersion::Jak1;
bool disable_display = false;
bool disable_debug_vm = false;
bool disable_debug_vm = true;
int server_port = DECI2_PORT;
};
+2
View File
@@ -11,6 +11,7 @@
#include "display.h"
#include "common/global_profiler/GlobalProfiler.h"
#include "common/log/log.h"
#include "common/symbols.h"
#include "common/util/FileUtil.h"
@@ -326,6 +327,7 @@ u32 Init(GameVersion version) {
void Loop(std::function<bool()> f) {
lg::info("GFX Loop");
while (f()) {
auto p = scoped_prof("gfx loop");
// check if we have a display
if (Display::GetMainDisplay()) {
// lg::debug("run display");
+3 -2
View File
@@ -100,7 +100,7 @@ inline fs::path mc_get_filename(GameVersion version, int ndx) {
return file_util::get_user_memcard_dir(version) / mc_get_filename_no_dir(version, ndx);
}
int mc_get_total_bank_size(GameVersion version) {
int mc_get_total_bank_size(GameVersion) {
return BANK_SIZE[g_game_version] + sizeof(McHeader) * 2;
}
@@ -145,7 +145,8 @@ u32 mc_checksum(Ptr<u8> data, s32 size) {
*/
bool file_is_present(int id, int bank = 0) {
auto bankname = mc_get_filename(g_game_version, 4 + id * 2 + bank);
if (!fs::exists(bankname) || fs::file_size(bankname) < mc_get_total_bank_size(g_game_version)) {
if (!fs::exists(bankname) ||
int(fs::file_size(bankname)) < mc_get_total_bank_size(g_game_version)) {
// file doesn't exist, or size is bad. we do not want to open files that will crash on read!
return false;
}
+1 -1
View File
@@ -2,7 +2,7 @@
#include "common/common_types.h"
#include "game/overlord/srpc.h"
#include "game/overlord/common/srpc.h"
#include "game/sound/989snd/ame_handler.h"
/*!
+3 -3
View File
@@ -88,7 +88,7 @@ int main(int argc, char** argv) {
bool verbose_logging = false;
bool disable_avx2 = false;
bool disable_display = false;
bool disable_debug_vm = false;
bool enable_debug_vm = false;
int port_number = -1;
fs::path project_path_override;
std::vector<std::string> game_args;
@@ -101,7 +101,7 @@ int main(int argc, char** argv) {
"Specify port number for listener connection (default is 8112 for Jak 1 and 8113 for Jak 2)");
app.add_flag("--no-avx2", verbose_logging, "Disable AVX2 for testing");
app.add_flag("--no-display", disable_display, "Disable video display");
app.add_flag("--no-vm", disable_debug_vm, "Disable debug PS2 VM (defaulted to on)");
app.add_flag("--vm", enable_debug_vm, "Enable debug PS2 VM (defaulted to off)");
app.add_option("--proj-path", project_path_override,
"Specify the location of the 'data/' folder");
app.footer(game_arg_documentation());
@@ -117,7 +117,7 @@ int main(int argc, char** argv) {
// Create struct with all non-kmachine handled args to pass to the runtime
GameLaunchOptions game_options;
game_options.disable_debug_vm = disable_debug_vm;
game_options.disable_debug_vm = !enable_debug_vm;
game_options.disable_display = disable_display;
game_options.game_version = game_name_to_version(game_name);
game_options.server_port =
@@ -1658,7 +1658,7 @@ struct Cache {
void* fake_scratchpad_data; // *fake-scratchpad-data*
} cache;
u64 execute(void* ctxt) {
u64 execute(void*) {
ASSERT(false);
return 0;
}
@@ -2149,7 +2149,7 @@ struct Cache {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
u32 call_addr = 0;
// u32 call_addr = 0;
c->daddiu(sp, sp, -96); // daddiu sp, sp, -96
c->sd(ra, 12432, at); // sd ra, 12432(at)
c->sq(s2, 12448, at); // sq s2, 12448(at)
@@ -2599,7 +2599,7 @@ u64 execute(void* ctxt) {
// c->gprs[t0].du64[0] = 0; // or t0, r0, r0
block_10:
// block_10:
c->dsll(t0, a0, 4); // dsll t0, a0, 4
// c->sw(a3, 128, a2); // sw a3, 128(a2)
sadr = c->sgpr64(a3);
+35
View File
@@ -0,0 +1,35 @@
#include "dma.h"
#include "game/sce/iop.h"
using namespace iop;
// note that jak 1 and 2 have different implementations, but we make them both instant.
// jak 2 has an EE dma semaphore, but we're going to ignore that.
/*!
* Wait for an ongoing DMA transfer to finish.
* IOP DMAs are instant in this version, so we return immediately and clear dmaid.
*/
void DMA_Sync() {}
/*!
* Start DMA transfer to the EE.
*/
void DMA_SendToEE(void* data, u32 size, void* dest) {
// finish previous DMA
DMA_Sync();
sceSifDmaData cmd; // DMA settings
// setup command
cmd.mode = 0;
cmd.data = data;
cmd.addr = dest;
cmd.size = size;
// start DMA (with disabled interrupts)
CpuDisableIntr();
sceSifSetDma(&cmd, 1);
CpuEnableIntr();
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "common/common_types.h"
void DMA_SendToEE(void* data, u32 size, void* dest);
void DMA_Sync();
+153
View File
@@ -0,0 +1,153 @@
/*!
* @file fake_iso.cpp
* This provides an implementation of IsoFs for reading a "fake iso".
* A "fake iso" is just a map file which maps 8.3 ISO file names to files in the source folder.
* This way we don't need to actually create an ISO.
*
* The game has this compilation unit, but there is nothing in it. Probably it is removed to save
* IOP memory and was only included on TOOL-only builds. So this is my interpretation of how it
* should work.
*/
#include "fake_iso.h"
#include <cstring>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/common/overlord_common.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/common/overlord.h"
#include "game/overlord/common/sbank.h"
#include "game/overlord/common/soundcommon.h"
#include "game/overlord/common/srpc.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
/*!
* Map from iso file name to file path in the src folder.
*/
struct FakeIsoEntry {
char iso_name[16];
char file_path[128];
};
FakeIsoEntry fake_iso_entries[MAX_ISO_FILES]; //! List of all known files
static FileRecord sFiles[MAX_ISO_FILES]; //! List of "FileRecords" for IsoFs API consumers
u32 fake_iso_entry_count; //! Total count of fake iso files
void fake_iso_init_globals() {
// init file lists
memset(fake_iso_entries, 0, sizeof(fake_iso_entries));
memset(sFiles, 0, sizeof(sFiles));
fake_iso_entry_count = 0;
}
/*!
* Initialize the file system.
*/
int fake_iso_FS_Init() {
for (const auto& f : fs::directory_iterator(file_util::get_jak_project_dir() / "out" /
game_version_names[g_game_version] / "iso")) {
if (f.is_regular_file()) {
ASSERT(fake_iso_entry_count < MAX_ISO_FILES);
FakeIsoEntry* e = &fake_iso_entries[fake_iso_entry_count];
std::string file_name = f.path().filename().string();
ASSERT(file_name.length() < 16); // should be 8.3.
strcpy(e->iso_name, file_name.c_str());
strcpy(e->file_path,
fmt::format("out/{}/iso/{}", game_version_names[g_game_version], file_name).c_str());
fake_iso_entry_count++;
}
}
for (u32 i = 0; i < fake_iso_entry_count; i++) {
MakeISOName(sFiles[i].name, fake_iso_entries[i].iso_name);
// we don't figure out the size yet.
// this is so you can change the file without restarting the game.
sFiles[i].size = -1;
// repurpose "location" as the index.
sFiles[i].location = i;
}
LoadMusicTweaks();
return 0;
}
/*!
* Find a file on the disc and return a FileRecord.
* Find using a "normal" 8.3 name.
* This is an ISO FS API Function
*/
FileRecord* FS_Find(const char* name) {
char name_buff[16];
MakeISOName(name_buff, name);
return FS_FindIN(name_buff);
}
/*!
* Find a file on the disc. Uses the "ISO name" of the file, which is different from the normal 8.3
* name. This can be generated with MakeISOFile.
* This is an ISO FS API Function.
*/
FileRecord* FS_FindIN(const char* iso_name) {
const uint32_t* buff = (const uint32_t*)iso_name;
uint32_t count = 0;
while (count < fake_iso_entry_count) {
const uint32_t* ref = (uint32_t*)sFiles[count].name;
if (ref[0] == buff[0] && ref[1] == buff[1] && ref[2] == buff[2]) {
return sFiles + count;
}
count++;
}
printf("[FAKEISO] failed to find %s\n", iso_name);
return nullptr;
}
/*!
* Build a full file path for a FileRecord.
*/
const char* get_file_path(FileRecord* fr) {
ASSERT(fr->location < fake_iso_entry_count);
static char path_buffer[1024];
strcpy(path_buffer, file_util::get_jak_project_dir().string().c_str());
strcat(path_buffer, "/");
strcat(path_buffer, fake_iso_entries[fr->location].file_path);
return path_buffer;
}
/*!
* Determine the length of a file. This isn't very fast, but nobody checks file sizes extremely
* quickly. This is an ISO FS API Function
*/
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 = file_util::open_file(path, "rb");
ASSERT(fp);
fseek(fp, 0, SEEK_END);
uint32_t len = ftell(fp);
rewind(fp);
fclose(fp);
return len;
}
void LoadMusicTweaks() {
char tweakname[16];
MakeISOName(tweakname, "TWEAKVAL.MUS");
auto file = FS_FindIN(tweakname);
if (file) {
auto fp = file_util::open_file(get_file_path(file), "rb");
fread(&gMusicTweakInfo, sizeof(gMusicTweakInfo), 1, fp);
fclose(fp);
} else {
gMusicTweakInfo.TweakCount = 0;
}
}
@@ -11,12 +11,13 @@
* should work.
*/
#ifndef JAK_V2_FAKE_ISO_H
#define JAK_V2_FAKE_ISO_H
#include "isocommon.h"
void fake_iso_init_globals();
extern IsoFs fake_iso;
#endif // JAK_V2_FAKE_ISO_H
int fake_iso_FS_Init();
const char* get_file_path(FileRecord* fr);
FileRecord* FS_Find(const char* name);
FileRecord* FS_FindIN(const char* iso_name);
uint32_t FS_GetLength(FileRecord* fr);
void LoadMusicTweaks();
extern u32 fake_iso_entry_count;
+56
View File
@@ -0,0 +1,56 @@
#include "iso.h"
#include <cstring>
#include "common/util/Assert.h"
#include "game/common/dgo_rpc_types.h"
#include "game/overlord/common/fake_iso.h"
#include "game/overlord/common/iso_api.h"
#include "game/overlord/jak1/dma.h"
#include "game/overlord/jak1/iso.h"
#include "game/overlord/jak1/ssound.h"
#include "game/overlord/jak1/stream.h"
#include "game/overlord/jak2/iso.h"
#include "game/overlord/jak2/stream.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
using namespace iop;
static constexpr s32 LOOP_END = 1;
static constexpr s32 LOOP_REPEAT = 2;
static constexpr s32 LOOP_START = 4;
// Empty ADPCM block with loop flags
// clang-format off
u8 VAG_SilentLoop[0x60] = {
0x0, LOOP_START | LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, LOOP_END | LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
// clang-format on
void iso_init_globals() {
// memset(&gVagDir, 0, sizeof(gVagDir));
}
/*!
* Does the messagebox have a message in it?
*/
u32 LookMbx(s32 mbx) {
MsgPacket* msg_packet;
return PollMbx((&msg_packet), mbx) != KE_MBOX_NOMSG;
}
/*!
* Wait for a messagebox to have a message. This is inefficient and polls with a 100 us wait.
* This is stupid because the IOP does have much better syncronization primitives so you don't have
* to do this.
*/
void WaitMbx(s32 mbx) {
while (!LookMbx(mbx)) {
DelayThread(100);
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "common/common_types.h"
#include "common/versions/versions.h"
#include "game/overlord/common/isocommon.h"
void iso_init_globals();
u32 LookMbx(s32 mbx);
void WaitMbx(s32 mbx);
extern u8 VAG_SilentLoop[0x60];
+6
View File
@@ -0,0 +1,6 @@
#include "iso_api.h"
#include "common/log/log.h"
#include "game/overlord/common/iso.h"
#include "game/sce/iop.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "game/overlord/common/isocommon.h"
+49
View File
@@ -0,0 +1,49 @@
#include "isocommon.h"
void MakeISOName(char* dst, const char* src) {
int i = 0;
const char* src_ptr = src;
char* dst_ptr = dst;
// copy name and upper case
while ((i < 8) && (*src_ptr) && (*src_ptr != '.')) {
char c = *src_ptr;
src_ptr++;
if (('`' < c) && (c < '{')) { // lower case
c -= 0x20;
}
*dst_ptr = c;
dst_ptr++;
i++;
}
// pad out name with spaces
while (i < 8) {
*dst_ptr = ' ';
dst_ptr++;
i++;
}
// increment past period
if (*src_ptr == '.')
src_ptr++;
// same for extension
while (i < 11 && (*src_ptr)) {
char c = *src_ptr;
src_ptr++;
if (('`' < c) && (c < '{')) { // lower case
c -= 0x20;
}
*dst_ptr = c;
dst_ptr++;
i++;
}
while (i < 11) {
*dst_ptr = ' ';
dst_ptr++;
i++;
}
*dst_ptr = 0;
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "common/common_types.h"
#include "common/link_types.h"
// was 350 in jak 1, 641 in jak 2, no reason not to go higher
constexpr int MAX_ISO_FILES = 999; // maximum files on FS
// was 16 in jak 1, 32 in jak 2. no reason not to increase for jak 1.
constexpr int MAX_OPEN_FILES = 32; // maximum number of open files at a time.
constexpr u32 CMD_STATUS_READ_ERR = 8; // read encountered a problem or was canceled.
constexpr u32 CMD_STATUS_NULL_CB = 7; // status returned if you don't set a callback
constexpr u32 CMD_STATUS_FAILED_TO_OPEN = 6; // status if file couldn't be opened
constexpr u32 CMD_STATUS_FAILED_TO_QUEUE = 2; // status if we couldn't be queued
constexpr u32 CMD_STATUS_IN_PROGRESS = 0xffffffff; // status if command is running and healthy
constexpr u32 CMD_STATUS_DONE = 0; // status if command is done.
constexpr int LOAD_TO_EE_CMD_ID = 0x100; // command to load file to ee
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
struct SoundBank;
/*!
* Record for file. There is one for each file in the FS, and pointers to each FileRecord act as
* an identifier.
* The location/size can't be counted on to be anything meaningful as it depends on the IsoFs
* implementation being used.
*/
struct FileRecord {
char name[12];
uint32_t location;
uint32_t size;
};
void MakeISOName(char* dst, const char* src);
+9
View File
@@ -0,0 +1,9 @@
#include "overlord.h"
/*!
* Loop endlessly and never return.
*/
void ExitIOP() {
while (true) {
}
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void ExitIOP();
@@ -1,13 +1,9 @@
#pragma once
#ifndef JAK_V2_SOUNDCOMMON_H
#define JAK_V2_SOUNDCOMMON_H
#include "common/common_types.h"
#include "game/overlord/sbank.h"
#include "game/overlord/common/sbank.h"
void strcpy_toupper(char* dest, const char* source);
void PrintBankInfo(SoundBank* buffer);
void ReadBankSoundInfo(SoundBank* bank, SoundBank* unk, s32 unk2);
#endif // JAK_V2_SOUNDCOMMON_H
+24
View File
@@ -0,0 +1,24 @@
#include "srpc.h"
#include <cstring>
// added
u32 gMusicFadeHack = 0;
MusicTweaks gMusicTweakInfo;
s32 gMusicTweak = 0x80;
int32_t gSoundEnable = 1;
s32 gMusic = 0;
s32 gMusicPause = 0;
s32 gSoundInUse = 0;
u8 gFPS = 60;
u32 gFrameNum = 0;
const char* gLanguage = nullptr;
void srpc_init_globals() {
gMusicFadeHack = 0;
gSoundEnable = 1;
gMusic = 0;
gMusicPause = 0;
gSoundInUse = 0;
memset((void*)&gMusicTweakInfo, 0, sizeof(gMusicTweakInfo));
}
+126
View File
@@ -0,0 +1,126 @@
#pragma once
#include "common/common_types.h"
#include "game/overlord/common/ssound.h"
// added for PC port
extern u32 gMusicFadeHack;
constexpr int MUSIC_TWEAK_COUNT = 32;
struct MusicTweaks {
u32 TweakCount;
struct {
char MusicName[12];
s32 VolumeAdjust;
} MusicTweak[MUSIC_TWEAK_COUNT];
};
struct SoundRpcGetIrxVersion {
u32 major;
u32 minor;
u32 ee_addr;
};
struct SoundRpcBankCommand {
u8 pad[12];
char bank_name[16];
};
struct SoundRpcSetLanguageCommand {
u32 langauge_id; // game_common_types.h, Language
};
struct SoundRpcPlayCommand {
u32 sound_id;
u32 pad[2];
char name[16];
SoundParams parms;
};
struct SoundRpcSetParamCommand {
u32 sound_id;
SoundParams parms;
s32 auto_time;
s32 auto_from;
};
struct SoundRpcSoundIdCommand {
u32 sound_id;
};
struct SoundRpcSetFlavaCommand {
u8 flava;
};
struct SoundRpcSetReverb {
u8 core;
s32 reverb;
u32 left;
u32 right;
};
struct SoundRpcSetEarTrans {
Vec3w ear_trans;
Vec3w cam_trans;
s32 cam_angle;
};
struct SoundRpc2SetEarTrans {
Vec3w ear_trans1;
Vec3w ear_trans0;
Vec3w cam_trans;
s32 cam_angle;
};
struct SoundRpcSetFPSCommand {
u8 fps;
};
struct SoundRpcSetFallof {
u8 pad[12];
char name[16];
s32 curve;
s32 min;
s32 max;
};
struct SoundRpcSetFallofCurve {
s32 curve;
s32 falloff;
s32 ease;
};
struct SoundRpcGroupCommand {
u8 group;
};
struct SoundRpcMasterVolCommand {
SoundRpcGroupCommand group;
s32 volume;
};
struct SoundRpcStereoMode {
s32 stereo_mode;
};
struct SoundRpcSetMidiReg {
s32 reg;
s32 value;
};
struct SoundRpcSetMirrror {
u8 value;
};
extern s32 gMusicTweak;
extern MusicTweaks gMusicTweakInfo;
extern int32_t gSoundEnable;
extern s32 gMusic;
extern s32 gMusicPause;
extern s32 gSoundInUse;
extern u8 gFPS;
extern const char* gLanguage;
extern u32 gFrameNum;
void srpc_init_globals();
@@ -1,37 +1,21 @@
#include "ssound.h"
#include <cstdio>
#include <cstring>
#include "common/util/Assert.h"
#include "game/overlord/iso.h"
#include "game/overlord/srpc.h"
#include "game/runtime.h"
#include "game/sound/sndshim.h"
using namespace iop;
s32 gMusicFade = 0;
s32 gSema;
Sound gSounds[64];
Curve gCurve[16];
VolumePair gPanTable[361];
Vec3w gEarTrans[2];
Vec3w gCamTrans;
s32 gCamAngle;
s32 gMusicVol = 0x400;
s32 gMusicFade = 0;
s32 gMusicFadeDir = 0;
u32 gStreamSRAM = 0;
u32 gTrapSRAM = 0;
Curve gCurve[16];
s32 gCamAngle;
u8 gMirrorMode = 0;
u32 sLastTick = 0;
s32 gSema;
static u32 sLastTick;
static s32 sqrt_table[256] = {
0, 4096, 5793, 7094, 8192, 9159, 10033, 10837, 11585, 12288, 12953, 13585, 14189,
14768, 15326, 15864, 16384, 16888, 17378, 17854, 18318, 18770, 19212, 19644, 20066, 20480,
@@ -69,98 +53,9 @@ static s32 atan_table[257] = {
43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, 45,
};
void CatalogSRAM() {}
static void* SndMemAlloc();
static void SndMemFree(void* ptr);
void InitSound_Overlord() {
for (auto& s : gSounds) {
s.id = 0;
}
if (g_game_version == GameVersion::Jak1) {
SetCurve(1, 0, 0);
SetCurve(2, 4096, 0);
SetCurve(3, 0, 4096);
SetCurve(4, 2048, 0);
SetCurve(5, 2048, 2048);
SetCurve(6, -4096, 0);
SetCurve(7, -2048, 0);
} else {
SetCurve(2, 0, 0);
SetCurve(9, 0, 0);
SetCurve(11, 0, 0);
SetCurve(10, 0, 0);
SetCurve(3, 4096, 0);
SetCurve(4, 0, 4096);
SetCurve(5, 2048, 0);
SetCurve(6, 2048, 2048);
SetCurve(7, -4096, 0);
SetCurve(8, -2048, 0);
}
snd_StartSoundSystem();
snd_RegisterIOPMemAllocator(SndMemAlloc, SndMemFree);
snd_LockVoiceAllocator(1);
u32 voice = snd_ExternVoiceAlloc(2, 0x7f);
snd_UnlockVoiceAllocator();
// The voice allocator returns a number in the range 0-47 where voices
// 0-23 are on SPU Core 0 and 24-47 are on core 2.
// For some reason we convert it to this format where 0-47 alternate core every step.
voice = voice / 24 + ((voice % 24) * 2);
// Allocate SPU RAM for our streams.
// (Which we don't need on PC)
gStreamSRAM = snd_SRAMMalloc(0xc030);
gTrapSRAM = gStreamSRAM + 0xC000;
snd_SetMixerMode(0, 0);
for (int i = 0; i < 8; i++) {
snd_SetGroupVoiceRange(i, 0x10, 0x2f);
}
snd_SetGroupVoiceRange(1, 0, 0xf);
snd_SetGroupVoiceRange(2, 0, 0xf);
snd_SetReverbDepth(SND_CORE_0 | SND_CORE_1, 0, 0);
snd_SetReverbType(SND_CORE_0, SD_REV_MODE_OFF);
snd_SetReverbType(SND_CORE_1, SD_REV_MODE_OFF);
CatalogSRAM();
for (int i = 0; i < 91; i++) {
s16 opposing_front = static_cast<s16>(((i * 0x33ff) / 0x5a) + 0xc00);
s16 rear_right = static_cast<s16>(((i * -0x2800) / 0x5a) + 0x3400);
s16 rear_left = static_cast<s16>(((i * -0xbff) / 0x5a) + 0x3fff);
gPanTable[90 - i].left = 0x3FFF;
gPanTable[180 - i].left = opposing_front;
gPanTable[270 - i].left = rear_right;
gPanTable[360 - i].left = rear_left;
gPanTable[i].right = opposing_front;
gPanTable[90 + i].right = 0x3FFF;
gPanTable[180 + i].right = rear_left;
gPanTable[270 + i].right = rear_right;
}
snd_SetPanTable((s16*)gPanTable);
snd_SetPlayBackMode(2);
SemaParam sema;
sema.attr = SA_THPRI;
sema.init_count = 1;
sema.max_count = 1;
sema.option = 0;
gSema = CreateSema(&sema);
if (gSema < 0) {
while (true)
;
}
void ssound_init_globals() {
gMusicFade = 0;
gSema = 0;
}
Sound* LookupSound(s32 id) {
@@ -198,37 +93,65 @@ void CleanSounds() {
}
}
void KillSoundsInGroup(u8 group) {
for (auto& s : gSounds) {
if (s.id != 0) {
s32 sndid = snd_SoundIsStillPlaying(s.sound_handle);
s.sound_handle = sndid;
s32 CalculateAngle(Vec3w* trans) {
s32 diffX = gCamTrans.x - trans->x;
s32 diffZ = gCamTrans.z - trans->z;
s32 angle;
if (sndid == 0) {
s.id = 0;
} else if (s.params.group & group) {
snd_StopSound(s.sound_handle);
s.id = 0;
s32 lookupX = diffX;
s32 lookupZ = diffZ;
if (diffX < 0) {
lookupX = trans->x - gCamTrans.x;
}
if (diffZ < 0) {
lookupZ = trans->z - gCamTrans.z;
}
if (lookupX == 0 && lookupZ == 0) {
return 0;
}
if (lookupZ >= lookupX) {
angle = atan_table[(lookupX << 8) / lookupZ];
if (diffZ >= 0) {
if (diffX < 0) {
angle = 360 - angle;
}
} else if (diffX >= 0) {
angle = 180 - angle;
} else {
angle += 180;
}
} else {
angle = atan_table[(lookupZ << 8) / lookupX];
if (diffX >= 0) {
if (diffZ >= 0) {
angle = 90 - angle;
} else {
angle = angle + 90;
}
} else if (diffZ >= 0) {
angle = angle + 270;
} else {
angle = 270 - angle;
}
}
angle = (angle - gCamAngle + 720) % 360;
if (gMirrorMode) {
angle = ((180 - angle) + 180) % 360;
}
return angle;
}
Sound* AllocateSound() {
for (auto& s : gSounds) {
if (s.id == 0) {
return &s;
}
}
CleanSounds();
for (auto& s : gSounds) {
if (s.id == 0) {
return &s;
}
}
return nullptr;
s32 GetPan(Sound* sound) {
return CalculateAngle(&sound->params.trans);
}
s32 CalculateFallofVolume(Vec3w* pos, s32 volume, s32 fo_curve, s32 fo_min, s32 fo_max) {
@@ -350,73 +273,56 @@ s32 CalculateFallofVolume(Vec3w* pos, s32 volume, s32 fo_curve, s32 fo_min, s32
return ret;
}
s32 CalculateAngle(Vec3w* trans) {
s32 diffX = gCamTrans.x - trans->x;
s32 diffZ = gCamTrans.z - trans->z;
s32 angle;
s32 lookupX = diffX;
s32 lookupZ = diffZ;
if (diffX < 0) {
lookupX = trans->x - gCamTrans.x;
}
if (diffZ < 0) {
lookupZ = trans->z - gCamTrans.z;
}
if (lookupX == 0 && lookupZ == 0) {
return 0;
}
if (lookupZ >= lookupX) {
angle = atan_table[(lookupX << 8) / lookupZ];
if (diffZ >= 0) {
if (diffX < 0) {
angle = 360 - angle;
}
} else if (diffX >= 0) {
angle = 180 - angle;
} else {
angle += 180;
}
} else {
angle = atan_table[(lookupZ << 8) / lookupX];
if (diffX >= 0) {
if (diffZ >= 0) {
angle = 90 - angle;
} else {
angle = angle + 90;
}
} else if (diffZ >= 0) {
angle = angle + 270;
} else {
angle = 270 - angle;
}
}
angle = (angle - gCamAngle + 720) % 360;
if (gMirrorMode) {
angle = ((180 - angle) + 180) % 360;
}
return angle;
}
s32 GetVolume(Sound* sound) {
return CalculateFallofVolume(&sound->params.trans, sound->params.volume, sound->params.fo_curve,
sound->params.fo_min, sound->params.fo_max);
}
s32 GetPan(Sound* sound) {
return CalculateAngle(&sound->params.trans);
void UpdateVolume(Sound* sound) {
s32 id = snd_SoundIsStillPlaying(sound->sound_handle);
sound->sound_handle = id;
if (sound->sound_handle == 0) {
sound->id = 0;
} else {
s32 volume = GetVolume(sound);
snd_SetSoundVolPan(id, volume, -2);
}
}
static void UpdateLocation(Sound* sound) {
Sound* AllocateSound() {
for (auto& s : gSounds) {
if (s.id == 0) {
return &s;
}
}
CleanSounds();
for (auto& s : gSounds) {
if (s.id == 0) {
return &s;
}
}
return nullptr;
}
void KillSoundsInGroup(u8 group) {
for (auto& s : gSounds) {
if (s.id != 0) {
s32 sndid = snd_SoundIsStillPlaying(s.sound_handle);
s.sound_handle = sndid;
if (sndid == 0) {
s.id = 0;
} else if (s.params.group & group) {
snd_StopSound(s.sound_handle);
s.id = 0;
}
}
}
}
void UpdateLocation(Sound* sound) {
if (sound->id == 0) {
return;
}
@@ -442,7 +348,7 @@ static void UpdateLocation(Sound* sound) {
}
}
static void UpdateAutoVol(Sound* sound, s32 ticks) {
void UpdateAutoVol(Sound* sound, s32 ticks) {
if (ticks < sound->auto_time) {
s32 nvol = sound->new_volume;
if (nvol == -4) {
@@ -483,39 +389,6 @@ static void UpdateAutoVol(Sound* sound, s32 ticks) {
sound->auto_time = 0;
}
void UpdateVolume(Sound* sound) {
s32 id = snd_SoundIsStillPlaying(sound->sound_handle);
sound->sound_handle = id;
if (sound->sound_handle == 0) {
sound->id = 0;
} else {
s32 volume = GetVolume(sound);
snd_SetSoundVolPan(id, volume, -2);
}
}
void SetEarTrans(Vec3w* ear_trans0, Vec3w* ear_trans1, Vec3w* cam_trans, s32 cam_angle) {
s32 tick = snd_GetTick();
u32 delta = tick - sLastTick;
sLastTick = tick;
gEarTrans[0] = *ear_trans0;
gEarTrans[1] = *ear_trans1;
gCamTrans = *cam_trans;
gCamAngle = cam_angle;
for (auto& s : gSounds) {
if (s.id != 0 && s.is_music == 0) {
if (s.auto_time != 0) {
UpdateAutoVol(&s, delta);
}
UpdateLocation(&s);
}
}
SetVAGVol();
}
void PrintActiveSounds() {
char string[64];
@@ -549,17 +422,3 @@ void SetCurve(s32 curve, s32 falloff, s32 ease) {
gCurve[curve].unk3 = ease - falloff - 0x1000;
gCurve[curve].unk4 = 0x1000;
}
void SetMusicVol() {
s32 volume = (gMusicVol * gMusicFade >> 0x10) * gMusicTweak >> 7;
snd_SetMasterVolume(1, volume);
snd_SetMasterVolume(2, volume);
}
// Do we even need/want these
// TODO void SetBufferMem() {}
// TODO void ReleaseBufferMem() {}
static void* SndMemAlloc() {
return nullptr;
}
static void SndMemFree(void* /*ptr*/) {}
@@ -1,25 +1,17 @@
#pragma once
#ifndef JAK_V2_SSOUND_H
#define JAK_V2_SSOUND_H
#include "common/common_types.h"
#include "sbank.h"
#include "game/overlord/common/sbank.h"
#include "game/sce/iop.h"
extern s32 gMusicFade;
extern s32 gSema;
extern s32 gMusicFadeDir;
struct VolumePair {
s16 left;
s16 right;
};
extern s32 gSema;
extern s32 gMusicFade;
extern s32 gMusicFadeDir;
extern s32 gMusicVol;
extern VolumePair gPanTable[361];
extern u32 gStreamSRAM;
extern u32 gTrapSRAM;
extern u8 gMirrorMode;
struct Vec3w {
s32 x;
s32 y;
@@ -58,18 +50,25 @@ struct Curve {
s32 unk4;
};
void InitSound_Overlord();
void SetCurve(s32 curve, s32 fallof, s32 ease);
void SetEarTrans(Vec3w* ear_trans1, Vec3w* ear_trans2, Vec3w* cam_trans, s32 cam_angle);
void KillSoundsInGroup(u8 group);
void PrintActiveSounds();
void SetMusicVol();
extern Sound gSounds[64];
extern Vec3w gEarTrans[2];
extern Curve gCurve[16];
extern Vec3w gCamTrans;
extern u8 gMirrorMode;
extern s32 gCamAngle;
extern u32 sLastTick;
void ssound_init_globals();
Sound* LookupSound(s32 id);
Sound* AllocateSound();
void UpdateVolume(Sound* sound);
s32 GetVolume(Sound* sound);
s32 GetPan(Sound* sound);
void UpdateVolume(Sound* sound);
s32 CalculateFallofVolume(Vec3w* pos, s32 volume, s32 fo_curve, s32 fo_min, s32 fo_max);
s32 GetPan(Sound* sound);
s32 CalculateAngle(Vec3w* trans);
#endif // JAK_V2_SSOUND_H
void KillSoundsInGroup(u8 group);
void UpdateLocation(Sound* sound);
void UpdateAutoVol(Sound* sound, s32 ticks);
void PrintActiveSounds();
void SetCurve(s32 curve, s32 fallof, s32 ease);
-113
View File
@@ -1,113 +0,0 @@
/*!
* @file dma.cpp
* DMA Related functions for Overlord.
* This code is not great.
*/
#include "dma.h"
#include <cstdio>
#include <cstring>
#include "common/common_types.h"
#include "game/sce/iop.h"
#include "game/sound/sdshim.h"
#include "game/sound/sndshim.h"
using namespace iop;
u32 dmaid; // ID of in-progress DMA. 0 if no DMA in progress
sceSifDmaData cmd; // DMA settings
u32 strobe; // ?? mysterious sound DMA flag.
void dma_init_globals() {
dmaid = 0;
memset(&cmd, 0, sizeof(cmd));
strobe = 0;
}
/*!
* Wait for an ongoing DMA transfer to finish.
* IOP DMAs are instant in this version, so we return immediately and clear dmaid.
*/
void DMA_Sync() {
// The DMA is complete. Clear dmaid.
dmaid = 0;
// for fun, the original code
// if(dmaid != 0) {
// if(sceSifDmaStat(dmaid) > 0) {
// u32 count = 10000;
// while(sceSifDmaStat(dmaid) > 0) {
// DelayThread(10);
// count--;
// if(count == 0) {
// u32 count = 10000;
// }
// }
// }
//
// // better do that again, just to be sure i did it the first time.
// u32 count = 10000;
// while(sceSifDmaStat(dmaid) > 0) {
// DelayThread(10);
// count--;
// if(count == 0) {
// u32 count = 10000;
// }
// }
// dmaid = 0;
// }
}
/*!
* Start DMA transfer to the EE.
*/
void DMA_SendToEE(void* data, u32 size, void* dest) {
// finish previous DMA
DMA_Sync();
// setup command
cmd.mode = 0;
cmd.data = data;
cmd.addr = dest;
cmd.size = size;
// start DMA (with disabled interrupts)
CpuDisableIntr();
dmaid = sceSifSetDma(&cmd, 1);
CpuEnableIntr();
if (dmaid == 0) {
do {
printf("Got a bad DMA ID!\n"); // added
} while (true);
}
}
/*!
* SPU DMA interrupt handler.
*/
s32 intr(s32 /*channel*/, void* /*userdata*/) {
strobe = 1;
return 0;
}
bool DMA_SendToSPUAndSync(void* src_addr, u32 size, u32 dst_addr) {
s32 channel = snd_GetFreeSPUDMA();
if (channel == -1)
return false;
strobe = 0;
sceSdSetTransIntrHandler(channel, intr, nullptr);
// Skip this, we end up memcpy's from OOB (which trips asan)
// u32 size_aligned = (size + 63) & 0xFFFFFFF0;
u32 size_aligned = size;
u32 transferred = sceSdVoiceTrans(channel, 0, src_addr, dst_addr, size_aligned);
while (!strobe)
;
sceSdSetTransIntrHandler(channel, nullptr, nullptr);
snd_FreeSPUDMA(channel);
return transferred >= size_aligned;
}
-444
View File
@@ -1,444 +0,0 @@
/*!
* @file fake_iso.cpp
* This provides an implementation of IsoFs for reading a "fake iso".
* A "fake iso" is just a map file which maps 8.3 ISO file names to files in the source folder.
* This way we don't need to actually create an ISO.
*
* The game has this compilation unit, but there is nothing in it. Probably it is removed to save
* IOP memory and was only included on TOOL-only builds. So this is my interpretation of how it
* should work.
*/
#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 "fake_iso.h"
#include <cstring>
#include "isocommon.h"
#include "overlord.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/overlord/sbank.h"
#include "game/overlord/soundcommon.h"
#include "game/overlord/srpc.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
IsoFs fake_iso;
/*!
* Map from iso file name to file path in the src folder.
*/
struct FakeIsoEntry {
char iso_name[16];
char file_path[128];
};
static LoadStackEntry sLoadStack[MAX_OPEN_FILES]; //! List of all files that are "open"
FakeIsoEntry fake_iso_entries[MAX_ISO_FILES]; //! List of all known files
static FileRecord sFiles[MAX_ISO_FILES]; //! List of "FileRecords" for IsoFs API consumers
u32 fake_iso_entry_count; //! Total count of fake iso files
static LoadStackEntry* sReadInfo; // LoadStackEntry for currently reading file
static int FS_Init(u8* buffer);
static FileRecord* FS_Find(const char* name);
static FileRecord* FS_FindIN(const char* iso_name);
static uint32_t FS_GetLength(FileRecord* fr);
static LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset);
static LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset);
static void FS_Close(LoadStackEntry* fd);
static uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len);
static uint32_t FS_SyncRead();
static uint32_t FS_LoadSoundBank(char*, void*);
static uint32_t FS_LoadMusic(char*, void*);
static uint32_t FS_LoadSoundBank2(char*, void*);
static uint32_t FS_LoadMusic2(char*, void*);
static void FS_PollDrive();
static void LoadMusicTweaks();
void fake_iso_init_globals() {
// init file lists
memset(fake_iso_entries, 0, sizeof(fake_iso_entries));
memset(sFiles, 0, sizeof(sFiles));
memset(sLoadStack, 0, sizeof(sLoadStack));
fake_iso_entry_count = 0;
// init API struct
fake_iso.init = FS_Init;
fake_iso.find = FS_Find;
fake_iso.find_in = FS_FindIN;
fake_iso.get_length = FS_GetLength;
fake_iso.open = FS_Open;
fake_iso.open_wad = FS_OpenWad;
fake_iso.close = FS_Close;
fake_iso.begin_read = FS_BeginRead;
fake_iso.sync_read = FS_SyncRead;
fake_iso.poll_drive = FS_PollDrive;
if (g_game_version == GameVersion::Jak1) {
fake_iso.load_sound_bank = FS_LoadSoundBank;
fake_iso.load_music = FS_LoadMusic;
} else {
fake_iso.load_sound_bank = FS_LoadSoundBank2;
fake_iso.load_music = FS_LoadMusic2;
}
sReadInfo = nullptr;
}
/*!
* Initialize the file system.
*/
int FS_Init(u8* buffer) {
(void)buffer;
for (const auto& f : fs::directory_iterator(file_util::get_jak_project_dir() / "out" /
game_version_names[g_game_version] / "iso")) {
if (f.is_regular_file()) {
ASSERT(fake_iso_entry_count < MAX_ISO_FILES);
FakeIsoEntry* e = &fake_iso_entries[fake_iso_entry_count];
std::string file_name = f.path().filename().string();
ASSERT(file_name.length() < 16); // should be 8.3.
strcpy(e->iso_name, file_name.c_str());
strcpy(e->file_path,
fmt::format("out/{}/iso/{}", game_version_names[g_game_version], file_name).c_str());
fake_iso_entry_count++;
}
}
for (u32 i = 0; i < fake_iso_entry_count; i++) {
MakeISOName(sFiles[i].name, fake_iso_entries[i].iso_name);
// we don't figure out the size yet.
// this is so you can change the file without restarting the game.
sFiles[i].size = -1;
// repurpose "location" as the index.
sFiles[i].location = i;
}
LoadMusicTweaks();
return 0;
}
/*!
* Find a file on the disc and return a FileRecord.
* Find using a "normal" 8.3 name.
* This is an ISO FS API Function
*/
FileRecord* FS_Find(const char* name) {
char name_buff[16];
MakeISOName(name_buff, name);
return FS_FindIN(name_buff);
}
/*!
* Find a file on the disc. Uses the "ISO name" of the file, which is different from the normal 8.3
* name. This can be generated with MakeISOFile.
* This is an ISO FS API Function.
*/
FileRecord* FS_FindIN(const char* iso_name) {
const uint32_t* buff = (const uint32_t*)iso_name;
uint32_t count = 0;
while (count < fake_iso_entry_count) {
const uint32_t* ref = (uint32_t*)sFiles[count].name;
if (ref[0] == buff[0] && ref[1] == buff[1] && ref[2] == buff[2]) {
return sFiles + count;
}
count++;
}
printf("[FAKEISO] failed to find %s\n", iso_name);
return nullptr;
}
/*!
* Build a full file path for a FileRecord.
*/
static const char* get_file_path(FileRecord* fr) {
ASSERT(fr->location < fake_iso_entry_count);
static char path_buffer[1024];
strcpy(path_buffer, file_util::get_jak_project_dir().string().c_str());
strcat(path_buffer, "/");
strcat(path_buffer, fake_iso_entries[fr->location].file_path);
return path_buffer;
}
/*!
* Determine the length of a file. This isn't very fast, but nobody checks file sizes extremely
* quickly. This is an ISO FS API Function
*/
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 = file_util::open_file(path, "rb");
ASSERT(fp);
fseek(fp, 0, SEEK_END);
uint32_t len = ftell(fp);
rewind(fp);
fclose(fp);
return len;
}
/*!
* Open a file by putting it on the load stack.
* Set the offset to 0 or -1 if you do not want to have an offset.
* This is an ISO FS API Function
*/
LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
lg::debug("[OVERLORD] FS Open {}", fr->name);
LoadStackEntry* selected = nullptr;
// find first unused spot on load stack.
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
selected = sLoadStack + i;
selected->fr = fr;
selected->location = 0;
if (offset != -1) {
selected->location += offset;
}
return selected;
}
}
lg::warn("[OVERLORD] Failed to FS Open {}", fr->name);
ExitIOP();
return nullptr;
}
/*!
* Open a file by putting it on the load stack.
* Like Open, but allows an offset of -1 to be applied.
* This is an ISO FS API Function
*/
LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
lg::debug("[OVERLORD] FS_OpenWad {}", fr->name);
LoadStackEntry* selected = nullptr;
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
selected = sLoadStack + i;
selected->fr = fr;
selected->location = offset;
return selected;
}
}
lg::warn("[OVERLORD] Failed to FS_OpenWad {}", fr->name);
ExitIOP();
return nullptr;
}
/*!
* Close an open file.
* This is an ISO FS API Function
*/
void FS_Close(LoadStackEntry* fd) {
lg::debug("[OVERLORD] FS_Close {} @ {}/{}", fd->fr->name, fd->fr->location, fd->location);
// close the FD
fd->fr = nullptr;
if (fd == sReadInfo) {
sReadInfo = nullptr;
}
}
/*!
* Begin reading! Returns FS_READ_OK on success (always)
* This is an ISO FS API Function
*
* Idea: do the fopen in FS_Open and keep the file open? It would be faster.
*/
uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
ASSERT(fd->fr->location < fake_iso_entry_count);
int32_t real_size = len;
if (len < 0) {
// not sure what this is about...
lg::warn("[OVERLORD ISO CD] Negative length warning!");
real_size = len + 0x7ff;
}
u32 sectors = real_size / SECTOR_SIZE;
real_size = sectors * SECTOR_SIZE;
u32 offset_into_file = SECTOR_SIZE * fd->location;
const char* path = get_file_path(fd->fr);
FILE* fp = file_util::open_file(path, "rb");
if (!fp) {
lg::error("[OVERLORD] fake iso could not open the file \"{}\"", path);
}
ASSERT(fp);
fseek(fp, 0, SEEK_END);
uint32_t file_len = ftell(fp);
rewind(fp);
if (offset_into_file < file_len) {
if (offset_into_file) {
fseek(fp, offset_into_file, SEEK_SET);
}
if (offset_into_file + real_size > file_len) {
real_size = (file_len - offset_into_file);
}
if (fread(buffer, real_size, 1, fp) != 1) {
ASSERT(false);
}
}
if (len < 0) {
len = len + 0x7ff;
}
fd->location += (len / SECTOR_SIZE);
sReadInfo = fd;
fclose(fp);
return CMD_STATUS_IN_PROGRESS;
}
/*!
* Block until read completes.
*/
uint32_t FS_SyncRead() {
// FS_BeginRead is blocking, so this is useless.
if (sReadInfo) {
sReadInfo = nullptr;
return CMD_STATUS_IN_PROGRESS;
} else {
return CMD_STATUS_READ_ERR;
}
}
/*!
* Poll drive
*/
void FS_PollDrive() {}
uint32_t FS_LoadMusic(char* name, void* buffer) {
s32* bank_handle = (s32*)buffer;
char namebuf[16];
strcpy(namebuf, name);
namebuf[8] = 0;
strcat(namebuf, ".mus");
auto file = FS_Find(namebuf);
if (!file)
return CMD_STATUS_FAILED_TO_OPEN;
*bank_handle = snd_BankLoadEx(get_file_path(file), 0, 0, 0);
snd_ResolveBankXREFS();
return 0;
}
uint32_t FS_LoadSoundBank(char* name, void* buffer) {
SoundBank* bank = (SoundBank*)buffer;
char namebuf[16];
int offset = 10 * 2048;
if (bank->sound_count == 101) {
offset = 1 * 2048;
}
strcpy(namebuf, name);
namebuf[8] = 0;
strcat(namebuf, ".sbk");
auto file = FS_Find(namebuf);
if (!file) {
file = FS_Find("empty1.sbk");
if (!file) // Might have no files when running tests.
return 0;
}
auto fp = file_util::open_file(get_file_path(file), "rb");
fread(buffer, offset, 1, fp);
fclose(fp);
s32 handle = snd_BankLoadEx(get_file_path(file), offset, 0, 0);
snd_ResolveBankXREFS();
PrintBankInfo(bank);
bank->bank_handle = handle;
return 0;
}
uint32_t FS_LoadMusic2(char* name, void* buffer) {
FileRecord* file = nullptr;
u32* bank_handle = (u32*)buffer;
char namebuf[16];
char isoname[16];
u32 handle;
strncpy(namebuf, name, 12);
namebuf[8] = 0;
strcat(namebuf, ".mus");
MakeISOName(isoname, namebuf);
file = FS_FindIN(isoname);
if (!file) {
return 6;
}
handle = snd_BankLoadEx(get_file_path(file), 0, 0xcfcc0, 0x61a80);
snd_ResolveBankXREFS();
*bank_handle = handle;
return 0;
}
uint32_t FS_LoadSoundBank2(char* name, void* buffer) {
SoundBank* bank = (SoundBank*)buffer;
FileRecord* file = nullptr;
char namebuf[16];
char isoname[16];
u32 handle;
strncpy(namebuf, name, 12);
namebuf[8] = 0;
strcat(namebuf, ".sbk");
MakeISOName(isoname, namebuf);
file = FS_FindIN(isoname);
if (!file) {
return 6;
}
handle = snd_BankLoadEx(get_file_path(file), 0, bank->spu_loc, bank->spu_size);
snd_ResolveBankXREFS();
bank->bank_handle = handle;
return 0;
}
void LoadMusicTweaks() {
char tweakname[16];
MakeISOName(tweakname, "TWEAKVAL.MUS");
auto file = FS_FindIN(tweakname);
if (file) {
auto fp = file_util::open_file(get_file_path(file), "rb");
fread(&gMusicTweakInfo, sizeof(gMusicTweakInfo), 1, fp);
fclose(fp);
} else {
gMusicTweakInfo.TweakCount = 0;
}
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(__clang__)
#pragma clang diagnostic pop
#endif
-990
View File
@@ -1,990 +0,0 @@
/*!
* @file iso_cd.cpp
* IsoFs API for accessing the CD/DVD drive.
*/
#include "iso_cd.h"
#include <cstring>
#include "isocommon.h"
#include "overlord.h"
#include "soundcommon.h"
#include "srpc.h"
#include "common/log/log.h"
#include "game/sce/iop.h"
#include "game/sce/stubs.h"
// iso_cd is an implementation of the IsoFs API for loading files from a CD/DVD with an ISO and/or
// DUP filesystem.
// The DUP filesystem is a custom Naughty Dog filesystem which attempts to hide
// files. The DUP filesystem also stores all files twice on the disk and will try reading from the
// other copy if it reading the first copy encounters errors. The DUP filesystem is unused.
using namespace iop;
typedef int (*mmode_func)(int);
// Drive State
// sector to read from (for DUP files, sector of the first copy of the file)
u32 _sector;
// number of sectors to read
u32 _sectors;
// number of retries in the current read
u32 _retries;
// buffer to read into
void* _buffer;
// set to 0 or 1 to indicate if the first or second copy of DUP files should be used.
uint32_t _dupseg;
// the actual sector to read from (differs from _sector when reading second copy of DUP file)
uint32_t _real_sector;
// set 1 if the current read was continuous from the previous read (didn't require a seek)
uint32_t _continuous;
// time when the current read was started
SysClock _starttime;
// time when the current read has ended
SysClock _endtime;
// Globals
u32 gDirtyCd; // set when we're waiting on a read which has errors
u32 gNoCD; // set when we believe the game disc has been removed.
static u32 sNumFiles; // number of files (includes both ISO and DUP files)
static u32 sArea1; // Sector where the first copy of DUP files live.
static u32 sAreaDiff; // Sectors in between the first and second copy of files.
u32 pirated; // do we think the game is pirated?
mmode_func cdmmode = nullptr; // function to call to set the expected media (CD/DVD)
static sceCdRMode sNominalMode; // drive settings for "nominal" reading
static sceCdRMode sStreamMode; // drive settings for "streaming" reading
static sceCdRMode* sMode; // pointer to currently selected read mode
static LoadStackEntry* sReadInfo; // LoadStackEntry for currently reading file
static u8* sSecBuffer[3]; // Buffers for a single sector
u32 add_files; // Should we add files we discover to the sFiles list?
static FileRecord sFiles[MAX_ISO_FILES]; // Info for all files on the disc
u32 CD_ID_SectorNum; // Sector of the DISK.ID file
s32 CD_ID_Sector[SECTOR_SIZE / 4]; // Contents of the DISK.ID file
s32 CD_ID_SectorSum; // Sum of the CD_ID_SECTOR array
LoadStackEntry sLoadStack[MAX_OPEN_FILES]; // List of all files that are "open"
static u32 sound_bank_loads; // might be a static variable in a function?
IsoFs iso_cd_; // IsoFs function pointers
constexpr int TIME_SIZE = 16; // how many samples for read timing
s32 _times[TIME_SIZE]; // read timing data
s32 _timesix;
s32 _tsamps[2];
s32 _tkps[2];
s32 gLastSpeed;
s32 gDiskSpeed[2];
s32 gDupSeg;
u32 ReadU32(u8* buffer);
u32 ReadSectorsNow(uint32_t sector, uint32_t len, void* buffer);
u32 ReadDirectory(uint32_t sector, uint32_t size, uint32_t secBufID);
void DecodeDUP(u8* buffer);
void LoadMusicTweaks(u8* buffer);
void LoadDiscID();
u32 CheckDiscID();
void SetRealSector();
void CD_WaitReturn();
static int FS_Init(u8* buffer);
static FileRecord* FS_Find(const char* name);
static FileRecord* FS_FindIN(const char* iso_name);
static uint32_t FS_GetLength(FileRecord* fr);
static LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset);
static LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset);
static void FS_Close(LoadStackEntry* fd);
static uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len);
static uint32_t FS_SyncRead();
static uint32_t FS_LoadSoundBank(char*, void*);
static uint32_t FS_LoadMusic(char*, void*);
static void FS_PollDrive();
void iso_cd_init_globals() {
_sector = 0;
_sectors = 0;
_retries = 0;
gDirtyCd = 0;
gNoCD = 0;
_dupseg = 0;
_real_sector = 0;
_continuous = 0;
_buffer = nullptr;
memset(&_starttime, 0, sizeof(SysClock));
memset(&_endtime, 0, sizeof(SysClock));
sNumFiles = 0;
sArea1 = 0;
sAreaDiff = 0;
pirated = 0;
cdmmode = nullptr;
sNominalMode.trycount = 0;
sNominalMode.spindlctrl = 1;
sNominalMode.datapattern = 0;
sNominalMode.pad = 0;
sStreamMode.trycount = 0xf;
sStreamMode.spindlctrl = 0;
sStreamMode.datapattern = 0;
sStreamMode.pad = 0;
sMode = &sStreamMode;
sReadInfo = nullptr;
memset(sSecBuffer, 0, sizeof(sSecBuffer));
add_files = 0;
memset(sFiles, 0, sizeof(sFiles));
CD_ID_SectorNum = 0;
memset(CD_ID_Sector, 0, sizeof(CD_ID_Sector));
CD_ID_SectorSum = 0;
memset(sLoadStack, 0, sizeof(sLoadStack));
sound_bank_loads = 0;
iso_cd_.init = FS_Init;
iso_cd_.find = FS_Find;
iso_cd_.find_in = FS_FindIN;
iso_cd_.get_length = FS_GetLength;
iso_cd_.open = FS_Open;
iso_cd_.open_wad = FS_OpenWad;
iso_cd_.close = FS_Close;
iso_cd_.begin_read = FS_BeginRead;
iso_cd_.sync_read = FS_SyncRead;
iso_cd_.load_sound_bank = FS_LoadSoundBank;
iso_cd_.load_music = FS_LoadMusic;
iso_cd_.poll_drive = FS_PollDrive;
memset(_times, 0, sizeof(_times));
memset(_tsamps, 0, sizeof(_tsamps));
memset(_tkps, 0, sizeof(_tkps));
_timesix = 0;
memset(gDiskSpeed, 0, sizeof(gDiskSpeed));
gLastSpeed = 0;
gDupSeg = 0;
}
/*!
* Read unaligned uint32_t from buffer. ISO file systems may have 32-bit values that aren't word
* aligned. DONE, EXACT
*/
uint32_t ReadU32(u8* data) {
return (uint32_t)data[0] + (((uint32_t)data[1]) * 0x100) + (((uint32_t)data[2]) * 0x10000) +
(((uint32_t)data[3]) * 0x1000000);
}
/*!
* Read from disc, immediately (blocking), into a local buffer. Will retry if needed, setting
* gDirtyCd. This does not use the DUP file system or drive state, so this should not be used
* outside of initialization. Will clear gDirtyCd on successful read. Returns 1 on success, 0 on
* sceCdRead failure, or otherwise retries forever until sceCdGetError is OK.
* The length is in terms of sectors.
* DONE, EXACT
*/
u32 ReadSectorsNow(uint32_t sector, uint32_t len, void* buffer) {
// reset the sector state to break any continuous reads in progress
_sector = 0;
// retry loop for read
while (true) {
// Start async read from DVD...
if (sceCdRead(sector, len, buffer, sMode) == 0) {
// if this fails, it indicates catastrophic failure of the DVD drive, so give up immediately.
return 0;
}
// Wait for read to finish (0x00 is blocking)
sceCdSync(0);
// check for error
if (sceCdGetError() == 0) {
// no error, we are good!
break;
}
// we got an error. Try again, and set a dirty flag so the EE knows we're having trouble
gDirtyCd = 1;
}
// success! clear the dirty flag and return!
gDirtyCd = 0;
return 1;
}
/*!
* Read ISO file systems directory tree, adding files to the filerecord table if add_files is set.
* Regardless of add_files is not set, it will be set for folders under NAUGHTY.DOG.
* This feature is used on demos with multiple games and Jak is in the NAUGHTY.DOG folder.
* Recursively walks the tree.
* There is a stack of single sector buffers used to recursively read the directories.
* Returns 1 on success and 0 on failure.
* Running out of sector buffers because of too many nested folders is considered success?
* DONE
*/
u32 ReadDirectory(uint32_t sector, uint32_t size, uint32_t secBufID) {
if (secBufID < 3) {
// grab our buffer from the stack
u8* buffer = sSecBuffer[secBufID];
uint32_t lsector = sector;
int32_t lsize = size;
// loop over sector reads
while (lsize > 0) {
// ISO low-level read
if (!ReadSectorsNow(lsector, 1, buffer)) {
lg::info("[OVERLORD ISO CD] Failed to read sector in ReadDirectory!");
return 0;
}
u8* lbuffer = buffer;
// loop over stuff in the sector
while ((*lbuffer != 0) && (lbuffer < buffer + SECTOR_SIZE)) {
u8 dir_record_size = *lbuffer;
if ((lbuffer[0x21] != 0) && (lbuffer[0x21] != 1)) { // skip over whatever these things are
uint32_t extent = ReadU32(lbuffer + 2);
uint32_t dir_size = ReadU32(lbuffer + 10);
uint32_t name_len = lbuffer[0x20];
bool is_directory = true;
if ((lbuffer[0x1f + name_len] == ';') && (lbuffer[0x20 + name_len] == '1')) {
is_directory = false;
}
if (is_directory) {
if (!add_files) {
// don't add file by default, but add files if we recurse in the NAUGHTY.DOG folder
if (!memcmp(lbuffer + 0x21, "NAUGHTY.DOG", 0xb)) {
add_files = true;
ReadDirectory(extent, dir_size, secBufID + 1);
add_files = false;
}
} else {
// otherwise just recurse
ReadDirectory(extent, dir_size, secBufID + 1);
}
} else {
if (sNumFiles == MAX_ISO_FILES) {
lg::info("[OVERLORD ISO CD] There are too many files on the disc!");
return 0;
}
if (add_files) {
lbuffer[0x1f + name_len] = 0; // null terminate the name
MakeISOName(sFiles[sNumFiles].name, (char*)(lbuffer + 0x21));
sFiles[sNumFiles].location = extent;
sFiles[sNumFiles].size = dir_size;
sNumFiles++;
}
}
}
lbuffer += dir_record_size;
}
lsector++;
lsize -= 0x800;
}
} else {
lg::info("[OVERLORD ISO CD] ReadDirectory ran out of sector buffers!");
}
return 1;
}
struct DupIndexEntry {
// 20 bytes
char name[12];
u32 location;
u32 size;
};
/*!
* DUP code. The DUP files aren't on the disc, so this doesn't do anything.
* This would allow for hidden files that aren't in the standard ISO format, presumably to make
* pirating harder? The DUP files store the location of these hidden files. Also it support having
* two copies of some files. There are two areas, both of which are identical. But it was never
* used.
*/
void DecodeDUP(u8* buffer) {
(void)buffer;
// set sArea1 to point to an impossibly large sector - if DUP initialization fails this means no
// file will be in the DUP zones.
sArea1 = 0x7fffffff;
char iso_name[16];
// all three of these will fail.
MakeISOName(iso_name, "Z1INDEX.DUP");
FileRecord* index_file = FS_FindIN(iso_name);
MakeISOName(iso_name, "Z3AREA1.DUP");
FileRecord* area1_file = FS_FindIN(iso_name);
MakeISOName(iso_name, "Z5AREA2.DUP");
FileRecord* area2_file = FS_FindIN(iso_name);
// Note - this reads 4 sectors, but the buffer only has enough room for 3 sectors.
// So this code would likely cause a crash if it was run.
// Maybe this is why it was removed?
// Or maybe there used to be 4 init buffers, but one was removed once they gave up on DUP?
if (index_file && area1_file && area2_file && ReadSectorsNow(index_file->location, 4, buffer)) {
sArea1 = area1_file->location; // marks start of 1st zone
sAreaDiff = area2_file->location - area1_file->location; // difference between zones
// make sure we have enough room to store all entries
if (sNumFiles + *(s32*)(buffer) <= MAX_ISO_FILES) {
// read entries
DupIndexEntry* dup_entries = (DupIndexEntry*)(((u8*)buffer) + 4);
for (int i = 0; i < *(s32*)(buffer); i++) {
*(s32*)(&sFiles[sNumFiles].name) = *(s32*)(&dup_entries[i].name);
*(s32*)(&sFiles[sNumFiles].name + 4) = *(s32*)(&dup_entries[i].name + 4);
*(s32*)(&sFiles[sNumFiles].name + 8) = *(s32*)(&dup_entries[i].name + 8);
sFiles[sNumFiles].size = dup_entries[i].size;
sFiles[sNumFiles].location = dup_entries[i].location;
sNumFiles++;
}
}
}
}
/*!
* Load the TWEAKVAL.MUS file into the gMusicTweakInfo file.
* Only works if the file is less than 1 sector long.
* If loading fails, writes a 0 to the first 32-bits of gMusicTweakInfo
* @param buffer a sector buffer which will be used
*/
void LoadMusicTweaks(u8* buffer) {
char iso_name[16];
MakeISOName(iso_name, "TWEAKVAL.MUS");
FileRecord* fr = FS_FindIN(iso_name);
if (!fr || !ReadSectorsNow(fr->location, 1, buffer)) {
gMusicTweakInfo.TweakCount = 0;
lg::warn("[OVERLORD ISO CD] Failed to load music tweaks!");
} else {
memcpy((void*)&gMusicTweakInfo, buffer, sizeof(MusicTweaks));
}
}
/*!
* Load the DISK ID file and compute the sum.
* This is used as a checksum to make sure the disc is correct.
* A literal sum is not a great checksum
* Also, this function name and the file on the disc itself spell dis{c,k} differently.
*
* If there is no DISK_ID.DIZ file, uses whatever is stored at 0x400 instead.
*/
void LoadDiscID() {
char iso_name[16];
MakeISOName(iso_name, "DISK_ID.DIZ");
FileRecord* fr = FS_FindIN(iso_name);
if (!fr) {
lg::warn(
"[OVERLORD ISO CD] LoadDiscID failed to find DISK_ID.DIZ, using sector 0x400 instead!");
CD_ID_SectorNum = 0x400;
} else {
CD_ID_SectorNum = fr->location;
}
ReadSectorsNow(CD_ID_SectorNum, 1, &CD_ID_Sector);
CD_ID_SectorSum = 0;
for (uint32_t i = 0; i < SECTOR_SIZE / 4; i++) {
CD_ID_SectorSum += CD_ID_Sector[i];
}
lg::info("[OVERLORD] DISK_ID.DIZ OK 0x{:x}", CD_ID_SectorSum);
}
/*!
* Verify that the DISK ID file has not changed. Returns 1 if it is good.
*/
u32 CheckDiskID() {
if (ReadSectorsNow(CD_ID_SectorNum, 1, CD_ID_Sector) == 0) {
// failed to read CD ID data
return 0;
}
int sum = 0;
for (uint32_t i = 0; i < SECTOR_SIZE / 4; i++) {
sum += CD_ID_Sector[i];
}
return sum == CD_ID_SectorSum;
}
/*!
* Set _real_sector in preparation for a read, based on the requested _sector.
* This has logic for a system which has a double copy of some data on the disc and can pick between
* two different copies. This selection is done with the dupseg flag.
*/
void SetRealSector() {
// if we are below sArea1, it's not a duplicated file, so ignore the dupseg flag and read directly
if (_sector < sArea1 || _dupseg == 0) {
_real_sector = _sector;
} else {
// it's a duplicated file, and duplicate read is enabled, so get the area 2 sector.
_real_sector = _sector + sAreaDiff;
lg::warn("[OVERLORD] Warning, adjusting real sector in SetRealSector");
}
// we suspect the game is pirated, load the wrong sector.
if (pirated) {
_real_sector += 3;
lg::warn("Pirated!");
}
}
/*!
* Initialize the ISO CD system and builds file record table.
* This is an ISO_FS API Function.
* Also loads music tweaks/DISK ID
* @param buffer : a buffer larger enough to hold 3 sectors
* this buffer can be freed immediately this returns
* Return 0 on success.
*/
int FS_Init(u8* buffer) {
// determine disk type
int disk_type = SCECdDETCT;
while (disk_type = sceCdGetDiskType(), disk_type == SCECdDETCT) {
// This SleepThread will cause the Overlord initialization to lock up. It's called with an
// argument of 10000, but SleepThread accepts no arguments. Probably they meant to call
// DelayThread. It ends up working because the drive already knows the disk type at this point.
SleepThread();
}
// what is this. it's crazy. why?
if (disk_type <= SCECdPS2DVD || disk_type < SCECdCDDA || disk_type <= SCECdDVDV ||
disk_type != SCECdIllegalMedia) {
// we are actually using the CD drive, so set the mmode function to the SCE function.
// This is called in FS_LoadMusic. If you call this with the wrong media type, it locks up.
// I guess this is an attempt at making convoluted anti-piracy code so it's harder to find
// calls to sceCdMmode with static analysis. But they left in debug symbols and the variable
// is called "cdmmode", which is not a very sneaky way to hide it! (At least on the EE it's
// called aybabtu and is a GOAL symbol which is way harder to figure out.) Also it seems like
// the primary mode of piracy they were concerned with is somebody swapping a DVD with a CD?
cdmmode = sceCdMmode;
// verify the disc is a DVD.
sceCdMmode(SCECdDVD);
// set up sector buffers used for initialization reads.
for (int i = 0; i < 3; i++) {
sSecBuffer[i] = buffer + i * SECTOR_SIZE;
}
// read primary volume descriptor into buffer
if (!ReadSectorsNow(0x10, 1, sSecBuffer[0])) {
lg::warn("[OVERLORD ISO CD] Failed to read primary volume descriptor");
return 1;
}
// check volume descriptor identifier
if (memcmp(sSecBuffer[0] + 1, "CD001", 5)) {
lg::warn("[OVERLORD ISO CD] Got the wrong volume descriptor identifier");
char* cptr = (char*)sSecBuffer[0] + 1;
printf("%c%c%c%c%c\n", cptr[0], cptr[1], cptr[2], cptr[3], cptr[4]);
return 1;
}
// read path table into buffer
uint32_t path_table_sector = ReadU32(sSecBuffer[0] + 0x8c);
if (!ReadSectorsNow(path_table_sector, 1, sSecBuffer[0])) {
lg::warn("[OVERLORD ISO CD] Failed to read path");
return 1;
}
// read path table's extent into buffer
uint32_t path_table_extent = ReadU32(sSecBuffer[0] + 2);
if (!ReadSectorsNow(path_table_extent, 1, sSecBuffer[0])) {
lg::warn("[OVERLORD ISO CD] Failed to read path table extent");
}
// read root directory
add_files = true;
uint32_t dir_size = ReadU32(sSecBuffer[0] + 10);
if (!ReadDirectory(path_table_extent, dir_size, 0)) {
lg::warn("[OVERLORD ISO CD] Failed to ReadDirectory");
return 1;
}
// load filesystem stuff
DecodeDUP(sSecBuffer[0]);
LoadMusicTweaks(sSecBuffer[0]);
LoadDiscID();
// there's some sort of weird loop here over all file that does nothing.
// my guess is its some commented out print thing?
// empty load stack
for (int i = 0; i < MAX_OPEN_FILES; i++) {
sLoadStack[i].fr = nullptr;
}
// kill sector buffers
for (int i = 0; i < 3; i++) {
sSecBuffer[i] = nullptr;
}
return 0;
} else {
lg::warn("[OVERLORD ISO CD] Bad Media Type!");
return 1;
}
}
/*!
* Find a file on the disc and return a FileRecord.
* This is an ISO FS API Function
*/
FileRecord* FS_Find(const char* name) {
char name_buff[16];
MakeISOName(name_buff, name);
return FS_FindIN(name_buff);
}
/*!
* Find a file on the disc. Uses the ISO name of the file.
* This can be generated with MakeISOFile
* This is an ISO FS API Function
* There is a weird anti-piracy thing in here to prevent people from making copies with less than
* 1 GB of data? I guess you could remove the audio in languages you don't care about and put in
* on a CD, and this would block this from happening.
*/
FileRecord* FS_FindIN(const char* iso_name) {
const uint32_t* buff = (const uint32_t*)iso_name;
for (;;) { // this loop will spin forever if you have < 1 GB of files
uint32_t size = 0; // total sum of file sizes
uint32_t count = 0;
while (count < sNumFiles) {
const uint32_t* ref = (uint32_t*)sFiles[count].name;
if (ref[0] == buff[0] && ref[1] == buff[1] && ref[2] == buff[2]) {
return sFiles + count;
}
size += sFiles[count].size;
count++;
}
// if we get here, we haven't found the file, we should return 0 to indicate we don't have it
// however, if we haven't found 1 GB of files after searching the whole thing
// we assume that we've pirated the game and should continue looping
// Note that the game attempts to load DUP files which will fails and will hit this condition.
buff +=
3; // to make this look less suspicious, lets increment buff. also will crash eventually.
if (0x3fffffff < size) {
return nullptr; // we got 1 GB of files, okay to return
}
// we didn't get 1 GB of files, you're a pirate.
lg::warn("Pirated!");
}
}
/*!
* Determine the length of a file.
* This is an ISO FS API Function
*/
uint32_t FS_GetLength(FileRecord* fr) {
return fr->size;
}
/*!
* Open a file by putting it on the load stack.
* Set the offset to 0 or -1 if you do not want to have an offset.
* This is an ISO FS API Function
*/
LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
lg::info("[OVERLORD] FS Open {}", fr->name);
LoadStackEntry* selected = nullptr;
// find first unused spot on load stack.
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
selected = sLoadStack + i;
selected->fr = fr;
selected->location = fr->location;
if (offset != -1) {
selected->location += offset;
}
return selected;
}
}
lg::warn("[OVERLORD ISO CD] Failed to FS_Open {}", fr->name);
ExitIOP();
return nullptr;
}
/*!
* Open a file by putting it on the load stack.
* Like Open, but allows an offset of -1 to be applied.
* This is an ISO FS API Function
*/
LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
printf("[OVERLORD] FS Open %s\n", fr->name);
LoadStackEntry* selected = nullptr;
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
selected = sLoadStack + i;
selected->fr = fr;
selected->location = fr->location + offset;
return selected;
}
}
lg::warn("[OVERLORD ISO CD] Failed to use FS_OpenWad {}", fr->name);
ExitIOP();
return nullptr;
}
/*!
* Close an open file.
* This is an ISO FS API Function
*/
void FS_Close(LoadStackEntry* fd) {
lg::info("[OVERLORD] FS Close {}", fd->fr->name);
if (fd == sReadInfo) {
// the file is currently being read, so lets try to finish out the read, if possible.
int count = 0;
// the non-blocking sync, so we don't get stuck here on a catastrophic error.
while (sceCdSync(1)) {
DelayThread(1000); // wait 1 ms and allow other stuff to run.
count++;
if (count == 1000) { // waited too long to close this file
sceCdBreak(); // interrupt the read
break;
}
}
sReadInfo = nullptr;
}
// close the FD
fd->fr = nullptr;
}
/*!
* Begin reading! Returns FS_READ_OK on success (always)
* This is an ISO FS API Function
*/
uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
// set the reading state:
// I guess continuous stream buffer reads don't count as continuous?
_continuous = (len == BUFFER_PAGE_SIZE) && (fd->location == (_sector + _sectors));
_sector = fd->location;
int32_t real_size = len;
if (len < 0) {
// not sure what this is about...
lg::warn("[OVERLORD ISO CD] Negative length warning!");
real_size = len + 0x7ff;
}
_sectors = real_size >> 11;
_retries = 0;
_buffer = buffer;
GetSystemTime(&_starttime);
// compute _real_sector
SetRealSector();
while (!sceCdRead(_real_sector, _sectors, _buffer, sMode)) {
// error starting the read. this is bad and possibly indicates somebody took the CD out.
// lets wait for the CD to be ready again...
CD_WaitReturn();
_retries++;
if (_sector >= sArea1) {
// the original file we tried to read is duplicated...
// so lets try reading the other copy of it!
_dupseg = 1 - _dupseg;
_continuous = 0; // mark as noncontinuous read
SetRealSector(); // recompute!
}
}
// ??? this is strangely set up.
if (len < 0) {
len = len + 0x7ff;
}
fd->location += (len >> 0xb);
// set sReadInfo to point to the current read.
sReadInfo = fd;
return CMD_STATUS_IN_PROGRESS;
}
/*!
* Wait for current read to complete!
* This is an ISO FS API Function
* @return
*/
uint32_t FS_SyncRead() {
// make sure a read is actually in progress
if (!sReadInfo) {
return CMD_STATUS_READ_ERR;
}
// remember when we start doing a SyncRead.
SysClock now;
GetSystemTime(&now);
// block and wait for completion
sceCdSync(0);
// remember when we sync.
GetSystemTime(&_endtime);
// Loop to check if read succeed and start an additional read if not.
while (sceCdGetError()) {
// no, it didn't, lets retry
_retries++;
// toggle dupseg
if (_sector >= sArea1) {
_dupseg = 1 - _dupseg;
_continuous = 0;
SetRealSector();
}
// try until a read starts...
while (!sceCdRead(_real_sector, _sectors, _buffer, sMode)) {
// read start failed, possibly CD is removed
CD_WaitReturn();
// retry!
_retries++;
// toggle dupseg if possible
if (_sector >= sArea1) {
_dupseg = 1 - _dupseg;
_continuous = 0;
SetRealSector();
}
}
// read has started
// set dirty cd to indicate we had trouble
gDirtyCd = 1;
// wait for read to finish...
sceCdSync(0);
// if the read/sync fails, the loop will go again.
}
// Read complete! Mark CD as not dirty and clear active read!
gDirtyCd = 0;
sReadInfo = nullptr;
// Optionally do some timing checks
// (note that these never run because we don't have DUP files)
// continuous read with no failures from dup zone
// more than half the time spent in FS_SyncRead
// Basically it tries to learn about which segment does worse in "too slow" reads
// by averaging all historical too slow reads. Once the other is winning by a certain amount
// it will swap. It will also swap if there isn't enough samples on one.
if (_retries == 0 && _continuous && _sectors >= sArea1 &&
(_endtime.hi - _starttime.hi) / 2 < (_endtime.hi - now.hi)) {
// record the time
_times[_timesix++] = _endtime.hi - _starttime.hi;
// if we filled the time buffer
if (_timesix == TIME_SIZE) {
// compute total time
s32 total_time = 0;
for (s32 i = 0; i < TIME_SIZE; i++) {
total_time += _times[i];
}
// determine read speed
gLastSpeed = 0x69780000 / (total_time >> 4); // todo - work out this constant
// add to average kps for this seg
if (_tsamps[_dupseg] < 0x40000) {
_tkps[_dupseg] = _tkps[_dupseg] + gLastSpeed;
_tsamps[_dupseg] = _tsamps[_dupseg];
}
// average speed of this segment
gDiskSpeed[_dupseg] = _tkps[_dupseg] / _tsamps[_dupseg];
_timesix = 0;
if (_tsamps[0] < 8) {
// not much information about segment 0
if (_dupseg) {
// and we aren't reading segment 0...
// so let's read segment 0
_dupseg = 0;
_sector = 0;
}
} else {
// got enough info about segment 0.
if (_tsamps[1] < 8) {
// not enough information about segment 1
if (!_dupseg) {
// and not reading, so lets read it.
_dupseg = 1;
_sector = 0;
}
} else {
// enough info about both.
if ((_tkps[1] / _tsamps[1] + 0x32) < (_tkps[0] / _tsamps[0])) {
// section 0 wins by at least 0x32, lets use it if we aren't already
if (_dupseg) {
_dupseg = 0;
_sector = 0;
}
} else if ((_tkps[0] / _tsamps[0] + 0x32) < (_tkps[1] / _tsamps[1])) {
if (!_dupseg) {
_dupseg = 1;
_sector = 0;
}
}
}
}
// set our current decision in a global for the EE to read.
gDupSeg = _dupseg;
}
}
return CMD_STATUS_IN_PROGRESS;
}
/*!
* Load a SoundBank now. Doesn't do any fancy read stuff.
*/
uint32_t FS_LoadSoundBank(char* name, void* buffer) {
char full_name[32]; // may actually be 8, but lets be safe
// ??? todo this is probably a field of the buffer.
u32 header_size;
if (*(s32*)(((u8*)buffer) + 0x14) == 0x65) {
header_size = 1;
} else {
header_size = 10;
}
if (strlen(name) > 16) {
printf("[OVERLORD ISO CD] FS_LoadSoundBank has an invalid name!\n");
}
// append .sbk
strcpy(full_name, name);
strcat(full_name, ".sbk");
FileRecord* fr = FS_Find(full_name);
if (!fr) {
printf("[OVERLORD ISO CD] FS_LoadSoundBank cannot find bank %s, loading empty instead.\n",
full_name);
fr = FS_Find("empty1.sbk");
}
// hack to do a read now (the Sound Bank loads bypass all the other fancy loading stuff evidently)
_sector = fr->location;
SetRealSector();
// loop until we read header successfully.
// don't set retries or dirty cd
while (!ReadSectorsNow(_real_sector, header_size, buffer)) {
// ReadSectorsNow will only return if the read fails to start. in this case we assume the disc
// was removed:
CD_WaitReturn();
// we don't increment retries...
if (_sector >= sArea1) {
_dupseg = 1 - _dupseg;
_continuous = 0;
SetRealSector();
}
}
// now have the sound library do a load.
// (this time we set dirty cd if it fails, but no retries)
auto load_status = snd_BankLoadByLoc(_real_sector + header_size, 0);
while (!load_status && snd_GetLastLoadError() < 0x100) {
CD_WaitReturn();
if (_sector >= sArea1) {
_dupseg = 1 - _dupseg;
_continuous = 0;
SetRealSector();
}
load_status = snd_BankLoadByLoc(_real_sector + header_size, 0);
if (!load_status) {
gDirtyCd = 1;
}
}
gDirtyCd = 0;
// pirate check sometimes
sound_bank_loads++;
if ((sound_bank_loads & 7) == 0) {
pirated = 1;
// check that one file is past sector 0x80000 (approx 1 GB)
for (u32 i = 0; i < sNumFiles; i++) {
if (sFiles[i].location + (sFiles[i].size >> 11) > 0x80000) {
pirated = 0;
}
}
}
snd_ResolveBankXREFS();
PrintBankInfo((SoundBank*)buffer);
_sector = 0;
// ??? todo this is probably a field of the buffer.
*(s32*)(((u8*)buffer) + 0x10) = load_status;
return 0;
}
/*!
* Load a music file. Load now, doesn't do fancy reading stuff.
*/
uint32_t FS_LoadMusic(char* name, void* buffer) {
char full_name[32]; // may actually be 8, but lets be safe
if (strlen(name) > 16) {
printf("[OVERLORD ISO CD] FS_LoadMusic has an invalid name!\n");
}
// append .mus
strcpy(full_name, name);
strcat(full_name, ".mus");
FileRecord* fr = FS_Find(full_name);
if (!fr) {
printf("[OVERLORD ISO CD] FS_LoadMusic cannot find bank %s.\n", full_name);
return 6;
}
_sector = fr->location;
SetRealSector();
// another "piracy" check to make sure the media is the correct type...
(*cdmmode)(SCECdDVD);
// now have the sound library do a load.
auto load_status = snd_BankLoadByLoc(_real_sector, 0);
// TODO magic constant 0x100
while (!load_status && snd_GetLastLoadError() < 0x100) {
CD_WaitReturn();
if (_sector >= sArea1) {
_dupseg = 1 - _dupseg;
_continuous = 0;
SetRealSector();
}
load_status = snd_BankLoadByLoc(_real_sector, 0);
if (!load_status) {
gDirtyCd = 1;
}
}
gDirtyCd = 0;
snd_ResolveBankXREFS();
_sector = 0;
*(s32*)buffer = load_status;
return 0;
}
/*!
* Make sure the drive is happy.
* NOTE - only call this when the drive should have nothing to do!
*/
void FS_PollDrive() {
if (sceCdDiskReady(1) == SCECdNotReady) { // non-blocking
CD_WaitReturn();
}
}
/*!
* Wait for the game CD/DVD to be put back in the playstation.
* Only call this if you think the CD/DVD has been removed, as requires a seek.
*/
void CD_WaitReturn() {
gNoCD = 1;
do {
while (sceCdDiskReady(1) == SCECdNotReady) {
}
} while (!CheckDiskID());
gNoCD = 0;
}
-18
View File
@@ -1,18 +0,0 @@
#pragma once
/*!
* @file iso_cd.cpp
* IsoFs API for accessing the CD/DVD drive.
*/
#ifndef JAK_ISO_CD_H
#define JAK_ISO_CD_H
#include "iso.h"
#include "common/common_types.h"
void iso_cd_init_globals();
extern IsoFs iso_cd_;
#endif // JAK_ISO_CD_H
-203
View File
@@ -1,203 +0,0 @@
/*!
* @file isocommon.cpp
* Common ISO utilities.
*/
#include "isocommon.h"
#include <cstring>
#include "common/common_types.h"
#include "common/util/Assert.h"
/*!
* Convert file name to "ISO Name"
* ISO names are upper case and 12 bytes long.
* xxxxxxxxyyy0
*
* x - uppercase letter of file name, or space
* y - uppercase letter of file extension, or space
* 0 - null terminator (\0, not the character zero)
*/
void MakeISOName(char* dst, const char* src) {
int i = 0;
const char* src_ptr = src;
char* dst_ptr = dst;
// copy name and upper case
while ((i < 8) && (*src_ptr) && (*src_ptr != '.')) {
char c = *src_ptr;
src_ptr++;
if (('`' < c) && (c < '{')) { // lower case
c -= 0x20;
}
*dst_ptr = c;
dst_ptr++;
i++;
}
// pad out name with spaces
while (i < 8) {
*dst_ptr = ' ';
dst_ptr++;
i++;
}
// increment past period
if (*src_ptr == '.')
src_ptr++;
// same for extension
while (i < 11 && (*src_ptr)) {
char c = *src_ptr;
src_ptr++;
if (('`' < c) && (c < '{')) { // lower case
c -= 0x20;
}
*dst_ptr = c;
dst_ptr++;
i++;
}
while (i < 11) {
*dst_ptr = ' ';
dst_ptr++;
i++;
}
*dst_ptr = 0;
}
/*!
* Unmakes an ISO name back to the original name.
* Keeps it upper case.
* Not used.
*/
void UnmakeISOName(char* dst, const char* src) {
int i = 0;
const char* src_ptr = src;
char* dst_ptr = dst;
// copy non-space characters
while ((i < 8) && (*src != ' ')) {
*dst_ptr = *src_ptr;
src_ptr++;
dst_ptr++;
i++;
}
// skip src to the extension
src_ptr += 8 - i;
if (*src_ptr != ' ') {
// if there's an extension, add the period
*dst_ptr = '.';
i = 0;
// copy extension
dst_ptr++;
while (i < 3 && *src_ptr != ' ') {
*dst_ptr = *src_ptr;
src_ptr++;
i++;
}
}
*dst_ptr = 0;
}
/*!
* Convert an animation name to ISO name.
* The animation name is a bunch of dash separated words.
* The resulting ISO name has the same first two chars as the animation name, and one char from each
* remaining word. Once there are no more words but remaining chars in the ISO name, the ith extra
* char is the i+1 th char of the last word. A word ending in a number (or just a number) is turned
* into the number. The word "resolution" becomes z. The word "accept" becomes y. The word "reject"
* becomes n. Other words become the first char of the word. The result is uppercased and the file
* extension is STR Examples (animation name and disc file name, not ISO name):
* green-sagecage-outro-beat-boss-enough-cells -> GRSOBBEC.STR
* swamp-tetherrock-swamprockexplode-4 -> SWTS4.STR
* minershort-resolution-1-orbs -> MIZ1ORBS.STR
* @param dst
* @param src
*/
void ISONameFromAnimationName(char* dst, const char* src) {
// The Animation Name is a bunch of words separated by dashes
// copy first two chars of the first word exactly
dst[0] = src[0];
dst[1] = src[1];
s32 i = 2; // 2 chars added to dst.
// skip ahead to the first dash (or \0 if there's no dashes)
const char* src_ptr = src;
while (*src_ptr && *src_ptr != '-') {
src_ptr++;
}
// the points to the next dash (or \0 if there's none).
const char* next_ptr = src_ptr;
if (*src_ptr) {
// loop over words (next_ptr points to dash before word, i counts chars in dest)
while (src_ptr = next_ptr + 1, i < 8) {
// scan next_ptr forward to next dash
next_ptr = src_ptr;
while (*next_ptr && *next_ptr != '-') {
next_ptr++;
}
// there's no next word, so break (the current word will be handled there)
if (!*next_ptr)
break;
// add a char for the current word:
char char_to_add;
if (next_ptr[-1] < '0' || next_ptr[-1] > '9') {
// word doesn't end in a number.
// some special case words map to special letters (likely to avoid animation name conflicts)
if (next_ptr - src_ptr == 10 && !memcmp(src_ptr, "resolution", 10)) {
// NOTE : jak 2 also allows "res" here but that doesn't work properly.
char_to_add = 'z';
} else if (next_ptr - src_ptr == 6 && !memcmp(src_ptr, "accept", 6)) {
char_to_add = 'y';
} else if (next_ptr - src_ptr == 6 && !memcmp(src_ptr, "reject", 6)) {
char_to_add = 'n';
} else if (next_ptr - src_ptr == 5 && !memcmp(src_ptr, "keira", 5)) {
// NOTE : this was added in jak 2. it's safe to use in jak 1 since she was referred to as
// "assistant" there
char_to_add = 'i';
} else {
// not a special case, just take the first letter.
char_to_add = *src_ptr;
}
} else {
// the current word ends in a number, just use this number (I think usually the whole word
// is just a number)
char_to_add = next_ptr[-1];
}
dst[i++] = char_to_add;
}
// here we ran out of room in dest, or words in source.
// if there's still room in dest and chars in source, just add them
while (*src_ptr && (i < 8)) {
dst[i] = *src_ptr;
src_ptr++;
i++;
}
}
// pad with spaces (for ISO Name)
while (i < 8) {
dst[i++] = ' ';
}
// upper case
for (i = 0; i < 8; i++) {
if (dst[i] > '`' && dst[i] < '{') {
dst[i] -= 0x20;
}
}
// append file extension
strcpy(dst + 8, "STR");
}
+51
View File
@@ -0,0 +1,51 @@
/*!
* @file dma.cpp
* DMA Related functions for Overlord.
* This code is not great.
*/
#include "dma.h"
#include <cstdio>
#include <cstring>
#include "common/common_types.h"
#include "game/sce/iop.h"
#include "game/sound/sdshim.h"
#include "game/sound/sndshim.h"
using namespace iop;
namespace jak1 {
u32 strobe; // ?? mysterious sound DMA flag.
void dma_init_globals() {
strobe = 0;
}
/*!
* SPU DMA interrupt handler.
*/
s32 intr(s32 /*channel*/, void* /*userdata*/) {
strobe = 1;
return 0;
}
bool DMA_SendToSPUAndSync(void* src_addr, u32 size, u32 dst_addr) {
s32 channel = snd_GetFreeSPUDMA();
if (channel == -1)
return false;
strobe = 0;
sceSdSetTransIntrHandler(channel, intr, nullptr);
// Skip this, we end up memcpy's from OOB (which trips asan)
// u32 size_aligned = (size + 63) & 0xFFFFFFF0;
u32 size_aligned = size;
u32 transferred = sceSdVoiceTrans(channel, 0, src_addr, dst_addr, size_aligned);
while (!strobe)
;
sceSdSetTransIntrHandler(channel, nullptr, nullptr);
snd_FreeSPUDMA(channel);
return transferred >= size_aligned;
}
} // namespace jak1
@@ -6,14 +6,10 @@
* This code is not great.
*/
#ifndef JAK_V2_DMA_H
#define JAK_V2_DMA_H
#include "common/common_types.h"
void DMA_Sync();
void DMA_SendToEE(void* data, u32 size, void* dest);
namespace jak1 {
bool DMA_SendToSPUAndSync(void* src_addr, u32 size, u32 dst_addr);
void dma_init_globals();
#endif // JAK_V2_DMA_H
} // namespace jak1
+225
View File
@@ -0,0 +1,225 @@
#include "fake_iso.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/overlord/common/fake_iso.h"
#include "game/overlord/common/overlord.h"
#include "game/overlord/common/soundcommon.h"
#include "game/overlord/jak1/isocommon.h"
#include "game/sound/sndshim.h"
namespace jak1 {
IsoFs fake_iso;
LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset);
uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len);
LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset);
uint32_t FS_LoadSoundBank(char* name, SoundBank* bank);
void FS_PollDrive();
uint32_t FS_SyncRead();
uint32_t FS_LoadMusic(char* name, s32* bank_handle);
void FS_Close(LoadStackEntry* fd);
static LoadStackEntry sLoadStack[MAX_OPEN_FILES]; //! List of all files that are "open"
static LoadStackEntry* sReadInfo; // LoadStackEntry for currently reading file
void fake_iso_init_globals() {
// init API struct
fake_iso.init = fake_iso_FS_Init;
fake_iso.find = FS_Find;
fake_iso.find_in = FS_FindIN;
fake_iso.get_length = FS_GetLength;
fake_iso.open = FS_Open;
fake_iso.open_wad = FS_OpenWad;
fake_iso.close = FS_Close;
fake_iso.begin_read = FS_BeginRead;
fake_iso.sync_read = FS_SyncRead;
fake_iso.poll_drive = FS_PollDrive;
fake_iso.load_sound_bank = FS_LoadSoundBank;
fake_iso.load_music = FS_LoadMusic;
memset(sLoadStack, 0, sizeof(sLoadStack));
sReadInfo = nullptr;
}
/*!
* Open a file by putting it on the load stack.
* Set the offset to 0 or -1 if you do not want to have an offset.
* This is an ISO FS API Function
*/
LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
lg::debug("[OVERLORD] FS Open {}", fr->name);
LoadStackEntry* selected = nullptr;
// find first unused spot on load stack.
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
selected = sLoadStack + i;
selected->fr = fr;
selected->location = 0;
if (offset != -1) {
selected->location += offset;
}
return selected;
}
}
lg::warn("[OVERLORD] Failed to FS Open {}", fr->name);
ExitIOP();
return nullptr;
}
/*!
* Open a file by putting it on the load stack.
* Like Open, but allows an offset of -1 to be applied.
* This is an ISO FS API Function
*/
LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
lg::debug("[OVERLORD] FS_OpenWad {}", fr->name);
LoadStackEntry* selected = nullptr;
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
selected = sLoadStack + i;
selected->fr = fr;
selected->location = offset;
return selected;
}
}
lg::warn("[OVERLORD] Failed to FS_OpenWad {}", fr->name);
ExitIOP();
return nullptr;
}
/*!
* Close an open file.
* This is an ISO FS API Function
*/
void FS_Close(LoadStackEntry* fd) {
lg::debug("[OVERLORD] FS_Close {} @ {}/{}", fd->fr->name, fd->fr->location, fd->location);
// close the FD
fd->fr = nullptr;
if (fd == sReadInfo) {
sReadInfo = nullptr;
}
}
/*!
* Begin reading! Returns FS_READ_OK on success (always)
* This is an ISO FS API Function
*
* Idea: do the fopen in FS_Open and keep the file open? It would be faster.
*/
uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
ASSERT(fd->fr->location < fake_iso_entry_count);
int32_t real_size = len;
if (len < 0) {
// not sure what this is about...
lg::warn("[OVERLORD ISO CD] Negative length warning!");
real_size = len + 0x7ff;
}
u32 sectors = real_size / SECTOR_SIZE;
real_size = sectors * SECTOR_SIZE;
u32 offset_into_file = SECTOR_SIZE * fd->location;
const char* path = get_file_path(fd->fr);
FILE* fp = file_util::open_file(path, "rb");
if (!fp) {
lg::error("[OVERLORD] fake iso could not open the file \"{}\"", path);
}
ASSERT(fp);
fseek(fp, 0, SEEK_END);
uint32_t file_len = ftell(fp);
rewind(fp);
if (offset_into_file < file_len) {
if (offset_into_file) {
fseek(fp, offset_into_file, SEEK_SET);
}
if (offset_into_file + real_size > file_len) {
real_size = (file_len - offset_into_file);
}
if (fread(buffer, real_size, 1, fp) != 1) {
ASSERT(false);
}
}
if (len < 0) {
len = len + 0x7ff;
}
fd->location += (len / SECTOR_SIZE);
sReadInfo = fd;
fclose(fp);
return CMD_STATUS_IN_PROGRESS;
}
/*!
* Block until read completes.
*/
uint32_t FS_SyncRead() {
// FS_BeginRead is blocking, so this is useless.
if (sReadInfo) {
sReadInfo = nullptr;
return CMD_STATUS_IN_PROGRESS;
} else {
return CMD_STATUS_READ_ERR;
}
}
/*!
* Poll drive
*/
void FS_PollDrive() {}
uint32_t FS_LoadMusic(char* name, s32* bank_handle) {
char namebuf[16];
strcpy(namebuf, name);
namebuf[8] = 0;
strcat(namebuf, ".mus");
auto file = FS_Find(namebuf);
if (!file)
return CMD_STATUS_FAILED_TO_OPEN;
*bank_handle = snd_BankLoadEx(get_file_path(file), 0, 0, 0);
snd_ResolveBankXREFS();
return 0;
}
uint32_t FS_LoadSoundBank(char* name, SoundBank* bank) {
char namebuf[16];
int offset = 10 * 2048;
if (bank->sound_count == 101) {
offset = 1 * 2048;
}
strcpy(namebuf, name);
namebuf[8] = 0;
strcat(namebuf, ".sbk");
auto file = FS_Find(namebuf);
if (!file) {
file = FS_Find("empty1.sbk");
if (!file) // Might have no files when running tests.
return 0;
}
auto fp = file_util::open_file(get_file_path(file), "rb");
fread(bank, offset, 1, fp);
fclose(fp);
s32 handle = snd_BankLoadEx(get_file_path(file), offset, 0, 0);
snd_ResolveBankXREFS();
PrintBankInfo(bank);
bank->bank_handle = handle;
return 0;
}
} // namespace jak1
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "game/overlord/jak1/isocommon.h"
namespace jak1 {
void fake_iso_init_globals();
extern IsoFs fake_iso;
} // namespace jak1
@@ -9,10 +9,7 @@
#include <cstdio>
#include <cstring>
#include "dma.h"
#include "fake_iso.h"
#include "iso_api.h"
#include "iso_cd.h"
#include "iso_queue.h"
#include "stream.h"
@@ -20,7 +17,12 @@
#include "common/util/Assert.h"
#include "game/common/dgo_rpc_types.h"
#include "game/overlord/srpc.h"
#include "game/overlord/common/dma.h"
#include "game/overlord/common/fake_iso.h"
#include "game/overlord/common/iso.h"
#include "game/overlord/jak1/dma.h"
#include "game/overlord/jak1/fake_iso.h"
#include "game/overlord/jak1/srpc.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
#include "game/sound/sdshim.h"
@@ -28,6 +30,7 @@
using namespace iop;
namespace jak1 {
u32 ISOThread();
u32 DGOThread();
u32 RunDGOStateMachine(IsoMessage* _cmd, IsoBufferHeader* buffer_header);
@@ -45,41 +48,11 @@ static s32 GetPlayPos();
static void UpdatePlayPos();
static void VAG_MarkLoopEnd(void* data, u32 size);
constexpr int LOADING_SCREEN_SIZE = 0x800000;
constexpr u32 LOADING_SCREEN_DEST_ADDR = 0x1000000;
static constexpr s32 LOOP_END = 1;
static constexpr s32 LOOP_REPEAT = 2;
static constexpr s32 LOOP_START = 4;
// Empty ADPCM block with loop flags
// clang-format off
static u8 VAG_SilentLoop[0x60] = {
0x0, LOOP_START | LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, LOOP_END | LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
// clang-format on
IsoFs* isofs;
u32 iso_init_flag;
s32 sync_mbx;
s32 iso_mbx;
s32 dgo_mbx;
s32 iso_thread;
s32 dgo_thread;
s32 str_thread;
s32 play_thread;
VagDirJak2 gVagDir;
u32 gPlayPos;
// todo move...
static RPC_Dgo_Cmd sRPCBuff[1];
DgoCommand scmd; // renamed to sLoadDGO in Jak 2
// :-)
#define sLoadDGO scmd
static VagCommand vag_cmd;
VagCommand* gVAGCMD = nullptr;
VagDir gVagDir;
s32 gDialogVolume = 0;
s32 gFakeVAGClockPaused = 0;
s32 gFakeVAGClockRunning = 0;
@@ -91,8 +64,20 @@ s32 gPlaying = 0;
s32 gSampleRate = 0;
bool gLastVagHalf = false;
s32 gVoice;
IsoFs* isofs;
u32 iso_init_flag;
s32 sync_mbx;
s32 iso_mbx;
s32 dgo_mbx;
s32 iso_thread;
s32 dgo_thread;
s32 str_thread;
s32 play_thread;
static RPC_Dgo_Cmd sRPCBuff[1];
DgoCommand sLoadDGO; // renamed from scmd to sLoadDGO in Jak 2
void iso_init_globals() {
gPlayPos = 0;
isofs = nullptr;
iso_init_flag = 0;
sync_mbx = 0;
@@ -102,48 +87,22 @@ void iso_init_globals() {
dgo_thread = 0;
str_thread = 0;
play_thread = 0;
memset(&gVagDir, 0, sizeof(gVagDir));
gPlayPos = 0;
memset(sRPCBuff, 0, sizeof(sRPCBuff));
memset(&scmd, 0, sizeof(DgoCommand));
memset(&sLoadDGO, 0, sizeof(DgoCommand));
}
/*!
* Initialize the ISO Driver.
* Requires a buffer large enough to hold 3 sector (or 4 if you have DUP files)
* Find a file by name. Return nullptr if it fails.
*/
static MsgPacket not_on_stack_sync;
void InitDriver(u8* buffer) {
if (!isofs->init(buffer)) {
// succesful init!
iso_init_flag = 0;
}
// you idiots, you're giving the kernel a pointer to a stack variable!
// (this is fixed in Jak 1 Japan and NTSC Greatest Hits)
// SendMbx(sync_mbx, &msg_packet);
// whoever fixed that bug felt similarly about it
SendMbx(sync_mbx, &not_on_stack_sync);
FileRecord* FindISOFile(const char* name) {
return isofs->find(name);
}
/*!
* Does the messagebox have a message in it?
* Get the length of an ISO File by FileRecord
*/
u32 LookMbx(s32 mbx) {
MsgPacket* msg_packet;
return PollMbx((&msg_packet), mbx) != KE_MBOX_NOMSG;
}
/*!
* Wait for a messagebox to have a message. This is inefficient and polls with a 100 us wait.
* This is stupid because the IOP does have much better syncronization primitives so you don't have
* to do this.
*/
void WaitMbx(s32 mbx) {
while (!LookMbx(mbx)) {
DelayThread(100);
}
u32 GetISOFileLength(FileRecord* f) {
return isofs->get_length(f);
}
/*!
@@ -155,22 +114,16 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) {
// isofs = &iso_cd;
// ADDED
if (!strcmp(fs_mode, "iso_cd")) {
isofs = &iso_cd_;
} else if (!strcmp(fs_mode, "fakeiso")) {
isofs = &fake_iso;
} else {
printf("[OVERLORD ISO] ISOFS has unknown fs_mode %s\n", fs_mode);
}
isofs = &fake_iso;
(void)fs_mode; // ignore user's request.
// always pick fake_iso because the others are not useful.
// END ADDED
// mark us as NOT initialized.
iso_init_flag = 1;
if (g_game_version == GameVersion::Jak1) {
while (!DMA_SendToSPUAndSync(&VAG_SilentLoop, 0x30, gTrapSRAM)) {
DelayThread(1000);
}
while (!jak1::DMA_SendToSPUAndSync(&VAG_SilentLoop, 0x30, jak1::gTrapSRAM)) {
DelayThread(1000);
}
// INITIALIZE MESSAGE BOXES
@@ -211,7 +164,7 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) {
thread_param.initPriority = 100;
thread_param.stackSize = 0x1000;
thread_param.option = 0;
thread_param.entry = (void*)ISOThread;
thread_param.entry = jak1::ISOThread;
strcpy(thread_param.name, "ISOThread");
iso_thread = CreateThread(&thread_param);
if (iso_thread <= 0) {
@@ -225,7 +178,7 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) {
thread_param.initPriority = 98;
thread_param.stackSize = 0x800;
thread_param.option = 0;
thread_param.entry = (void*)DGOThread;
thread_param.entry = DGOThread;
strcpy(thread_param.name, "DGOThread");
dgo_thread = CreateThread(&thread_param);
if (dgo_thread <= 0) {
@@ -239,7 +192,7 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) {
thread_param.initPriority = 97;
thread_param.stackSize = 0x800;
thread_param.option = 0;
thread_param.entry = (void*)STRThread;
thread_param.entry = jak1::STRThread;
strcpy(thread_param.name, "STRThread");
str_thread = CreateThread(&thread_param);
if (str_thread <= 0) {
@@ -253,7 +206,7 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) {
thread_param.initPriority = 97;
thread_param.stackSize = 0x800;
thread_param.option = 0;
thread_param.entry = (void*)PLAYThread;
thread_param.entry = jak1::PLAYThread;
strcpy(thread_param.name, "PLAYThread");
play_thread = CreateThread(&thread_param);
if (play_thread <= 0) {
@@ -275,13 +228,15 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) {
// LOAD VAGDIR file
FileRecord* vagdir_file = FindISOFile("VAGDIR.AYB");
if (vagdir_file) {
LoadISOFileToIOP(vagdir_file, &gVagDir,
g_game_version == GameVersion::Jak1 ? sizeof(VagDir) : sizeof(VagDirJak2));
LoadISOFileToIOP(vagdir_file, &gVagDir, sizeof(gVagDir));
} else {
printf("IOP: ======================================================================\n");
printf("IOP : iso InitISOFS : cannot load VAG directory\n");
printf("IOP: ======================================================================\n");
}
constexpr int LOADING_SCREEN_SIZE = 0x800000;
constexpr u32 LOADING_SCREEN_DEST_ADDR = 0x1000000;
FileRecord* loading_screen_file = FindISOFile(loading_screen);
if (loading_screen_file) {
LoadISOFileToEE(loading_screen_file, LOADING_SCREEN_DEST_ADDR, LOADING_SCREEN_SIZE);
@@ -292,33 +247,189 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) {
}
/*!
* Find a file by name. Return nullptr if it fails.
* Initialize the ISO Driver.
* Requires a buffer large enough to hold 3 sector (or 4 if you have DUP files)
*/
FileRecord* FindISOFile(const char* name) {
return isofs->find(name);
}
/*!
* Get the length of an ISO File by FileRecord
*/
u32 GetISOFileLength(FileRecord* f) {
return isofs->get_length(f);
}
/*!
* Find VAG file by "name", where name is 8 bytes (chars with spaces at the end, treated as two
* s32's). Returns pointer to name in the VAGDIR file data.
*/
VagDirEntry* FindVAGFile(const char* name) {
VagDirEntry* entry = gVagDir.vag;
for (u32 idx = 0; idx < gVagDir.count; idx++) {
// check if matching name
if (memcmp(entry->name, name, 8) == 0) {
return entry;
}
entry++;
static MsgPacket not_on_stack_sync;
void InitDriver() {
if (!isofs->init()) {
// succesful init!
iso_init_flag = 0;
}
// you idiots, you're giving the kernel a pointer to a stack variable!
// (this is fixed in Jak 1 Japan and NTSC Greatest Hits)
// SendMbx(sync_mbx, &msg_packet);
// whoever fixed that bug felt similarly about it
SendMbx(sync_mbx, &not_on_stack_sync);
}
void* RPC_DGO(unsigned int fno, void* _cmd, int y);
void LoadDGO(RPC_Dgo_Cmd* cmd);
void LoadNextDGO(RPC_Dgo_Cmd* cmd);
void CancelDGO(RPC_Dgo_Cmd* cmd);
/*!
* DGO RPC Thread.
*/
u32 DGOThread() {
sceSifQueueData dq;
sceSifServeData serve;
// setup RPC.
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, DGO_RPC_ID[g_game_version], RPC_DGO, sRPCBuff, nullptr, nullptr, &dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
/*!
* DGO RPC Handler.
*/
void* RPC_DGO(unsigned int fno, void* _cmd, int y) {
(void)y;
auto* cmd = (RPC_Dgo_Cmd*)_cmd;
// call appropriate handler.
switch (fno) {
case DGO_RPC_LOAD_FNO:
LoadDGO(cmd);
break;
case DGO_RPC_LOAD_NEXT_FNO:
LoadNextDGO(cmd);
break;
case DGO_RPC_CANCEL_FNO:
CancelDGO(cmd);
break;
default:
cmd->result = DGO_RPC_RESULT_ERROR;
}
return cmd;
}
/*!
* Begin loading a DGO. Returns when the first obj is loaded.
* Then will load the next obj into the second buffer.
* Then the DGO loader will block until LoadNextDGO is called.
* This approach keeps two loads in flight at a time to increase loading throughput.
* One load will be read from DVD / DMA'd to EE
* Another will be linked on the EE.
* The final load is done directly onto the heap, and isn't double buffered
* (otherwise the linking object could allocate on the heap where the final loading object is
* being copied). This avoids having to relocate the data from the temporary load buffer to the
* heap, and is the only way to make sure that the entire heap can be filled.
*/
void LoadDGO(RPC_Dgo_Cmd* cmd) {
// Find the file
FileRecord* fr = isofs->find(cmd->name);
if (!fr) {
cmd->result = DGO_RPC_RESULT_ERROR;
return;
}
// cancel an in progress command and wait for it to end.
// note - this doesn't handle a nullptr correctly, so if this actually ends up cancelling
// it will crash.
CancelDGO(nullptr);
// set up the ISO Command
sLoadDGO.cmd_id = LOAD_DGO_CMD_ID;
sLoadDGO.messagebox_to_reply = dgo_mbx;
sLoadDGO.thread_id = 0;
sLoadDGO.buffer1 = (u8*)(u64)(cmd->buffer1);
sLoadDGO.buffer2 = (u8*)(u64)(cmd->buffer2);
sLoadDGO.buffer_heaptop = (u8*)(u64)(cmd->buffer_heap_top);
sLoadDGO.fr = fr;
// printf("LOAD DGO -- 0x%x\n", cmd->buffer1);
// send the command to ISO Thread
SendMbx(iso_mbx, &sLoadDGO);
// wait for the ReturnMessage in the DGO callback state machine.
// this happens when the first file is loaded
WaitMbx(dgo_mbx);
if (sLoadDGO.status == CMD_STATUS_IN_PROGRESS) {
// we got one, but there's more to load.
// we don't set cmd->buffer1 as it's already the correct buffer in this case -
// when there are >1 objs, we load into buffer1 first.
cmd->result = DGO_RPC_RESULT_MORE;
} else if (sLoadDGO.status == CMD_STATUS_DONE) {
// all done! make sure our reply says we loaded to the top.
cmd->result = DGO_RPC_RESULT_DONE;
cmd->buffer1 = cmd->buffer_heap_top;
sLoadDGO.cmd_id = 0;
} else {
// error.
cmd->result = DGO_RPC_RESULT_ERROR;
sLoadDGO.cmd_id = 0;
}
}
/*!
* Signal to the IOP it can keep loading and overwrite the oldest obj buffer.
* This will return when there's another loaded obj.
*/
void LoadNextDGO(RPC_Dgo_Cmd* cmd) {
// printf("LOAD NEXT DGO -- 0x%x\n", cmd->buffer1);
if (sLoadDGO.cmd_id == 0) {
// something went wrong.
cmd->result = DGO_RPC_RESULT_ERROR;
} else {
// update heap location
sLoadDGO.buffer_heaptop = (u8*)(u64)cmd->buffer_heap_top;
if (g_game_version != GameVersion::Jak1) {
sLoadDGO.buffer1 = (u8*)(u64)cmd->buffer1;
sLoadDGO.buffer2 = (u8*)(u64)cmd->buffer2;
}
// allow DGO state machine to advance
SendMbx(sync_mbx, nullptr);
// wait for another load to finish.
WaitMbx(dgo_mbx);
// another load finished, respond with the result.
if (sLoadDGO.status == CMD_STATUS_IN_PROGRESS) {
// more, use the selected buffer.
cmd->result = DGO_RPC_RESULT_MORE;
cmd->buffer1 = (u32)(u64)sLoadDGO.selectedBuffer;
} else if (sLoadDGO.status == CMD_STATUS_DONE) {
// last obj, always loaded to top.
cmd->result = DGO_RPC_RESULT_DONE;
cmd->buffer1 = cmd->buffer_heap_top;
sLoadDGO.cmd_id = 0;
} else {
cmd->result = DGO_RPC_RESULT_ERROR;
sLoadDGO.cmd_id = 0;
}
}
}
/*!
* Abort an in progress load.
*/
void CancelDGO(RPC_Dgo_Cmd* cmd) {
if (sLoadDGO.cmd_id) {
sLoadDGO.want_abort = 1;
// wake up DGO state machine with abort
SendMbx(sync_mbx, nullptr);
// wait for it to abort.
WaitMbx(dgo_mbx);
// this will cause a crash if we cancel because we try to load 2 dgos at the same time.
// this should succeed if it's an actual cancel because we changed which level we're trying to
// load.
// This is weird in the original game, the IOP doesn't crash on writing to 0
// or, we have some other bug.
// NOTE : actually got fixed in Jak 2 so who cares
if (cmd) {
cmd->result = DGO_RPC_RESULT_ABORTED;
}
sLoadDGO.cmd_id = 0;
}
return nullptr;
}
/*!
@@ -328,7 +439,7 @@ u32 ISOThread() {
// Initialize!
InitBuffers();
auto temp_buffer = AllocateBuffer(BUFFER_PAGE_SIZE);
InitDriver(temp_buffer->get_data()); // unblocks InitISOFS's WaitMbx
InitDriver(); // unblocks InitISOFS's WaitMbx
FreeBuffer(temp_buffer);
VagCommand* in_progress_vag_command = nullptr;
@@ -567,7 +678,7 @@ u32 ISOThread() {
gFakeVAGClockRunning = true;
gFakeVAGClockPaused = 0;
}
gVAG_Id = in_progress_vag_command->sound_id;
gVAG_Id = in_progress_vag_command ? in_progress_vag_command->sound_id : 1;
}
ReturnMessage(cmd);
} break;
@@ -1340,173 +1451,6 @@ static void UpdatePlayPos() {
gPlayPos = pos;
}
void* RPC_DGO(unsigned int fno, void* _cmd, int y);
void LoadDGO(RPC_Dgo_Cmd* cmd);
void LoadNextDGO(RPC_Dgo_Cmd* cmd);
void CancelDGO(RPC_Dgo_Cmd* cmd);
/*!
* DGO RPC Thread.
*/
u32 DGOThread() {
sceSifQueueData dq;
sceSifServeData serve;
// setup RPC.
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, DGO_RPC_ID[g_game_version], RPC_DGO, sRPCBuff, nullptr, nullptr, &dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
/*!
* DGO RPC Handler.
*/
void* RPC_DGO(unsigned int fno, void* _cmd, int y) {
(void)y;
auto* cmd = (RPC_Dgo_Cmd*)_cmd;
// call appropriate handler.
switch (fno) {
case DGO_RPC_LOAD_FNO:
LoadDGO(cmd);
break;
case DGO_RPC_LOAD_NEXT_FNO:
LoadNextDGO(cmd);
break;
case DGO_RPC_CANCEL_FNO:
CancelDGO(cmd);
break;
default:
cmd->result = DGO_RPC_RESULT_ERROR;
}
return cmd;
}
/*!
* Begin loading a DGO. Returns when the first obj is loaded.
* Then will load the next obj into the second buffer.
* Then the DGO loader will block until LoadNextDGO is called.
* This approach keeps two loads in flight at a time to increase loading throughput.
* One load will be read from DVD / DMA'd to EE
* Another will be linked on the EE.
* The final load is done directly onto the heap, and isn't double buffered
* (otherwise the linking object could allocate on the heap where the final loading object is
* being copied). This avoids having to relocate the data from the temporary load buffer to the
* heap, and is the only way to make sure that the entire heap can be filled.
*/
void LoadDGO(RPC_Dgo_Cmd* cmd) {
// Find the file
FileRecord* fr = isofs->find(cmd->name);
if (!fr) {
cmd->result = DGO_RPC_RESULT_ERROR;
return;
}
// cancel an in progress command and wait for it to end.
// note - this doesn't handle a nullptr correctly, so if this actually ends up cancelling
// it will crash.
CancelDGO(nullptr);
// set up the ISO Command
scmd.cmd_id = LOAD_DGO_CMD_ID;
scmd.messagebox_to_reply = dgo_mbx;
scmd.thread_id = 0;
scmd.buffer1 = (u8*)(u64)(cmd->buffer1);
scmd.buffer2 = (u8*)(u64)(cmd->buffer2);
scmd.buffer_heaptop = (u8*)(u64)(cmd->buffer_heap_top);
scmd.fr = fr;
// printf("LOAD DGO -- 0x%x\n", cmd->buffer1);
// send the command to ISO Thread
SendMbx(iso_mbx, &scmd);
// wait for the ReturnMessage in the DGO callback state machine.
// this happens when the first file is loaded
WaitMbx(dgo_mbx);
if (scmd.status == CMD_STATUS_IN_PROGRESS) {
// we got one, but there's more to load.
// we don't set cmd->buffer1 as it's already the correct buffer in this case -
// when there are >1 objs, we load into buffer1 first.
cmd->result = DGO_RPC_RESULT_MORE;
} else if (scmd.status == CMD_STATUS_DONE) {
// all done! make sure our reply says we loaded to the top.
cmd->result = DGO_RPC_RESULT_DONE;
cmd->buffer1 = cmd->buffer_heap_top;
scmd.cmd_id = 0;
} else {
// error.
cmd->result = DGO_RPC_RESULT_ERROR;
scmd.cmd_id = 0;
}
}
/*!
* Signal to the IOP it can keep loading and overwrite the oldest obj buffer.
* This will return when there's another loaded obj.
*/
void LoadNextDGO(RPC_Dgo_Cmd* cmd) {
// printf("LOAD NEXT DGO -- 0x%x\n", cmd->buffer1);
if (scmd.cmd_id == 0) {
// something went wrong.
cmd->result = DGO_RPC_RESULT_ERROR;
} else {
// update heap location
scmd.buffer_heaptop = (u8*)(u64)cmd->buffer_heap_top;
if (g_game_version != GameVersion::Jak1) {
scmd.buffer1 = (u8*)(u64)cmd->buffer1;
scmd.buffer2 = (u8*)(u64)cmd->buffer2;
}
// allow DGO state machine to advance
SendMbx(sync_mbx, nullptr);
// wait for another load to finish.
WaitMbx(dgo_mbx);
// another load finished, respond with the result.
if (scmd.status == CMD_STATUS_IN_PROGRESS) {
// more, use the selected buffer.
cmd->result = DGO_RPC_RESULT_MORE;
cmd->buffer1 = (u32)(u64)scmd.selectedBuffer;
} else if (scmd.status == CMD_STATUS_DONE) {
// last obj, always loaded to top.
cmd->result = DGO_RPC_RESULT_DONE;
cmd->buffer1 = cmd->buffer_heap_top;
scmd.cmd_id = 0;
} else {
cmd->result = DGO_RPC_RESULT_ERROR;
scmd.cmd_id = 0;
}
}
}
/*!
* Abort an in progress load.
*/
void CancelDGO(RPC_Dgo_Cmd* cmd) {
if (scmd.cmd_id) {
scmd.want_abort = 1;
// wake up DGO state machine with abort
SendMbx(sync_mbx, nullptr);
// wait for it to abort.
WaitMbx(dgo_mbx);
// this will cause a crash if we cancel because we try to load 2 dgos at the same time.
// this should succeed if it's an actual cancel because we changed which level we're trying to
// load.
// This is weird in the original game, the IOP doesn't crash on writing to 0
// or, we have some other bug.
// NOTE : actually got fixed in Jak 2 so who cares
if (cmd) {
cmd->result = DGO_RPC_RESULT_ABORTED;
}
scmd.cmd_id = 0;
}
}
s32 GetVAGStreamPos() {
UpdatePlayPos();
if (gFakeVAGClockRunning) {
@@ -1521,3 +1465,20 @@ s32 GetVAGStreamPos() {
static void VAG_MarkLoopEnd(void* data, u32 size) {
((u8*)data)[size - 15] = 3;
}
/*!
* Find VAG file by "name", where name is 8 bytes (chars with spaces at the end, treated as two
* s32's). Returns pointer to name in the VAGDIR file data.
*/
VagDirEntry* FindVAGFile(const char* name) {
VagDirEntry* entry = gVagDir.vag;
for (u32 idx = 0; idx < gVagDir.count; idx++) {
// check if matching name
if (memcmp(entry->name, name, 8) == 0) {
return entry;
}
entry++;
}
return nullptr;
}
} // namespace jak1
@@ -10,33 +10,36 @@
#include "common/common_types.h"
namespace jak1 {
extern s32 gFakeVAGClockPaused;
extern s32 gFakeVAGClockRunning;
extern s32 gFakeVAGClock;
extern s32 gRealVAGClock;
extern s32 gVoice;
extern IsoFs* isofs;
extern s32 iso_mbx;
extern s32 sync_mbx;
extern DgoCommand sLoadDGO; // renamed from scmd to sLoadDGO in Jak 2
struct VagDirEntry {
char name[8];
u32 offset;
};
static constexpr int VAG_COUNT = 868;
struct VagDir {
u32 count;
VagDirEntry vag[VAG_COUNT];
};
static constexpr int VAG_COUNT_JAK2 = 2728;
struct VagDirJak2 {
u32 count;
VagDirEntry vag[VAG_COUNT_JAK2];
};
extern VagDir gVagDir;
void iso_init_globals();
FileRecord* FindISOFile(const char* name);
u32 GetISOFileLength(FileRecord* f);
u32 InitISOFS(const char* fs_mode, const char* loading_screen);
VagDirEntry* FindVAGFile(const char* name);
s32 GetVAGStreamPos();
void SetVAGVol();
u32 ISOThread();
u32 InitISOFS(const char* fs_mode, const char* loading_screen);
void InitDriver(u8* buffer);
FileRecord* FindISOFile(const char* name);
u32 GetISOFileLength(FileRecord* f);
VagDirEntry* FindVAGFile(const char* name);
} // namespace jak1
@@ -1,77 +1,18 @@
#include "iso_api.h"
#include "iso_queue.h"
#include "sbank.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "game/overlord/srpc.h"
#include "game/overlord/common/sbank.h"
#include "game/overlord/common/srpc.h"
#include "game/overlord/jak1/iso.h"
#include "game/sce/iop.h"
using namespace iop;
/*!
* Load a File to IOP memory (blocking)
*/
s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) {
lg::debug("[OVERLORD] LoadISOFileToIOP {}, {}/{} bytes", file->name, length, (s32)file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_IOP_CMD_ID;
cmd.messagebox_to_reply = 0;
cmd.thread_id = GetThreadId();
cmd.file_record = file;
cmd.dest_addr = (u8*)addr;
cmd.length = length;
SendMbx(iso_mbx, &cmd);
SleepThread();
if (cmd.status) {
cmd.length_to_copy = 0;
}
return cmd.length_to_copy;
}
/*!
* Load a File to IOP memory (blocking)
*/
s32 LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) {
lg::debug("[OVERLORD] LoadISOFileToEE {}, {}/{} bytes", file->name, length, (s32)file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_EE_CMD_ID;
cmd.messagebox_to_reply = 0;
cmd.thread_id = GetThreadId();
cmd.file_record = file;
cmd.dest_addr = (u8*)(u64)addr;
cmd.length = length;
SendMbx(iso_mbx, &cmd);
SleepThread();
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) {
lg::debug("[OVERLORD] LoadISOFileChunkToEE {} : {} offset {}", 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;
}
namespace jak1 {
/*!
* Send a command to the ISO thread to load a sound bank. This will sleep the calling thread
@@ -199,3 +140,67 @@ void UnpauseVAGStream() {
cmd->thread_id = 0;
SendMbx(iso_mbx, cmd);
}
/*!
* Load a File to IOP memory (blocking)
*/
s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) {
lg::debug("[OVERLORD] LoadISOFileToIOP {}, {}/{} bytes", file->name, length, (s32)file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_IOP_CMD_ID;
cmd.messagebox_to_reply = 0;
cmd.thread_id = GetThreadId();
cmd.file_record = file;
cmd.dest_addr = (u8*)addr;
cmd.length = length;
SendMbx(iso_mbx, &cmd);
SleepThread();
if (cmd.status) {
cmd.length_to_copy = 0;
}
return cmd.length_to_copy;
}
/*!
* Load a File to IOP memory (blocking)
*/
s32 LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) {
lg::debug("[OVERLORD] LoadISOFileToEE {}, {}/{} bytes", file->name, length, (s32)file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_EE_CMD_ID;
cmd.messagebox_to_reply = 0;
cmd.thread_id = GetThreadId();
cmd.file_record = file;
cmd.dest_addr = (u8*)(u64)addr;
cmd.length = length;
SendMbx(iso_mbx, &cmd);
SleepThread();
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) {
lg::debug("[OVERLORD] LoadISOFileChunkToEE {} : {} offset {}", 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;
}
} // namespace jak1
@@ -1,13 +1,11 @@
#pragma once
#include "isocommon.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/common/ssound.h"
struct SoundBank;
struct VagDirEntry;
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);
namespace jak1 {
struct VagDirEntry;
void LoadSoundBank(const char* bank_name, SoundBank* bank);
void LoadMusic(const char* music_name, s32* bank);
@@ -23,3 +21,8 @@ void SetDialogVolume(s32 volume);
void StopVAGStream(VagDirEntry* vag, u32 unk);
void PauseVAGStream();
void UnpauseVAGStream();
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);
} // namespace jak1
@@ -8,10 +8,12 @@
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "game/overlord/jak1/iso.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak1 {
constexpr int N_BUFFERS = 4;
constexpr int N_STR_BUFFERS = 1;
constexpr int N_VAG_CMDS = 64;
@@ -367,3 +369,4 @@ void FreeVAGCommand(VagCommand* cmd) {
printf("[OVERLORD] Invalid FreeVAGCommand!\n");
}
}
} // namespace jak1
@@ -4,6 +4,9 @@
#include "common/common_types.h"
#include "game/overlord/common/isocommon.h"
namespace jak1 {
void iso_queue_init_globals();
void InitBuffers();
IsoBufferHeader* AllocateBuffer(uint32_t size);
@@ -18,3 +21,4 @@ IsoBufferHeader* TryAllocateBuffer(uint32_t size);
VagCommand* GetVAGCommand();
void FreeVAGCommand(VagCommand* cmd);
void ReleaseMessage(IsoMessage* cmd);
} // namespace jak1
+51
View File
@@ -0,0 +1,51 @@
/*!
* @file isocommon.cpp
* Common ISO utilities.
*/
#include "isocommon.h"
#include <cstring>
#include "common/common_types.h"
#include "common/util/Assert.h"
namespace jak1 {
/*!
* Unmakes an ISO name back to the original name.
* Keeps it upper case.
* Not used.
*/
void UnmakeISOName(char* dst, const char* src) {
int i = 0;
const char* src_ptr = src;
char* dst_ptr = dst;
// copy non-space characters
while ((i < 8) && (*src != ' ')) {
*dst_ptr = *src_ptr;
src_ptr++;
dst_ptr++;
i++;
}
// skip src to the extension
src_ptr += 8 - i;
if (*src_ptr != ' ') {
// if there's an extension, add the period
*dst_ptr = '.';
i = 0;
// copy extension
dst_ptr++;
while (i < 3 && *src_ptr != ' ') {
*dst_ptr = *src_ptr;
src_ptr++;
i++;
}
}
*dst_ptr = 0;
}
} // namespace jak1
@@ -5,59 +5,35 @@
* Common ISO utilities.
*/
#ifndef JAK_V2_ISOCOMMON_H
#define JAK_V2_ISOCOMMON_H
#include <string>
#include "common/common_types.h"
#include "common/link_types.h"
#include "game/common/overlord_common.h"
#include "game/overlord/ssound.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/jak1/ssound.h"
struct SoundBank;
namespace jak1 {
struct VagDirEntry;
constexpr int PRI_STACK_LENGTH = 4; // number of queued commands per priority
constexpr int N_PRIORITIES = 4; // number of priorities
constexpr u32 CMD_STATUS_READ_ERR = 8; // read encountered a problem or was canceled.
constexpr u32 CMD_STATUS_NULL_CB = 7; // status returned if you don't set a callback
constexpr u32 CMD_STATUS_FAILED_TO_OPEN = 6; // status if file couldn't be opened
constexpr u32 CMD_STATUS_FAILED_TO_QUEUE = 2; // status if we couldn't be queued
constexpr u32 CMD_STATUS_IN_PROGRESS = 0xffffffff; // status if command is running and healthy
constexpr u32 CMD_STATUS_DONE = 0; // status if command is done.
constexpr int BUFFER_PAGE_SIZE = 0xc000; // size in bytes of normal read buffer
constexpr int STR_BUFFER_DATA_SIZE = 0x6000; // size in bytes of vag read buffer
constexpr int LOAD_TO_EE_CMD_ID = 0x100; // command to load file to ee
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 LOAD_SOUND_BANK = 0x300; // Command to load a sound bank
constexpr int LOAD_MUSIC = 0x380; // Command to load music
constexpr int QUEUE_VAG_STREAM = 0x400; // Command to load a vag stream
constexpr int PLAY_VAG_STREAM = 0x401; // Command to play a vag stream
constexpr int STOP_VAG_STREAM = 0x402; // Command to stop a vag stream
constexpr int PAUSE_VAG_STREAM = 0x403; // Command to pause a vag stream
constexpr int CONTINUE_VAG_STREAM = 0x404; // Command to continue a vag stream
constexpr int SET_VAG_VOLUME = 0x405; // Command to set the volume of vag playback
constexpr int SET_DIALOG_VOLUME = 0x406; // Command to set the volume of vag playback
// TODO - hack workaround for now, was originally 350
constexpr int MAX_ISO_FILES = 999; // maximum files on FS
constexpr int MAX_OPEN_FILES = 16; // maximum number of open files at a time.
/*!
* Record for file. There is one for each file in the FS, and pointers to each FileRecord act as
* an identifier.
* The location/size can't be counted on to be anything meaningful as it depends on the IsoFs
* implementation being used.
*/
struct FileRecord {
char name[12];
uint32_t location;
uint32_t size;
};
constexpr int LOAD_SOUND_BANK = 0x300; // Command to load a sound bank
constexpr int LOAD_MUSIC = 0x380; // Command to load music
constexpr int QUEUE_VAG_STREAM = 0x400; // Command to load a vag stream
constexpr int PLAY_VAG_STREAM = 0x401; // Command to play a vag stream
constexpr int STOP_VAG_STREAM = 0x402; // Command to stop a vag stream
constexpr int PAUSE_VAG_STREAM = 0x403; // Command to pause a vag stream
constexpr int CONTINUE_VAG_STREAM = 0x404; // Command to continue a vag stream
constexpr int SET_VAG_VOLUME = 0x405; // Command to set the volume of vag playback
constexpr int SET_DIALOG_VOLUME = 0x406; // Command to set the volume of vag playback
/*!
* Record for an open file.
@@ -67,6 +43,26 @@ struct LoadStackEntry {
uint32_t location; // sectors.
};
/*!
* API to access files. There are debug modes + reading from an ISO filesystem.
*/
struct IsoFs {
int (*init)(); // 0
FileRecord* (*find)(const char*); // 4
FileRecord* (*find_in)(const char*); // 8
uint32_t (*get_length)(FileRecord*); // c
LoadStackEntry* (*open)(FileRecord*, int32_t); // 10
LoadStackEntry* (*open_wad)(FileRecord*, int32_t); // 14
void (*close)(LoadStackEntry*); // 18
uint32_t (*begin_read)(LoadStackEntry*, void*, int32_t); // 1c
uint32_t (*sync_read)(); // 20
uint32_t (*load_sound_bank)(char*, SoundBank*); // 24
uint32_t (*load_music)(char*, s32*);
void (*poll_drive)();
};
struct IsoMessage;
/*!
* Header for a ISO data buffer.
*/
@@ -80,9 +76,6 @@ struct IsoBufferHeader {
u8* get_data() { return ((u8*)this) + sizeof(IsoBufferHeader); }
};
struct IsoMessage;
struct LoadStackEntry;
//! Callback function for data loads.
typedef u32 (*iso_callback_func)(IsoMessage* cmd, IsoBufferHeader* buffer);
@@ -115,40 +108,6 @@ struct IsoCommandLoadSingle : public IsoMessage {
s32 bytes_done; // 0x40
};
struct VagDirEntry;
/*!
* Command to do something.
*/
struct VagCommand : public IsoMessage {
FileRecord* file;
VagDirEntry* vag;
u32 buffer_number;
u32 data_left;
u32 started;
u32 paused;
u32 sample_rate;
u32 stop;
s32 end_point;
u32 unk2;
s32 volume;
u32 sound_id;
u32 priority;
u32 positioned;
Vec3w trans;
};
struct SoundBank;
struct SoundBankLoadCommand : public IsoMessage {
char bank_name[16];
SoundBank* bank;
};
struct MusicLoadCommand : public IsoMessage {
char music_name[16];
s32* music_handle;
};
/*!
* DGO Load State Machine states.
*/
@@ -185,6 +144,37 @@ struct DgoCommand : public IsoMessage {
u32 want_abort; // 0xd4, should we quit?
};
/*!
* Command to do something.
*/
struct VagCommand : public IsoMessage {
FileRecord* file;
VagDirEntry* vag;
u32 buffer_number;
u32 data_left;
u32 started;
u32 paused;
u32 sample_rate;
u32 stop;
s32 end_point;
u32 unk2;
s32 volume;
u32 sound_id;
u32 priority;
u32 positioned;
Vec3w trans;
};
struct SoundBankLoadCommand : public IsoMessage {
char bank_name[16];
SoundBank* bank;
};
struct MusicLoadCommand : public IsoMessage {
char music_name[16];
s32* music_handle;
};
/*!
* Priority Stack entry.
*/
@@ -196,28 +186,4 @@ struct PriStackEntry {
void reset();
};
/*!
* API to access files. There are debug modes + reading from an ISO filesystem.
*/
struct IsoFs {
int (*init)(u8*); // 0
FileRecord* (*find)(const char*); // 4
FileRecord* (*find_in)(const char*); // 8
uint32_t (*get_length)(FileRecord*); // c
LoadStackEntry* (*open)(FileRecord*, int32_t); // 10
LoadStackEntry* (*open_wad)(FileRecord*, int32_t); // 14
void (*close)(LoadStackEntry*); // 18
uint32_t (*begin_read)(LoadStackEntry*, void*, int32_t); // 1c
uint32_t (*sync_read)(); // 20
uint32_t (*load_sound_bank)(char*, void*); // 24
uint32_t (*load_music)(char*, void*);
void (*poll_drive)();
};
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
} // namespace jak1
@@ -2,18 +2,18 @@
#include <cstring>
#include "iso.h"
#include "ramdisk.h"
#include "sbank.h"
#include "srpc.h"
#include "ssound.h"
#include "common/util/Assert.h"
#include "game/overlord/jak1/iso.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak1 {
static s32 gargc;
static const char* const* gargv;
static bool* init_complete;
@@ -46,7 +46,7 @@ int start_overlord(int argc, const char* const* argv) {
thread_param.initPriority = 98;
thread_param.stackSize = 0x800;
thread_param.option = 0;
thread_param.entry = (void*)Thread_Server;
thread_param.entry = Thread_Server;
strcpy(thread_param.name, "Server"); // added
auto thread_server = CreateThread(&thread_param);
if (thread_server <= 0) {
@@ -57,7 +57,7 @@ int start_overlord(int argc, const char* const* argv) {
thread_param.initPriority = 96;
thread_param.stackSize = 0x800;
thread_param.option = 0;
thread_param.entry = (void*)Thread_Player;
thread_param.entry = Thread_Player;
strcpy(thread_param.name, "Player"); // added
auto thread_player = CreateThread(&thread_param);
if (thread_player <= 0) {
@@ -68,7 +68,7 @@ int start_overlord(int argc, const char* const* argv) {
thread_param.initPriority = 99;
thread_param.stackSize = 0x1000;
thread_param.option = 0;
thread_param.entry = (void*)Thread_Loader;
thread_param.entry = Thread_Loader;
strcpy(thread_param.name, "Loader"); // added for debug
auto thread_loader = CreateThread(&thread_param);
if (thread_loader <= 0) {
@@ -83,13 +83,14 @@ int start_overlord(int argc, const char* const* argv) {
return 0;
}
static void call_start() {
static u32 call_start() {
start_overlord(gargc, gargv);
*init_complete = true;
while (true) {
SleepThread();
}
return 0;
}
int start_overlord_wrapper(int argc, const char* const* argv, bool* signal) {
@@ -104,7 +105,7 @@ int start_overlord_wrapper(int argc, const char* const* argv, bool* signal) {
param.stackSize = 0x800;
param.option = 0;
strcpy(param.name, "start"); // added for debug
param.entry = (void*)call_start;
param.entry = call_start;
auto start_thread = CreateThread(&param);
StartThread(start_thread, 0);
@@ -112,10 +113,4 @@ int start_overlord_wrapper(int argc, const char* const* argv, bool* signal) {
return 0;
}
/*!
* Loop endlessly and never return.
*/
void ExitIOP() {
while (true) {
}
}
} // namespace jak1
@@ -1,10 +1,6 @@
#pragma once
#ifndef JAK_V2_OVERLORD_H
#define JAK_V2_OVERLORD_H
namespace jak1 {
int start_overlord(int argc, const char* const* argv);
int start_overlord_wrapper(int argc, const char* const* argv, bool* signal);
void ExitIOP();
#endif // JAK_V2_OVERLORD_H
} // namespace jak1
@@ -9,19 +9,19 @@
#include <cstdio>
#include <cstring>
#include "iso.h"
#include "iso_api.h"
#include "common/common_types.h"
#include "common/util/Assert.h"
#include "game/common/ramdisk_rpc_types.h"
#include "game/overlord/jak1/iso.h"
#include "game/overlord/jak1/iso_api.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
// Note - the RAMDISK code supports having multiple files, but it appears only one file can ever be
// used at a time.
namespace jak1 {
constexpr int RAMDISK_SIZE = 0xcac00; // Memory size of RAMDISK
constexpr int RAMDISK_MAX_FILES = 16; // Maximum number of files to store in RAMDISK.
constexpr int RAMDISK_RETURN_BUFFER_SIZE = 0x2000; // Maximum size of an individual RAMDISK read
@@ -192,3 +192,4 @@ void* RPC_Ramdisk(unsigned int fno, void* data, int size) {
}
return nullptr;
}
} // namespace jak1
@@ -6,15 +6,12 @@
* Also called "Server".
*/
#ifndef JAK_RAMDISK_H
#define JAK_RAMDISK_H
#include "common/common_types.h"
namespace jak1 {
extern u32 gMemFreeAtStart;
void ramdisk_init_globals();
void InitRamdisk();
u32 Thread_Server();
#endif // JAK_RAMDISK_H
} // namespace jak1
@@ -3,10 +3,8 @@
#include <cstdio>
#include <cstring>
#include "iso.h"
#include "iso_api.h"
#include "ramdisk.h"
#include "sbank.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
@@ -16,7 +14,12 @@
#include "game/common/loader_rpc_types.h"
#include "game/common/player_rpc_types.h"
#include "game/graphics/gfx.h"
#include "game/overlord/soundcommon.h"
#include "game/overlord/common/iso.h"
#include "game/overlord/common/sbank.h"
#include "game/overlord/common/soundcommon.h"
#include "game/overlord/common/srpc.h"
#include "game/overlord/common/ssound.h"
#include "game/overlord/jak1/iso.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
@@ -26,33 +29,22 @@
using namespace iop;
MusicTweaks gMusicTweakInfo;
namespace jak1 {
constexpr int SRPC_MESSAGE_SIZE = 0x50;
static uint8_t gLoaderBuf[SRPC_MESSAGE_SIZE];
static uint8_t gPlayerBuf[SRPC_MESSAGE_SIZE * 128];
int32_t gSoundEnable = 1;
static u32 gInfoEE = 0; // EE address where we should send info on each frame.
s16 gFlava;
static s32 gMusic;
s32 gMusicTweak = 0x80;
s32 gMusicPause = 0;
u32 gFreeMem = 0;
u32 gFrameNum = 0;
u8 gFPS = 60;
// added
u32 gMusicFadeHack = 0;
static SoundIopInfo info;
s32 gVAG_Id = 0; // TODO probably doesn't belong here.
// english, french, germain, spanish, italian, japanese, uk.
static const char* languages[] = {"ENG", "FRE", "GER", "SPA", "ITA", "JAP", "UKE"};
const char* gLanguage = nullptr;
const char* languages[] = {"ENG", "FRE", "GER", "SPA", "ITA", "JAP", "UKE"};
void srpc_init_globals() {
memset((void*)&gMusicTweakInfo, 0, sizeof(gMusicTweakInfo));
memset((void*)gLoaderBuf, 0, sizeof(gLoaderBuf));
memset((void*)gPlayerBuf, 0, sizeof(gPlayerBuf));
gSoundEnable = 1;
@@ -61,8 +53,6 @@ void srpc_init_globals() {
}
void* RPC_Player(unsigned int fno, void* data, int size);
void* RPC_Player2(unsigned int fno, void* data, int size);
PerGameVersion<void* (*)(unsigned int, void*, int)> RPC_Player_Func = {RPC_Player, RPC_Player2};
u32 Thread_Player() {
sceSifQueueData dq;
@@ -72,17 +62,14 @@ u32 Thread_Player() {
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, PLAYER_RPC_ID[g_game_version], RPC_Player_Func[g_game_version],
gPlayerBuf, nullptr, nullptr, &dq);
sceSifRegisterRpc(&serve, PLAYER_RPC_ID[g_game_version], RPC_Player, gPlayerBuf, nullptr, nullptr,
&dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
void* RPC_Loader(unsigned int fno, void* data, int size);
void* RPC_Loader2(unsigned int fno, void* data, int size);
PerGameVersion<void* (*)(unsigned int, void*, int)> RPC_Loader_Func = {RPC_Loader, RPC_Loader2};
u32 Thread_Loader() {
sceSifQueueData dq;
@@ -92,8 +79,8 @@ u32 Thread_Loader() {
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, LOADER_RPC_ID[g_game_version], RPC_Loader_Func[g_game_version],
gLoaderBuf, nullptr, nullptr, &dq);
sceSifRegisterRpc(&serve, LOADER_RPC_ID[g_game_version], RPC_Loader, gLoaderBuf, nullptr, nullptr,
&dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
@@ -362,301 +349,6 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
return nullptr;
}
void* RPC_Player2(unsigned int /*fno*/, void* data, int size) {
if (!gSoundEnable) {
return nullptr;
}
gFreeMem = QueryTotalFreeMemSize();
if (!PollSema(gSema)) {
if (gMusic) {
if (!gMusicPause && !LookupSound(666)) {
Sound* music = AllocateSound();
if (music != nullptr) {
gMusicFade = 0;
gMusicFadeDir = 1;
SetMusicVol();
music->sound_handle = snd_PlaySoundVolPanPMPB(gMusic, 0, 0x400, -1, 0, 0);
music->id = 666;
music->is_music = 1;
}
}
}
SignalSema(gSema);
}
SetMusicVol();
Sound* music = LookupSound(666);
if (music != nullptr) {
snd_SetSoundVolPan(music->sound_handle, 0x7FFFFFFF, 0);
}
int n_messages = size / SRPC_MESSAGE_SIZE;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
if (!gSoundEnable) {
return nullptr;
}
while (n_messages > 0) {
switch (cmd->j2command) {
case Jak2SoundCommand::play: {
if (!cmd->play.sound_id) {
break;
}
auto sound = LookupSound(cmd->play.sound_id);
if (sound != nullptr) {
// update
sound->params = cmd->play.parms;
sound->is_music = false;
SFXUserData data{};
s32 found = snd_GetSoundUserData(0, nullptr, -1, sound->name, &data);
if ((sound->params.mask & 0x40) == 0) {
s16 fo_min = 5;
if (found && data.data[0])
fo_min = data.data[0];
sound->params.fo_min = fo_min;
}
if ((sound->params.mask & 0x80) == 0) {
s16 fo_max = 30;
if (found && data.data[1])
fo_max = data.data[1];
sound->params.fo_max = fo_max;
}
if ((sound->params.mask & 0x100) == 0) {
s16 fo_curve = 2;
if (found && data.data[2])
fo_curve = data.data[2];
sound->params.fo_curve = fo_curve;
}
UpdateVolume(sound);
snd_SetSoundPitchModifier(sound->sound_handle, sound->params.pitch_mod);
if (sound->params.mask & 0x4) {
snd_SetSoundPitchBend(sound->sound_handle, sound->params.bend);
}
if (sound->params.mask & 0x800) {
snd_SetSoundReg(sound->sound_handle, 0, sound->params.reg[0]);
}
if (sound->params.mask & 0x1000) {
snd_SetSoundReg(sound->sound_handle, 1, sound->params.reg[1]);
}
if (sound->params.mask & 0x2000) {
snd_SetSoundReg(sound->sound_handle, 2, sound->params.reg[2]);
}
} else {
// new sound
sound = AllocateSound();
if (sound == nullptr) {
// no free sounds
break;
}
strcpy_toupper(sound->name, cmd->play.name);
// TODO update params struct
sound->params = cmd->play.parms;
sound->is_music = false;
sound->bank_entry = nullptr;
SFXUserData data{};
s32 found = snd_GetSoundUserData(0, nullptr, -1, sound->name, &data);
if ((sound->params.mask & 0x40) == 0) {
s16 fo_min = 5;
if (found && data.data[0])
fo_min = data.data[0];
sound->params.fo_min = fo_min;
}
if ((sound->params.mask & 0x80) == 0) {
s16 fo_max = 30;
if (found && data.data[1])
fo_max = data.data[1];
sound->params.fo_max = fo_max;
}
if ((sound->params.mask & 0x100) == 0) {
s16 fo_curve = 2;
if (found && data.data[2])
fo_curve = data.data[2];
sound->params.fo_curve = fo_curve;
}
// lg::warn("RPC: PLAY {} v:{}, p:{}", sound->name, GetVolume(sound), GetPan(sound));
s32 handle = snd_PlaySoundByNameVolPanPMPB(0, nullptr, sound->name, GetVolume(sound),
GetPan(sound), sound->params.pitch_mod,
sound->params.bend);
sound->sound_handle = handle;
if (handle != 0) {
sound->id = cmd->play.sound_id;
if (sound->params.mask & 0x800) {
snd_SetSoundReg(sound->sound_handle, 0, sound->params.reg[0]);
}
if (sound->params.mask & 0x1000) {
snd_SetSoundReg(sound->sound_handle, 1, sound->params.reg[1]);
}
if (sound->params.mask & 0x2000) {
snd_SetSoundReg(sound->sound_handle, 2, sound->params.reg[2]);
}
}
}
} break;
case Jak2SoundCommand::pause_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_PauseSound(sound->sound_handle);
}
// TODO vag
} break;
case Jak2SoundCommand::stop_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_StopSound(sound->sound_handle);
}
// TODO vag
} break;
case Jak2SoundCommand::continue_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_ContinueSound(sound->sound_handle);
}
// TODO vag
} break;
case Jak2SoundCommand::set_param: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
u32 mask = cmd->param.parms.mask;
if (sound != nullptr) {
if (mask & 1) {
if (mask & 0x10) {
sound->auto_time = cmd->param.auto_time;
sound->new_volume = cmd->param.parms.volume;
} else {
sound->params.volume = cmd->param.parms.volume;
}
}
if (mask & 0x20) {
sound->params.trans = cmd->param.parms.trans;
}
if (mask & 0x21) {
UpdateVolume(sound);
}
if (mask & 2) {
sound->params.pitch_mod = cmd->param.parms.pitch_mod;
if (mask & 0x10) {
snd_AutoPitch(sound->sound_handle, sound->params.pitch_mod, cmd->param.auto_time,
cmd->param.auto_from);
} else {
snd_SetSoundPitchModifier(sound->sound_handle, cmd->param.parms.pitch_mod);
}
}
if (mask & 4) {
sound->params.bend = cmd->param.parms.bend;
if (mask & 0x10) {
snd_AutoPitchBend(sound->sound_handle, sound->params.bend, cmd->param.auto_time,
cmd->param.auto_from);
} else {
snd_SetSoundPitchBend(sound->sound_handle, cmd->param.parms.bend);
}
}
if (mask & 0x400) {
sound->params.priority = cmd->param.parms.priority;
}
if (mask & 0x8) {
sound->params.group = cmd->param.parms.group;
}
if (mask & 0x40) {
sound->params.fo_min = cmd->param.parms.fo_min;
}
if (mask & 0x80) {
sound->params.fo_max = cmd->param.parms.fo_max;
}
if (mask & 0x100) {
sound->params.fo_curve = cmd->param.parms.fo_curve;
}
if (mask & 0x800) {
sound->params.reg[0] = cmd->param.parms.reg[0];
snd_SetSoundReg(sound->sound_handle, 0, cmd->param.parms.reg[0]);
}
if (mask & 0x1000) {
sound->params.reg[1] = cmd->param.parms.reg[1];
snd_SetSoundReg(sound->sound_handle, 1, cmd->param.parms.reg[1]);
}
if (mask & 0x2000) {
sound->params.reg[2] = cmd->param.parms.reg[2];
snd_SetSoundReg(sound->sound_handle, 2, cmd->param.parms.reg[2]);
}
}
// TODO vag
} break;
case Jak2SoundCommand::set_master_volume: {
u32 group = cmd->master_volume.group.group;
// FIXME array of set volumes
for (int i = 0; i < 32; i++) {
if (((group >> i) & 1) != 0) {
if (i == 1) {
gMusicVol = cmd->master_volume.volume;
} else if (i == 2) {
SetDialogVolume(cmd->master_volume.volume);
} else {
snd_SetMasterVolume(i, cmd->master_volume.volume);
}
}
}
} break;
case Jak2SoundCommand::pause_group: {
snd_PauseAllSoundsInGroup(cmd->group.group);
if (cmd->group.group & 2) {
gMusicPause = 1;
}
if (cmd->group.group & 4) {
// TODO vag
}
} break;
case Jak2SoundCommand::stop_group: {
KillSoundsInGroup(cmd->group.group);
} break;
case Jak2SoundCommand::continue_group: {
snd_ContinueAllSoundsInGroup(cmd->group.group);
if (cmd->group.group & 2) {
gMusicPause = 0;
}
if (cmd->group.group & 4) {
// TODO vag
}
} break;
case Jak2SoundCommand::set_midi_reg: {
if (cmd->midi_reg.reg == 16) {
snd_SetGlobalExcite(cmd->midi_reg.value);
} else {
Sound* sound = LookupSound(666);
if (sound != nullptr) {
snd_SetMIDIRegister(sound->sound_handle, cmd->midi_reg.reg, cmd->midi_reg.value);
}
}
} break;
case Jak2SoundCommand::set_reverb: {
lg::warn("RPC_Player: unimplemented set_reverb");
// TODO reverb
} break;
case Jak2SoundCommand::set_ear_trans: {
SetEarTrans(&cmd->ear_trans_j2.ear_trans0, &cmd->ear_trans_j2.ear_trans1,
&cmd->ear_trans_j2.cam_trans, cmd->ear_trans_j2.cam_angle);
} break;
case Jak2SoundCommand::shutdown: {
gSoundEnable = 0;
} break;
case Jak2SoundCommand::set_fps: {
gFPS = cmd->fps.fps;
} break;
default:
ASSERT_MSG(false, fmt::format("Unhandled RPC Player command {}",
magic_enum::enum_name(cmd->j2command)));
}
n_messages--;
cmd++;
}
return nullptr;
}
void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
int n_messages = size / SRPC_MESSAGE_SIZE;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
@@ -748,104 +440,6 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
return nullptr;
}
static void UnLoadMusic(s32* handle) {
gMusicFadeDir = -1;
while (gMusicFade)
DelayThread(1000);
snd_UnloadBank(*handle);
snd_ResolveBankXREFS();
*handle = 0;
}
void* RPC_Loader2(unsigned int /*fno*/, void* data, int size) {
int n_messages = size / SRPC_MESSAGE_SIZE;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
if (!gSoundEnable) {
return nullptr;
}
while (n_messages > 0) {
switch (cmd->j2command) {
case Jak2SoundCommand::load_bank: {
if (LookupBank(cmd->load_bank.bank_name)) {
break;
}
auto bank = AllocateBankName(cmd->load_bank.bank_name);
if (bank == nullptr) {
break;
}
strncpy(bank->name, cmd->load_bank.bank_name, 16);
bank->in_use = true;
bank->unk4 = 0;
LoadSoundBank(cmd->load_bank.bank_name, bank);
} break;
case Jak2SoundCommand::load_music: {
while (WaitSema(gSema))
;
if (gMusic) {
UnLoadMusic(&gMusic);
}
LoadMusic(cmd->load_bank.bank_name, &gMusic);
SignalSema(gSema);
} break;
case Jak2SoundCommand::unload_bank: {
auto bank = LookupBank(cmd->load_bank.bank_name);
if (!bank) {
break;
}
auto handle = bank->bank_handle;
if (!bank->unk4) {
bank->in_use = false;
}
bank->in_use = false;
snd_UnloadBank(handle);
snd_ResolveBankXREFS();
} break;
case Jak2SoundCommand::get_irx_version: {
cmd->irx_version.major = 4;
cmd->irx_version.minor = 0;
gInfoEE = cmd->irx_version.ee_addr;
return data;
} break;
case Jak2SoundCommand::set_language: {
gLanguage = languages[cmd->set_language.langauge_id];
} break;
case Jak2SoundCommand::list_sounds: {
// Not present in real jak2 overlord
PrintActiveSounds();
} break;
case Jak2SoundCommand::unload_music: {
while (WaitSema(gSema))
;
if (gMusic) {
UnLoadMusic(&gMusic);
}
SignalSema(gSema);
} break;
case Jak2SoundCommand::set_stereo_mode: {
s32 mode = cmd->stereo_mode.stereo_mode;
if (mode == 0) {
snd_SetPlayBackMode(1);
} else if (mode == 1) {
snd_SetPlayBackMode(2);
} else if (mode == 2) {
snd_SetPlayBackMode(0);
}
} break;
default:
ASSERT_MSG(false, fmt::format("Unhandled RPC Loader command {}",
magic_enum::enum_name(cmd->j2command)));
}
n_messages--;
cmd++;
}
return nullptr;
}
static s32 dmaid = 0;
s32 VBlank_Handler(void*) {
@@ -914,3 +508,4 @@ s32 VBlank_Handler(void*) {
return 1;
}
} // namespace jak1
+94
View File
@@ -0,0 +1,94 @@
#pragma once
#include "ssound.h"
#include "common/common_types.h"
#include "game/overlord/common/srpc.h"
namespace jak1 {
void srpc_init_globals();
extern s32 gVAG_Id;
enum class Jak1SoundCommand : u16 {
LOAD_BANK = 0,
LOAD_MUSIC = 1,
UNLOAD_BANK = 2,
PLAY = 3,
PAUSE_SOUND = 4,
STOP_SOUND = 5,
CONTINUE_SOUND = 6,
SET_PARAM = 7,
SET_MASTER_VOLUME = 8,
PAUSE_GROUP = 9,
STOP_GROUP = 10,
CONTINUE_GROUP = 11,
GET_IRX_VERSION = 12,
SET_FALLOFF_CURVE = 13,
SET_SOUND_FALLOFF = 14,
RELOAD_INFO = 15,
SET_LANGUAGE = 16,
SET_FLAVA = 17,
SET_REVERB = 18,
SET_EAR_TRANS = 19,
SHUTDOWN = 20,
LIST_SOUNDS = 21,
UNLOAD_MUSIC = 22,
MIRROR_MODE = 201,
};
struct SoundRpcCommand {
u16 rsvd1;
union {
Jak1SoundCommand j1command;
// Jak2SoundCommand j2command;
};
union {
SoundRpcGetIrxVersion irx_version;
SoundRpcBankCommand load_bank;
SoundRpcSetLanguageCommand set_language;
SoundRpcPlayCommand play;
SoundRpcSoundIdCommand sound_id;
SoundRpcSetFPSCommand fps;
SoundRpcSetEarTrans ear_trans;
SoundRpc2SetEarTrans ear_trans_j2;
SoundRpcSetReverb reverb;
SoundRpcSetFallof fallof;
SoundRpcSetFallofCurve fallof_curve;
SoundRpcGroupCommand group;
SoundRpcSetFlavaCommand flava;
SoundRpcMasterVolCommand master_volume;
SoundRpcSetParamCommand param;
SoundRpcStereoMode stereo_mode;
SoundRpcSetMidiReg midi_reg;
SoundRpcSetMirrror mirror;
u8 max_size[0x4C]; // Temporary
};
};
static_assert(sizeof(SoundRpcCommand) == 0x50);
struct SoundIopInfo {
u32 frame;
s32 strpos;
u32 std_id;
u32 freemem;
u8 chinfo[48];
u32 freemem2;
u32 nocd;
u32 dirtycd;
u32 diskspeed[2];
u32 lastspeed;
s32 dupseg;
u32 times[41];
u32 times_seq;
u8 pad[10]; // pad up to transfer size
};
u32 Thread_Loader();
u32 Thread_Player();
s32 VBlank_Handler(void*);
} // namespace jak1
+153
View File
@@ -0,0 +1,153 @@
#include "ssound.h"
#include <cstdio>
#include <cstring>
#include "common/util/Assert.h"
#include "game/overlord/common/srpc.h"
#include "game/overlord/common/ssound.h"
#include "game/overlord/jak1/iso.h"
#include "game/runtime.h"
#include "game/sound/sndshim.h"
using namespace iop;
namespace jak1 {
VolumePair gPanTable[361];
s32 gMusicVol = 0x400;
u32 gStreamSRAM = 0;
u32 gTrapSRAM = 0;
void CatalogSRAM() {}
static void* SndMemAlloc();
static void SndMemFree(void* ptr);
void InitSound_Overlord() {
for (auto& s : gSounds) {
s.id = 0;
}
if (g_game_version == GameVersion::Jak1) {
SetCurve(1, 0, 0);
SetCurve(2, 4096, 0);
SetCurve(3, 0, 4096);
SetCurve(4, 2048, 0);
SetCurve(5, 2048, 2048);
SetCurve(6, -4096, 0);
SetCurve(7, -2048, 0);
} else {
SetCurve(2, 0, 0);
SetCurve(9, 0, 0);
SetCurve(11, 0, 0);
SetCurve(10, 0, 0);
SetCurve(3, 4096, 0);
SetCurve(4, 0, 4096);
SetCurve(5, 2048, 0);
SetCurve(6, 2048, 2048);
SetCurve(7, -4096, 0);
SetCurve(8, -2048, 0);
}
snd_StartSoundSystem();
snd_RegisterIOPMemAllocator(SndMemAlloc, SndMemFree);
snd_LockVoiceAllocator(1);
u32 voice = snd_ExternVoiceAlloc(2, 0x7f);
snd_UnlockVoiceAllocator();
// The voice allocator returns a number in the range 0-47 where voices
// 0-23 are on SPU Core 0 and 24-47 are on core 2.
// For some reason we convert it to this format where 0-47 alternate core every step.
voice = voice / 24 + ((voice % 24) * 2);
// Allocate SPU RAM for our streams.
// (Which we don't need on PC)
gStreamSRAM = snd_SRAMMalloc(0xc030);
gTrapSRAM = gStreamSRAM + 0xC000;
snd_SetMixerMode(0, 0);
for (int i = 0; i < 8; i++) {
snd_SetGroupVoiceRange(i, 0x10, 0x2f);
}
snd_SetGroupVoiceRange(1, 0, 0xf);
snd_SetGroupVoiceRange(2, 0, 0xf);
snd_SetReverbDepth(SND_CORE_0 | SND_CORE_1, 0, 0);
snd_SetReverbType(SND_CORE_0, SD_REV_MODE_OFF);
snd_SetReverbType(SND_CORE_1, SD_REV_MODE_OFF);
CatalogSRAM();
for (int i = 0; i < 91; i++) {
s16 opposing_front = static_cast<s16>(((i * 0x33ff) / 0x5a) + 0xc00);
s16 rear_right = static_cast<s16>(((i * -0x2800) / 0x5a) + 0x3400);
s16 rear_left = static_cast<s16>(((i * -0xbff) / 0x5a) + 0x3fff);
gPanTable[90 - i].left = 0x3FFF;
gPanTable[180 - i].left = opposing_front;
gPanTable[270 - i].left = rear_right;
gPanTable[360 - i].left = rear_left;
gPanTable[i].right = opposing_front;
gPanTable[90 + i].right = 0x3FFF;
gPanTable[180 + i].right = rear_left;
gPanTable[270 + i].right = rear_right;
}
snd_SetPanTable((s16*)gPanTable);
snd_SetPlayBackMode(2);
SemaParam sema;
sema.attr = SA_THPRI;
sema.init_count = 1;
sema.max_count = 1;
sema.option = 0;
gSema = CreateSema(&sema);
if (gSema < 0) {
while (true)
;
}
}
void SetEarTrans(Vec3w* ear_trans0, Vec3w* ear_trans1, Vec3w* cam_trans, s32 cam_angle) {
s32 tick = snd_GetTick();
u32 delta = tick - sLastTick;
sLastTick = tick;
gEarTrans[0] = *ear_trans0;
gEarTrans[1] = *ear_trans1;
gCamTrans = *cam_trans;
gCamAngle = cam_angle;
for (auto& s : gSounds) {
if (s.id != 0 && s.is_music == 0) {
if (s.auto_time != 0) {
UpdateAutoVol(&s, delta);
}
UpdateLocation(&s);
}
}
SetVAGVol();
}
void SetMusicVol() {
s32 volume = (gMusicVol * gMusicFade >> 0x10) * gMusicTweak >> 7;
snd_SetMasterVolume(1, volume);
snd_SetMasterVolume(2, volume);
}
// Do we even need/want these
// TODO void SetBufferMem() {}
// TODO void ReleaseBufferMem() {}
static void* SndMemAlloc() {
return nullptr;
}
static void SndMemFree(void* /*ptr*/) {}
} // namespace jak1
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "game/overlord/common/sbank.h"
#include "game/overlord/common/ssound.h"
#include "game/sce/iop.h"
namespace jak1 {
extern VolumePair gPanTable[361];
extern u32 gStreamSRAM;
extern u32 gTrapSRAM;
extern s32 gMusicVol;
void InitSound_Overlord();
void SetEarTrans(Vec3w* ear_trans1, Vec3w* ear_trans2, Vec3w* cam_trans, s32 cam_angle);
void SetMusicVol();
} // namespace jak1
+226
View File
@@ -0,0 +1,226 @@
/*!
* @file stream.cpp
* OVERLORD streaming driver.
* Supports loading a file directly to the EE, or loading chunks of a chunked file.
*/
#include "stream.h"
#include <cstring>
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/common/play_rpc_types.h"
#include "game/common/str_rpc_types.h"
#include "game/overlord/common/iso.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/jak1/iso.h"
#include "game/overlord/jak1/iso_api.h"
#include "game/overlord/jak1/srpc.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak1 {
static RPC_Str_Cmd_Jak1 sSTRBuf;
static RPC_Play_Cmd_Jak1 sPLAYBuf[2];
void* RPC_STR(unsigned int fno, void* _cmd, int y);
void* RPC_PLAY(unsigned int fno, void* _cmd, int y);
static constexpr int PLAY_MSG_SIZE = 0x40;
static u32 global_vag_count = 0;
/*!
* 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.
StrFileHeaderJ1 header;
};
// the actual header cache.
constexpr int STR_INDEX_CACHE_SIZE = 4;
CacheEntry sCacheJ1[STR_INDEX_CACHE_SIZE];
void stream_init_globals() {
memset(&sSTRBuf, 0, sizeof(RPC_Str_Cmd_Jak1));
memset(&sPLAYBuf, 0, sizeof(RPC_Play_Cmd_Jak1) * 2);
}
/*!
* Run the STR RPC handler.
*/
u32 STRThread() {
sceSifQueueData dq;
sceSifServeData serve;
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, STR_RPC_ID[g_game_version], RPC_STR, &sSTRBuf, nullptr, nullptr, &dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
u32 PLAYThread() {
sceSifQueueData dq;
sceSifServeData serve;
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, PLAY_RPC_ID[g_game_version], RPC_PLAY, sPLAYBuf, nullptr, nullptr, &dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
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_Jak1*)_cmd;
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];
file_util::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 && sCacheJ1[cache_entry].fr != file_record) {
sCacheJ1[cache_entry].countdown--;
if (sCacheJ1[cache_entry].countdown < oldest) {
oldest_idx = cache_entry;
oldest = sCacheJ1[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;
sCacheJ1[oldest_idx].fr = file_record;
sCacheJ1[oldest_idx].countdown = INT32_MAX - 1;
if (!LoadISOFileToIOP(file_record, &sCacheJ1[oldest_idx].header, sizeof(StrFileHeaderJ1))) {
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,
sCacheJ1[cache_entry].header.sizes[cmd->chunk_id],
sCacheJ1[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 = sCacheJ1[cache_entry].header.sizes[cmd->chunk_id];
cmd->result = 0;
}
}
}
return cmd;
}
void* RPC_PLAY([[maybe_unused]] unsigned int fno, void* _cmd, int size) {
s32 n_messages = size / PLAY_MSG_SIZE;
char namebuf[16];
auto* cmd = (RPC_Play_Cmd_Jak1*)(_cmd);
while (n_messages > 0) {
if (cmd->name[0] == '$') {
char* name_part = &cmd->name[1];
size_t name_len = strlen(name_part);
if (name_len < 9) {
memset(namebuf, ' ', 8);
memcpy(namebuf, name_part, name_len);
} else {
memcpy(namebuf, name_part, 8);
}
// ASCII toupper
for (int i = 0; i < 8; i++) {
if (namebuf[i] >= 0x61 && namebuf[i] < 0x7b) {
namebuf[i] -= 0x20;
}
}
} else {
file_util::ISONameFromAnimationName(namebuf, cmd->name);
}
auto vag = FindVAGFile(namebuf);
memcpy(namebuf, "VAGWAD ", 8);
strcpy(&namebuf[8], gLanguage);
FileRecord* file = nullptr;
global_vag_count = (global_vag_count + 1) & 0x3f;
if (!cmd->result && global_vag_count == 0) {
namebuf[0] -= 3;
file = isofs->find_in(namebuf);
namebuf[0] += 3;
}
file = isofs->find_in(namebuf);
if (cmd->result == 0) {
PlayVAGStream(file, vag, cmd->address, 0x400, 1, nullptr);
} else if (cmd->result == 1) {
StopVAGStream(vag, 1);
} else {
QueueVAGStream(file, vag, 0, 1);
}
n_messages--;
cmd++;
}
return _cmd;
}
} // namespace jak1
@@ -1,11 +1,9 @@
#pragma once
#ifndef JAK_V2_STREAM_H
#define JAK_V2_STREAM_H
#include "common/common_types.h"
namespace jak1 {
u32 STRThread();
u32 PLAYThread();
void stream_init_globals();
#endif // JAK_V2_STREAM_H
} // namespace jak1
+232
View File
@@ -0,0 +1,232 @@
#include "dma.h"
#include "game/overlord/jak2/vag.h"
#include "game/sound/sdshim.h"
#include "game/sound/sndshim.h"
namespace jak2 {
// This file has SPU DMA functions. Unlike Jak 1, they run SPU DMA in the background, and it doesn't
// work correctly if we assume instant DMA. We end up running the DMA finished interrupt handler
// in the loop of ISO thread - this makes sure that DMA appears to finish pretty quickly, and before
// any more loading stuff happens (so loads will never "wait" for the fake simulated dma).
s32 SpuDmaStatus = 0; //! set to 1 when SPU DMA is in progress
VagCmd* DmaVagCmd; //! VagCmd currently doing SPU DMA
VagCmd* DmaStereoVagCmd; //! VagCmd for the stereo sibling SPU DMA
int pending_dma = 0; //! PC-port addition to indicate that "dma is running"
constexpr int kDmaDelay = 10;
void dma_init_globals() {
SpuDmaStatus = 0;
DmaVagCmd = nullptr;
DmaStereoVagCmd = nullptr;
pending_dma = 0;
}
/*!
* Interrupt handler for DMA completion.
*/
int SpuDmaIntr(int, void*) {
if (SpuDmaStatus != 1) {
return 0;
}
if (!DmaVagCmd) {
goto cleanup;
}
if (!DmaStereoVagCmd) {
// not stereo mode, just set status bits
if ((DmaVagCmd->num_processed_chunks & 1U) == 0) {
DmaVagCmd->sb_even_buffer_dma_complete = 1;
} else {
DmaVagCmd->sb_odd_buffer_dma_complete = 1;
}
} else {
// in stereo mode, we need to do two DMA transfers. The first one starts the second stereo one.
if (DmaStereoVagCmd->xfer_size != 0) {
// pick the appropriate double buffer
s16 chan;
u8* iop_ptr;
int spu_addr;
if ((DmaStereoVagCmd->num_processed_chunks & 1U) == 0) {
chan = DmaVagCmd->dma_chan;
iop_ptr = DmaStereoVagCmd->dma_iop_mem_ptr;
spu_addr = DmaStereoVagCmd->spu_stream_dma_mem_addr;
} else {
chan = DmaVagCmd->dma_chan;
iop_ptr = DmaStereoVagCmd->dma_iop_mem_ptr;
spu_addr = DmaStereoVagCmd->spu_stream_dma_mem_addr + 0x2000;
}
// clear the pending transfer, so we don't try to start it again
int old_xfer = DmaStereoVagCmd->xfer_size;
DmaStereoVagCmd->xfer_size = 0;
DmaStereoVagCmd->dma_iop_mem_ptr = nullptr;
// start the transfer!
pending_dma = kDmaDelay;
sceSdVoiceTrans((int)chan, 0, iop_ptr, spu_addr, old_xfer);
// and return. This will get called again on completion
return 0;
}
// if we made it here, both transfers are done, so just toggle the buffers.
if ((DmaVagCmd->num_processed_chunks & 1U) == 0) {
DmaVagCmd->sb_even_buffer_dma_complete = 1;
DmaStereoVagCmd->sb_even_buffer_dma_complete = 1;
} else {
DmaVagCmd->sb_odd_buffer_dma_complete = 1;
DmaStereoVagCmd->sb_odd_buffer_dma_complete = 1;
}
}
// if we just finished the first upload, start playing! I'm not entirely sure if this is playing
// sound yet, or if we're just looping and waiting for the second upload to actually start.
// (we don't set the appropriate volumes here, so this is probably not the real playback)
if (DmaVagCmd->num_processed_chunks == 1) {
int pitch = CalculateVAGPitch(DmaVagCmd->pitch1, DmaVagCmd->unk_256_pitch2);
if (!DmaStereoVagCmd) {
DmaVagCmd->spu_addr_to_start_playing = 0;
sceSdSetAddr(((s16)DmaVagCmd->voice) | 0x2040, DmaVagCmd->spu_stream_dma_mem_addr + 0x30);
sceSdSetParam((u16)DmaVagCmd->voice | 0x300, 0xf);
sceSdSetParam((u16)DmaVagCmd->voice | 0x400, 0x1fc0);
sceSdSetParam((u16)DmaVagCmd->voice | 0x200, pitch);
// start playback!
sceSdkey_on_jak2_voice(DmaVagCmd->voice);
} else {
// same for stereo, but we start both voices.
DmaVagCmd->spu_addr_to_start_playing = 0;
DmaStereoVagCmd->spu_addr_to_start_playing = 0;
sceSdSetAddr((u16)DmaVagCmd->voice | 0x2040, DmaVagCmd->spu_stream_dma_mem_addr + 0x30);
sceSdSetAddr((u16)DmaStereoVagCmd->voice | 0x2040,
DmaStereoVagCmd->spu_stream_dma_mem_addr + 0x30);
sceSdSetParam((u16)DmaVagCmd->voice | 0x300, 0xf);
sceSdSetParam((u16)DmaStereoVagCmd->voice | 0x300, 0xf);
sceSdSetParam((u16)DmaVagCmd->voice | 0x400, 0x1fc0);
sceSdSetParam((u16)DmaStereoVagCmd->voice | 0x400, 0x1fc0);
sceSdSetParam((u16)DmaVagCmd->voice | 0x200, pitch);
sceSdSetParam((u16)DmaStereoVagCmd->voice | 0x200, pitch);
sceSdkey_on_jak2_voice(DmaVagCmd->voice);
sceSdkey_on_jak2_voice(DmaStereoVagCmd->voice);
}
} else if (DmaVagCmd->num_processed_chunks == 2) {
// on the second chunk's DMA finish, start playing by unpausing.
// set playing flag
DmaVagCmd->sb_playing = 1;
if (DmaStereoVagCmd) {
DmaStereoVagCmd->sb_playing = 1;
}
// if we paused since the first, kill the voice.
if (DmaVagCmd->sb_paused) {
if (!DmaStereoVagCmd) {
sceSdSetParam((u16)DmaVagCmd->voice | 0x200, 0);
} else {
sceSdSetParam((u16)DmaStereoVagCmd->voice | 0x200, 0);
sceSdSetParam((u16)DmaVagCmd->voice | 0x200, 0);
}
sceSdkey_off_jak2_voice(DmaVagCmd->voice);
goto hack;
}
// mark as paused manually and unpause to start playback.
DmaVagCmd->sb_paused = 1;
UnPauseVAG(DmaVagCmd, 0);
hack:;
}
// now that we're done, mark it as not in use and remove from DmaVagCmd.
DmaVagCmd->safe_to_change_dma_fields = 1;
if (DmaStereoVagCmd) {
DmaStereoVagCmd->safe_to_change_dma_fields = 1;
}
DmaVagCmd = nullptr;
DmaStereoVagCmd = nullptr;
cleanup:
// sceSdSetTransIntrHandler(param_1, nullptr, nullptr);
// if (-1 < param_1) {
// snd_FreeSPUDMA(param_1);
// }
SpuDmaStatus = 0;
return 0;
}
/*!
* Call any pending DMA transfer complete interrupt handlers.
*/
void spu_dma_hack() {
if (pending_dma) {
pending_dma--;
if (!pending_dma) {
SpuDmaIntr(0, nullptr);
}
}
}
/*!
* Despite the name, this does not sync and just starts dma.
*/
bool DMA_SendToSPUAndSync(uint8_t* iop_mem,
int size_one_side,
int spu_addr,
VagCmd* cmd,
int /*disable_intr*/) {
int chan;
int ret;
if ((SpuDmaStatus == 0) && (chan = snd_GetFreeSPUDMA(), chan != -1)) {
DmaVagCmd = cmd;
if (cmd) {
auto* sibling = cmd->stereo_sibling;
// mark as in-use by dma.
cmd->safe_to_change_dma_fields = 0;
DmaStereoVagCmd = sibling;
if (sibling) {
sibling->dma_iop_mem_ptr = iop_mem + size_one_side;
sibling->xfer_size = size_one_side;
sibling->num_processed_chunks = cmd->num_processed_chunks;
cmd->dma_chan = chan;
}
}
SpuDmaStatus = 1;
// note: I've bypassed the way the dma interrupts work for jak 2.
// here, the interrupt handler set is removed, and we instead set a pending_dma flag.
// the actual interrupt will be run from iso.cpp, in the isothread loop.
// sceSdSetTransIntrHandler(chan, SpuDmaIntr, 0);
pending_dma = kDmaDelay; // added
int sz = sceSdVoiceTrans((int)(short)chan, 0, iop_mem, spu_addr, size_one_side);
// if (disable_intr == 1) {
// CpuResumeIntr(local_28[0]);
//}
ret = int(size_one_side + 0x3fU & 0xffffffc0) <= sz;
} else {
ret = false;
// if (disable_intr == 1) {
// CpuResumeIntr(local_28[0]);
ret = false;
//}
}
return ret;
}
void DmaCancelThisVagCmd(VagCmd* param_1) {
if (DmaVagCmd == param_1) {
sceSdSetTransIntrHandler(DmaVagCmd->dma_chan, nullptr, nullptr);
if (-1 < DmaVagCmd->dma_chan) {
snd_FreeSPUDMA(DmaVagCmd->dma_chan);
}
DmaVagCmd = nullptr;
DmaStereoVagCmd = nullptr;
SpuDmaStatus = 0;
}
}
} // namespace jak2
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "common/common_types.h"
namespace jak2 {
struct VagCmd;
void dma_init_globals();
bool DMA_SendToSPUAndSync(uint8_t* iop_mem,
int size_one_side,
int spu_addr,
VagCmd* cmd,
int disable_intr);
} // namespace jak2
File diff suppressed because it is too large Load Diff
+177
View File
@@ -0,0 +1,177 @@
#pragma once
#include "common/common_types.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/jak2/pages.h"
namespace jak2 {
void iso_init_globals();
u32 ISOThread();
extern u32 IsoThreadCounter;
struct LoadStackEntry {
FileRecord* fr;
int cd_offset; // location in cd (sector). In OpenGOAL, it's just relative to the start of the
// file.
int uses_blzo;
int read_bytes;
u32 size_after_decompression;
};
enum class OpenMode { MODE0, MODE1, KNOWN_NOT_BLZO };
struct Buffer {
u8* decomp_buffer;
int decompressed_size;
Buffer* next;
u8* unk_12;
int data_buffer_idx;
int use_mode;
PageList* plist;
int num_pages;
int unk_32;
int free_pages;
Page* page;
int unk_44;
};
struct IsoFs {
int (*init)(); // 0
FileRecord* (*find)(const char*); // 4
FileRecord* (*find_in)(const char*); // 8
uint32_t (*get_length)(FileRecord*); // c
LoadStackEntry* (*open)(FileRecord*, int, OpenMode); // 10
LoadStackEntry* (*open_wad)(FileRecord*, int32_t); // 14
void (*close)(LoadStackEntry*); // 18
int (*page_begin_read)(LoadStackEntry*, Buffer*); // 1c
uint32_t (*sync_read)(); // 20
uint32_t (*load_sound_bank)(char*, SoundBank*); // 24
uint32_t (*load_music)(char*, s32*);
// void (*poll_drive)();
};
struct LargeBuffer {
PageList* page_list;
Page* allocated_pages;
Page* page_before_midway_page;
Page* mid_way_page;
Page* unk_page_end2;
Page* current_page;
int unk3;
int first_done_flag;
// ??
int done_flag_2;
int maybe_post_first_done;
int unk4;
// ??
int chunk_cnt_1;
int chunk_cnt_2;
u8* some_buffer;
u8* current_buffer_base_ptr;
u32 blzo_chunk_size;
int blzo_buffer_size_bytes;
u32 incoming_block_size;
u8* ptr; // pointer to data to be processed by DecompressBlock
u8* end_ptr; // end of data to be processed by DecompressBlock
int init0_2;
};
extern IsoFs* isofs;
extern LargeBuffer* SpLargeBuffer;
struct CmdHeader {
int unk_0;
int unk_4;
int cmd_kind;
int status;
int mbx_to_reply;
int thread_id;
int unk_24; // 24 (init to 1)
Buffer* callback_buffer; // 28
int (*callback)(CmdHeader*, Buffer*); // 32
LoadStackEntry* lse; // 36
};
struct CmdLoadSingleIop {
CmdHeader header;
FileRecord* file_record;
u8* dest_addr;
int length;
int length_to_copy;
int offset;
u8* ptr;
int unk_64;
};
/*!
* DGO Load State Machine states.
*/
enum class DgoState {
Init = 0,
Read_Header = 1,
Finish_Obj = 2,
Read_Last_Obj = 3,
Read_Obj_Header = 4,
Read_Obj_data = 5,
Finish_Dgo = 6,
Finish_Obj_NoDoubleBuffer = 7, // jak 2 only
};
struct CmdDgo {
CmdHeader header;
FileRecord* fr; // 0x28, DGO file that's open
u8* buffer1; // 0x2c, first EE buffer
u8* buffer2; // 0x30, second EE buffer
u8* buffer_heaptop; // 0x34, top of the heap
DgoHeader dgo_header; // 0x38, current DGO's header
ObjectHeader obj_header; // 0x78, current obj's header
u8* ee_dest_buffer; // 0xb8, where we are currently loading to on ee
u32 bytes_processed; // 0xbc, how many bytes processed in the current state
u32 objects_loaded; // 0xc0, completed object count
DgoState dgo_state; // 0xc4, state machine state
u32 finished_first_object; // 0xc8, have we finished loading the first object?
u32 buffer_toggle; // 0xcc, which buffer to load into (top, buffer1, buffer2)
u8* selected_buffer; // 0xd0, most recently completed load destination
u32 want_abort; // 0xd4, should we quit?
};
struct CmdLoadSoundBank {
CmdHeader header;
char bank_name[16];
SoundBank* bank;
};
struct CmdLoadMusic {
CmdHeader header;
char name[16];
s32* handle;
};
struct VagDirEntry {
char name[8];
u32 offset;
u32 flag;
};
struct VagCmd;
int NullCallback(CmdHeader* cmd, Buffer* buff);
u32 InitISOFS();
void IsoStopVagStream(VagCmd* param_1, int param_2);
void ProcessMessageData();
void IsoPlayVagStream(VagCmd* param_1, int param_2);
VagDirEntry* FindVAGFile(const char* name);
void IsoQueueVagStream(VagCmd* cmd, int param_2);
static constexpr int VAG_COUNT = 2728;
struct VagDir {
u32 count;
VagDirEntry vag[VAG_COUNT];
};
extern VagDir gVagDir;
extern s32 iso_mbx;
} // namespace jak2
+254
View File
@@ -0,0 +1,254 @@
#include "iso_api.h"
#include <cstring>
#include "common/util/FileUtil.h"
#include "game/overlord/common/srpc.h"
#include "game/overlord/jak2/iso_queue.h"
#include "game/overlord/jak2/streamlist.h"
#include "game/overlord/jak2/vag.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
namespace jak2 {
void EEVagAndVagwad(char* name, VagCmd* cmd) {
char name_buff[16];
int name_len;
if (*name != '$') {
name_len = strlen(name);
if (8 < name_len) {
file_util::ISONameFromAnimationName(name_buff, name);
goto LAB_0000cc60;
}
}
name_len = strlen(name);
if (*name == '$') {
name = name + 1;
}
if ((int)name_len < 9) {
memset(name_buff, 0x20, 8);
memcpy(name_buff, name, name_len);
} else {
memcpy(name_buff, name, 8);
}
{
int iVar3 = 0;
char* pbVar4 = name_buff;
do {
*pbVar4 = ::toupper(*pbVar4);
iVar3 = iVar3 + 1;
pbVar4 = (name_buff + iVar3);
} while (iVar3 < 8);
}
LAB_0000cc60:
cmd->vag_dir_entry = FindVAGFile(name_buff);
memcpy(name_buff, "VAGWAD ", 8);
strncpy(name_buff + 8, gLanguage, 4);
cmd->file_record = (isofs->find_in)(name_buff);
}
void QueueVAGStream(VagStrListNode* param_1) {
char bVar1;
int iVar2;
char* pcVar3;
char* pbVar4;
VagCmd cmd;
char local_20[12];
cmd.header.cmd_kind = 0x400;
cmd.header.mbx_to_reply = 0;
cmd.header.thread_id = 0;
if (param_1->unk_72 == 0) {
EEVagAndVagwad(param_1->name, &cmd);
cmd.vol_multiplier = 0x400;
} else {
pcVar3 = param_1->name;
strcpy(local_20, " ");
pbVar4 = local_20;
do {
bVar1 = *pcVar3;
pcVar3 = pcVar3 + 1;
*pbVar4 = bVar1;
pbVar4 = pbVar4 + 1;
if (*pcVar3 == 0x2e)
break;
} while (*pcVar3 != 0);
iVar2 = 0;
pbVar4 = local_20;
do {
if (*pbVar4 - 0x61 < 0x1a) {
*pbVar4 = *pbVar4 - 0x20;
}
iVar2 = iVar2 + 1;
pbVar4 = (local_20 + iVar2);
} while (iVar2 < 0xc);
cmd.vag_dir_entry = FindVAGFile(local_20);
strcpy(local_20, "VAGWAD ");
strncpy(local_20 + 8, gLanguage, 3);
cmd.file_record = (isofs->find_in)(local_20);
cmd.vol_multiplier = param_1->vol_multiplier;
cmd.unk_176 = param_1->unk_100;
}
strncpy(cmd.name, param_1->name, 0x30);
cmd.unk_136 = param_1->unk_72;
cmd.id = param_1->id;
cmd.plugin_id = param_1->unk_68;
cmd.priority = param_1->prio;
cmd.unk_288 = param_1->unk_76;
cmd.unk_292 = param_1->unk_80;
if (cmd.unk_288 != 0) {
cmd.byte10 = '\x01';
}
if (cmd.unk_292 != 0) {
cmd.unk_232 = '\x01';
}
cmd.unk_296 = 0;
IsoQueueVagStream(&cmd, 1);
}
int LoadISOFileToIOP(FileRecord* fr, uint8_t* addr, int len) {
int iVar1;
CmdLoadSingleIop cmd;
cmd.header.cmd_kind = 0x101;
cmd.header.mbx_to_reply = 0;
cmd.header.thread_id = GetThreadId();
cmd.file_record = fr;
cmd.dest_addr = addr;
cmd.length = len;
SendMbx(iso_mbx, &cmd);
SleepThread();
iVar1 = 0;
if (cmd.header.status == 0) {
iVar1 = cmd.length_to_copy;
}
return iVar1;
}
int LoadISOFileToEE(FileRecord* param_1, uint32_t param_2, int param_3) {
int iVar1;
CmdLoadSingleIop auStack88;
auStack88.header.cmd_kind = 0x100;
auStack88.header.mbx_to_reply = 0;
auStack88.header.thread_id = GetThreadId();
auStack88.file_record = param_1;
auStack88.dest_addr = (u8*)(u64)param_2;
auStack88.length = param_3;
SendMbx(iso_mbx, &auStack88);
SleepThread();
iVar1 = 0;
if (auStack88.header.status == 0) {
iVar1 = auStack88.length_to_copy;
}
return iVar1;
}
int LoadISOFileChunkToEE(FileRecord* param_1, uint32_t param_2, int param_3, int param_4) {
int iVar1;
CmdLoadSingleIop auStack96;
auStack96.header.cmd_kind = 0x102;
auStack96.header.mbx_to_reply = 0;
auStack96.header.thread_id = GetThreadId();
auStack96.file_record = param_1;
auStack96.dest_addr = (u8*)(u64)param_2;
auStack96.length = param_3;
auStack96.offset = param_4;
SendMbx(iso_mbx, &auStack96);
SleepThread();
iVar1 = 0;
if (auStack96.header.status == 0) {
iVar1 = auStack96.length_to_copy;
}
return iVar1;
}
void PauseVAGStreams() {
VagCmd* inasdf;
inasdf = GetVAGCommand();
(inasdf->header).cmd_kind = 0x403;
(inasdf->header).mbx_to_reply = 0;
(inasdf->header).thread_id = 0;
SendMbx(iso_mbx, inasdf);
}
void UnpauseVAGStreams() {
auto* inasdf = GetVAGCommand();
(inasdf->header).cmd_kind = 0x404;
(inasdf->header).mbx_to_reply = 0;
(inasdf->header).thread_id = 0;
SendMbx(iso_mbx, inasdf);
}
void SetVAGStreamPitch(int param_1, int param_2) {
auto* inasdf = GetVAGCommand();
(inasdf->header).cmd_kind = 0x406;
(inasdf->header).mbx_to_reply = 0;
(inasdf->header).thread_id = 0;
inasdf->id = param_1;
inasdf->unk_256_pitch2 = param_2;
SendMbx(iso_mbx, inasdf);
}
void SetDialogVolume(int param_1) {
auto* inasdf = GetVAGCommand();
(inasdf->header).cmd_kind = 0x407;
(inasdf->header).mbx_to_reply = 0;
(inasdf->header).thread_id = 0;
inasdf->vol_multiplier = param_1;
SendMbx(iso_mbx, inasdf);
}
void LoadSoundBank(char* param_1, SoundBank* param_2) {
CmdLoadSoundBank auStack80;
auStack80.header.cmd_kind = 0x300;
auStack80.header.mbx_to_reply = 0;
auStack80.header.thread_id = GetThreadId();
strncpy(auStack80.bank_name, param_1, 0x10);
auStack80.bank = param_2;
SendMbx(iso_mbx, &auStack80);
SleepThread();
}
void LoadMusic(char* param_1, s32* param_2) {
CmdLoadMusic auStack88;
auStack88.header.cmd_kind = 0x380;
auStack88.header.mbx_to_reply = 0;
auStack88.header.thread_id = GetThreadId();
strncpy(auStack88.name, param_1, 0x10);
auStack88.handle = param_2;
SendMbx(iso_mbx, &auStack88);
SleepThread();
for (u32 i = 0; i < gMusicTweakInfo.TweakCount; i++) {
if (!strcmp(gMusicTweakInfo.MusicTweak[i].MusicName, param_1)) {
gMusicTweak = gMusicTweakInfo.MusicTweak[i].VolumeAdjust;
return;
}
}
gMusicTweak = 0x80;
}
void UnLoadMusic(s32* param_1) {
gMusicFadeDir = -1;
if (gMusicFade != 0) {
do {
DelayThread(1000);
} while (gMusicFade != 0);
}
snd_UnloadBank(*param_1);
snd_ResolveBankXREFS();
*param_1 = 0;
}
} // namespace jak2
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "common/common_types.h"
struct SoundBank;
struct FileRecord;
namespace jak2 {
struct VagStrListNode;
void SetVAGStreamPitch(int param_1, int param_2);
void SetDialogVolume(int param_1);
void LoadSoundBank(char* param_1, SoundBank* param_2);
void UnLoadMusic(s32* param_1);
void LoadMusic(char* param_1, s32* param_2);
void QueueVAGStream(VagStrListNode* param_1);
int LoadISOFileToEE(FileRecord* param_1, uint32_t param_2, int param_3);
int LoadISOFileToIOP(FileRecord* fr, uint8_t* addr, int len);
int LoadISOFileChunkToEE(FileRecord* param_1, uint32_t param_2, int param_3, int param_4);
} // namespace jak2
+758
View File
@@ -0,0 +1,758 @@
#include "iso_cd.h"
#include <cstdio>
#include <cstring>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/common/overlord_common.h"
#include "game/overlord/common/fake_iso.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/common/sbank.h"
#include "game/overlord/jak2/iso_queue.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
namespace jak2 {
/// Page being written by CD reads
Page* ReadPagesCurrentPage = nullptr;
/// Semaphore that blocks on a CD read
s32 DvdSema = -1;
s32 BlzoSema = -1;
/// Callback function installed before using the iso_cd.cpp system
void (*PreviousCallBack)(int) = nullptr;
/// Flag to indicate that we should stop reading.
s32 ReadPagesCancelRead = 0;
/// The number of pages to read for the current read.
s32 ReadPagesNumToRead = 0;
/// The actual buffer in IOP memory to write to for the current read.
void* ReadPagesCurrentBuffer = nullptr;
/// The sector of the read in progress
s32 ReadPagesCurrentSector = 0;
/// How many sectors should be done per page (and call to sceCdRead)
s32 ReadPagesSectorsPerPage = 0;
PageList* ReadPagesPagePool = nullptr;
s32 SubBufferToRead = 0;
// -1 is set after some stuff.
// 5 or 6 needed for IsoCdPagesCallback to fire
// 5 for non-blzo read
// 6 for blzo read
// 9 is backed up by insta-reads (it happens during CD_WaitReturn)
// 8 for insta-reads itself
/// Flag that can be set to 1 once reading pages is done.
s32* ReadPagesDoneFlag = nullptr;
/// LoadStackEntry for currently reading file
static LoadStackEntry* sReadInfo;
s32 StopPluginStreams = 0;
s32 SubBuffersFlags = 0;
void* sMode = nullptr;
static LoadStackEntry sLoadStack[MAX_OPEN_FILES]; //! List of all files that are "open"
IsoFs iso_cd;
int FS_Init();
LoadStackEntry* FS_Open(FileRecord* file_record, int offset, OpenMode mode);
LoadStackEntry* FS_OpenWad(FileRecord* fr, int offset);
void FS_Close(LoadStackEntry* lse);
int FS_PageBeginRead(LoadStackEntry* lse, Buffer* buffer);
uint32_t FS_LoadSoundBank(char* name, SoundBank* buffer);
uint32_t FS_LoadMusic(char* name, s32* buffer);
u32 FS_SyncRead();
void iso_cd_init_globals() {
ReadPagesCurrentPage = nullptr;
DvdSema = -1;
BlzoSema = -1;
PreviousCallBack = nullptr;
ReadPagesCancelRead = 0;
SubBufferToRead = 0;
ReadPagesNumToRead = 0;
ReadPagesCurrentBuffer = nullptr;
ReadPagesCurrentSector = 0;
ReadPagesSectorsPerPage = 0;
ReadPagesDoneFlag = nullptr;
ReadPagesPagePool = nullptr;
SpLargeBuffer = nullptr;
SubBuffersFlags = 0;
StopPluginStreams = 0;
memset(sLoadStack, 0, sizeof(sLoadStack));
sReadInfo = nullptr;
iso_cd.init = FS_Init;
iso_cd.find = FS_Find;
iso_cd.find_in = FS_FindIN;
iso_cd.get_length = FS_GetLength;
iso_cd.open = FS_Open;
iso_cd.open_wad = FS_OpenWad;
iso_cd.close = FS_Close;
iso_cd.page_begin_read = FS_PageBeginRead;
iso_cd.load_sound_bank = FS_LoadSoundBank;
iso_cd.load_music = FS_LoadMusic;
iso_cd.sync_read = FS_SyncRead;
}
///////////////////////////
// Sony Fake CD Functions
///////////////////////////
struct FakeCd {
int offset_into_file = 0;
FILE* fp = nullptr;
void (*callback)(int) = nullptr;
FileRecord* last_fr = nullptr;
} gFakeCd;
auto sceCdCallback(void (*callback)(int)) {
auto ret = gFakeCd.callback;
gFakeCd.callback = callback;
return ret;
}
int sceCdRead(int lsn, int num_sectors, void* dest, void* mode) {
(void)mode;
// printf("sceCdRead %d, %d -> %p\n", lsn, num_sectors, dest);
ASSERT(gFakeCd.fp);
if (fseek(gFakeCd.fp, lsn * SECTOR_SIZE, SEEK_SET)) {
ASSERT_MSG(false, "Failed to fseek");
}
if (fread(dest, num_sectors * SECTOR_SIZE, 1, gFakeCd.fp) < 0) {
printf("dest is %p, num_sectors %d, lsn %d\n", dest, num_sectors, lsn);
printf("err: %s\n", strerror(errno));
ASSERT_MSG(false, "Failed to fread");
}
ASSERT(gFakeCd.callback);
return 1;
}
void do_cd_callback() {
if (gFakeCd.callback) {
gFakeCd.callback(1);
}
}
////////////////////////
// Overlord Functions
////////////////////////
/*!
* Handle a completed read. Called by Sony CD system.
*/
void IsoCdPagesCallback(int done) {
if (!ReadPagesCurrentPage) {
// somehow there is nothing left to read. I guess the read is no longer desired.
printf("-- ReadPagesCurrentPage was mysteriously null in IsoCdPagesCallback\n");
// allow stuff waiting on the dvd semaphore
SignalSema(DvdSema); // was iSignalSema
// restore old CD callback
sceCdCallback(PreviousCallBack);
} else if (done == 1) {
if (ReadPagesCancelRead == 0) {
// read is still in progress.
// ???
if ((SubBufferToRead != 5) && (SubBufferToRead != 6)) {
// not a FS_BeginPagedRead read, so don't use this callback. seems bad if this is reached.
return;
}
// ???
if (ReadPagesCurrentPage->state == PageState::ALLOCATED_EMPTY) {
ReadPagesCurrentPage->state = PageState::ALLOCATED_FILLED;
}
// advance to the next page.
ReadPagesCurrentPage = ReadPagesCurrentPage->next;
ReadPagesNumToRead = ReadPagesNumToRead + -1;
// kick off next read if we can.
if (ReadPagesCurrentPage && ReadPagesNumToRead > 0) {
ReadPagesCurrentBuffer = ReadPagesCurrentPage->buffer;
ReadPagesCurrentSector = ReadPagesCurrentSector + ReadPagesSectorsPerPage;
// read!
int cd_ret = sceCdRead(ReadPagesCurrentSector, ReadPagesSectorsPerPage,
ReadPagesCurrentBuffer, sMode);
if (cd_ret != 0) {
return;
}
// no need for CdReturnThread - just checks for removed CD.
// iWakeupThread(CdReturnThread);
return;
}
// otherwise, we cannot read. Hopefully we read everything the user requested
// added this check here
if (ReadPagesNumToRead) {
printf("---- IsoCdPagesCallback wants to keep reading, but ran out of pages!\n");
ASSERT_NOT_REACHED();
}
ReadPagesPagePool = 0;
ReadPagesCurrentPage = nullptr;
ReadPagesCurrentSector = 0;
if (ReadPagesDoneFlag == nullptr) {
ReadPagesDoneFlag = nullptr;
} else {
*ReadPagesDoneFlag = 1;
}
} else { // we were cancelled.
ReadPagesPagePool = 0;
ReadPagesCurrentPage = nullptr;
ReadPagesCurrentSector = 0;
ReadPagesDoneFlag = nullptr;
ReadPagesCancelRead = 0;
}
// read is done.
SubBufferToRead = -1;
sceCdCallback(PreviousCallBack);
SignalSema(DvdSema); // was iSignalSema.
}
}
// ReadDirectory : skipped
// DecodeDUP : skipped
// LoadMusicTweaks : can use common
// LoadDiscID : skipped
/*!
* Initialize the iso_cd filesystem.
*/
int FS_Init() {
// added: initialize fake_iso. This will allow us to call fake_iso functions in the implementation
// of jak 2 CD reading code.
// this will also take care of loading music tweaks
fake_iso_FS_Init();
// removed checks related to checking DVD/CD type.
SemaParam sema_param;
sema_param.attr = 0;
sema_param.init_count = 1;
sema_param.max_count = 1;
sema_param.option = 0;
DvdSema = CreateSema(&sema_param);
if (DvdSema < 0) {
printf("IOP: ======================================================================\n");
printf("IOP: iso_cd FS_Init: Can\'t create DVD semaphore\n");
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
// removed creation of CD Return thread.
// this only checks for the CD being removed and put back in.
sema_param.attr = 1;
sema_param.init_count = 1;
sema_param.max_count = 1;
sema_param.option = 0;
BlzoSema = CreateSema(&sema_param);
if (BlzoSema < 0) {
printf("IOP: ======================================================================\n");
printf("IOP: iso_cd FS_Init: Can\'t create BLZO semaphore\n");
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
// init the LargeBuffer.
SubBufferToRead = -1;
SubBuffersFlags = 0;
SpLargeBuffer->chunk_cnt_1 = 8;
SpLargeBuffer->chunk_cnt_2 = 8;
SpLargeBuffer->blzo_buffer_size_bytes = 0x40000;
SpLargeBuffer->current_buffer_base_ptr = nullptr;
SpLargeBuffer->page_list = SpMemoryBuffers;
SpLargeBuffer->blzo_chunk_size = 0x8000;
SpLargeBuffer->incoming_block_size = 0;
SpLargeBuffer->ptr = nullptr;
SpLargeBuffer->init0_2 = 0;
SpLargeBuffer->end_ptr = (SpLargeBuffer->current_buffer_base_ptr + 0x40000); // ?
return 0;
}
LoadStackEntry* FS_Open(FileRecord* file_record, int offset, OpenMode mode) {
// CpuSuspendIntr();
// first, find a LoadStackEntry*
LoadStackEntry* load_stack_entry = nullptr;
for (auto& entry : sLoadStack) {
if (!entry.fr) {
load_stack_entry = &entry;
load_stack_entry->fr = file_record;
break;
}
}
// CpuResumeIntr(local_28[0]);
if (!load_stack_entry) {
printf("IOP: ======================================================================\n");
printf("IOP: iso_cd FS_Open: stack full\n");
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
u32 first_four_bytes_of_file = 0;
u32 next_four_bytes_of_file = 0;
switch (mode) {
case OpenMode::MODE0:
case OpenMode::MODE1:
// we'd read 1 sector now to check the header. this would populate first_four_bytes_of_file.
// in OpenGOAL we don't use BLZO, so just put
first_four_bytes_of_file = 0;
break;
case OpenMode::KNOWN_NOT_BLZO:
// like it was in the original
first_four_bytes_of_file = 0;
break;
default:
ASSERT_NOT_REACHED();
}
if (first_four_bytes_of_file == 0x426c5a6f) { // blzo magic.
load_stack_entry->uses_blzo = 1;
if (PollSema(BlzoSema) != 0) {
load_stack_entry->size_after_decompression = 0;
file_record->size = 0;
printf("======================================================================\n");
// printf("IOP: iso_cd FS_Open: blzo file %s cant get blzo semaphore\n", file_record);
printf("======================================================================\n");
ASSERT_NOT_REACHED();
}
// remember the size
load_stack_entry->size_after_decompression = next_four_bytes_of_file;
// also modify the file record!!
file_record->size = next_four_bytes_of_file;
// allocate a list of pages to hold the data.
Page* blzo_pages =
AllocPagesBytes(SpLargeBuffer->page_list, SpLargeBuffer->blzo_buffer_size_bytes);
SpLargeBuffer->allocated_pages = blzo_pages;
auto page_size = SpLargeBuffer->page_list->page_size;
// make sure we have enough pages.
if ((u32)blzo_pages->free_pages <
((SpLargeBuffer->blzo_buffer_size_bytes + page_size) - 1) / page_size) {
printf("IOP: ======================================================================\n");
page_size = SpLargeBuffer->page_list->page_size;
printf("IOP: iso_cd FS_Open: need %d pages got %d\n",
((SpLargeBuffer->blzo_buffer_size_bytes + page_size) - 1) / page_size,
SpLargeBuffer->allocated_pages->free_pages);
printf("IOP: ======================================================================\n");
if (0 < SpLargeBuffer->allocated_pages->free_pages) {
FreePagesList(SpLargeBuffer->page_list, SpLargeBuffer->allocated_pages);
}
StopPluginStreams = 1;
SignalSema(BlzoSema);
load_stack_entry->size_after_decompression = 0;
load_stack_entry->fr = nullptr;
return nullptr;
}
auto* page_iter = SpLargeBuffer->allocated_pages;
SpLargeBuffer->current_page = blzo_pages;
SpLargeBuffer->mid_way_page = page_iter->end_page_first_only;
SpLargeBuffer->unk_page_end2 = page_iter->end_page_first_only;
// seek page-iter to the midway page.
int midway_page_idx = page_iter->free_pages / 2;
int i = 0;
if (0 < midway_page_idx) {
do {
page_iter = page_iter->next;
i = i + 1;
} while (i < midway_page_idx);
}
SpLargeBuffer->mid_way_page = page_iter;
blzo_pages = page_iter->prev;
SpLargeBuffer->first_done_flag = 0;
SpLargeBuffer->done_flag_2 = 0;
SpLargeBuffer->unk3 = 0;
SpLargeBuffer->maybe_post_first_done = 0;
SpLargeBuffer->unk4 = 0;
SpLargeBuffer->ptr = 0;
SpLargeBuffer->page_before_midway_page = blzo_pages;
SpLargeBuffer->some_buffer = nullptr;
SpLargeBuffer->current_buffer_base_ptr = SpLargeBuffer->allocated_pages->buffer;
} else {
load_stack_entry->uses_blzo = 0;
// load_stack_entry->size_after_decompression = file_record->size;
load_stack_entry->size_after_decompression = FS_GetLength(file_record);
}
load_stack_entry->read_bytes = 0;
// load_stack_entry->cd_offset = file_record->location;
load_stack_entry->cd_offset = 0;
if (offset != -1) {
load_stack_entry->cd_offset += offset;
}
return load_stack_entry;
}
int DecompressBlock(u8* /*input*/, u8* output) {
int size_out = -1;
int decomp_return_code = -1;
// Note: in here SpLargeBuffer's ptr points to the start of the new data
// and Page's ptr points to the end.
// see if we should use decompress or not.
// in some cases, compressing the data makes it bigger.
// in this case, the data on DVD contains a CHUNK_SIZE block of uncompressed data
// and the _incoming_block_size is set to the larger-than-CHUNK_SIZE size of a compressed block.
if (SpLargeBuffer->blzo_chunk_size < SpLargeBuffer->incoming_block_size) {
// the case where compressing doesn't help. Copy data from the current page to output.
FromPagesCopy(SpLargeBuffer->current_page, SpLargeBuffer->ptr, output,
SpLargeBuffer->blzo_chunk_size);
// output size is exactly max size
size_out = SpLargeBuffer->blzo_chunk_size;
// no error possible in this case.
decomp_return_code = 0;
// adjust to the actual size of data processed.
SpLargeBuffer->incoming_block_size = size_out;
} else {
// otherwise actually decompress.
// this reads incoming_block_size data, writes decompressed to output, and stores the number
// of bytes output in size_out.
ASSERT_NOT_REACHED();
// decomp_return_code = lzo1x_decompress(input, SpLargeBuffer->incoming_block_size, output,
// &size_out, 0);
}
// align to 4 bytes so our next read is still 4 byte aligned.
auto aligned_incoming_size = SpLargeBuffer->incoming_block_size + 3U & 0xfffffffc;
SpLargeBuffer->incoming_block_size = aligned_incoming_size;
// seek the pointer in SpLargeBuffer simply by adding the size for now...
// this might run on the page...
auto* end_of_incoming = SpLargeBuffer->ptr + aligned_incoming_size;
SpLargeBuffer->ptr = end_of_incoming;
// now check to see if we need to advance the page
Page* page = SpLargeBuffer->current_page;
auto* page_ptr = page->ptr;
if (page_ptr < end_of_incoming) { // did our pointer seek go past the end of this page?
// if so, seek the page!
page = page->next;
if (page) {
SpLargeBuffer->current_page = page;
// carry over... not sure what the -1 is.
SpLargeBuffer->ptr = end_of_incoming - page_ptr + page->buffer - 1;
}
}
// check to see if there is additional data read, after this block.
if (SpLargeBuffer->ptr < SpLargeBuffer->end_ptr) {
// there is!
// we can determine the next block's size!
// read it
memcpy(&SpLargeBuffer->incoming_block_size, SpLargeBuffer->ptr, 4);
end_of_incoming = SpLargeBuffer->ptr + 4;
SpLargeBuffer->ptr = end_of_incoming;
page_ptr = SpLargeBuffer->current_page->ptr;
// check for crossing page boundary by reading this...
if (page_ptr < end_of_incoming) {
page = SpLargeBuffer->current_page->next;
if (page) {
SpLargeBuffer->current_page = page;
SpLargeBuffer->ptr = end_of_incoming - page_ptr + page->buffer - 1;
}
}
// check for insane value
if (70000 < SpLargeBuffer->incoming_block_size) {
printf("IOP: ======================================================================\n");
printf("IOP: iso_cd DecompressBlock: next compressed block length too big page %d\n",
SpLargeBuffer->current_page->maybe_page_id);
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
} else {
// no data read. I guess we set incoming block size to 0, for now.
SpLargeBuffer->incoming_block_size = 0;
}
if (decomp_return_code != 0) {
ASSERT_NOT_REACHED();
}
return size_out;
}
int FS_PageBeginRead(LoadStackEntry* lse, Buffer* buffer) {
// bool bVar1;
// undefined4 uVar3;
// int iVar4;
// int *piVar5;
// int iVar6;
// Page *pPVar7;
// uint8_t *puVar8;
// int iVar9;
// uint uVar10;
// uint uVar11;
// PageList *plist;
// Page *pages;
// undefined4 local_40;
// undefined4 local_3c;
// undefined4 local_38;
// int local_34;
// undefined4 local_30 [2];
// _buffer = buffer->decomp_buffer;
int sector = lse->cd_offset;
// _sectors = 0;
sReadInfo = lse;
// _sector = uVar10;
// _real_sector = uVar10;
if (lse->uses_blzo == 1) {
ASSERT_NOT_REACHED_MSG("no blzo for u");
} else {
if ((lse->size_after_decompression != 0) &&
(lse->size_after_decompression <= (u32)lse->read_bytes)) {
// _sectors = 0;
// read past the end already.
// not entirely sure why this is "in progress"...
return CMD_STATUS_IN_PROGRESS;
}
// these pages will hold the data.
auto* plist = buffer->plist;
auto* pages = buffer->page;
// wait for drive
if (PollSema(DvdSema) == KE_SEMA_ZERO) {
WaitSema(DvdSema);
}
// wait for pending read to finish, I guess...
if (-1 < SubBufferToRead) {
while (-1 < SubBufferToRead) {
DelayThread(100);
}
}
// set SubBufferToRead to 5, to indicate a non-blzo FS read.
// iVar4 = CpuSuspendIntr(local_30);
SubBufferToRead = 5;
/*
if (iVar4 != -0x66) {
CpuResumeIntr(local_30[0]);
}
*/
int page_size = plist->page_size;
if (page_size < 0) { // usual page size mystery.
page_size = page_size + 0x7ff;
}
// init globals for the callback
ReadPagesCurrentBuffer = (void*)pages->buffer;
ReadPagesSectorsPerPage = page_size >> 0xb;
ReadPagesCancelRead = 0;
ReadPagesPagePool = plist;
ReadPagesCurrentPage = pages;
ReadPagesCurrentSector = sector;
int local_34 = 0; // super sketchy...
ReadPagesDoneFlag = &local_34;
int npages = pages->free_pages;
ReadPagesNumToRead = npages;
// mark all as allocated and empty.
pages->state = PageState::ALLOCATED_EMPTY;
auto* next = pages->next;
while ((next && (npages = npages + -1, 0 < npages))) {
pages = pages->next;
next = pages->next;
pages->state = PageState::ALLOCATED_EMPTY;
}
// start a read!
if (gFakeCd.last_fr != lse->fr) {
const char* path = get_file_path(lse->fr);
FILE* fp = file_util::open_file(path, "rb");
if (!fp) {
lg::error("[OVERLORD] fake iso could not open the file \"{}\"", path);
} else {
// printf("PAGE READING %s\n", path);
}
ASSERT(fp);
if (gFakeCd.fp) {
fclose(gFakeCd.fp);
}
gFakeCd.fp = fp;
gFakeCd.last_fr = lse->fr;
}
while (true) {
PreviousCallBack = nullptr;
PreviousCallBack = sceCdCallback(IsoCdPagesCallback);
int ret =
sceCdRead(ReadPagesCurrentSector, ReadPagesSectorsPerPage, ReadPagesCurrentBuffer, sMode);
if (ret)
break;
sceCdCallback(PreviousCallBack);
// FS_PollDrive();
}
// wait for read to finish
while (-1 < SubBufferToRead) {
// DelayThread(1000);
do_cd_callback(); // added, to make progress. TODO remove sleep avoe.
}
// update stats.
int bytes_read = buffer->page->free_pages * buffer->plist->page_size;
lse->read_bytes = lse->read_bytes + bytes_read;
buffer->decompressed_size = bytes_read;
lse->cd_offset += bytes_read >> 0xb;
}
// not sure why we use "in progress" here, maybe the meaning changed.
return CMD_STATUS_IN_PROGRESS;
}
uint32_t FS_LoadSoundBank(char* name, SoundBank* buffer) {
SoundBank* bank = (SoundBank*)buffer;
FileRecord* file = nullptr;
char namebuf[16];
char isoname[16];
u32 handle;
strncpy(namebuf, name, 12);
namebuf[8] = 0;
strcat(namebuf, ".sbk");
MakeISOName(isoname, namebuf);
file = FS_FindIN(isoname);
if (!file) {
return CMD_STATUS_FAILED_TO_OPEN;
}
handle = snd_BankLoadEx(get_file_path(file), 0, bank->spu_loc, bank->spu_size);
snd_ResolveBankXREFS();
bank->bank_handle = handle;
return CMD_STATUS_DONE;
}
uint32_t FS_LoadMusic(char* name, s32* bank_handle) {
FileRecord* file = nullptr;
char namebuf[16];
char isoname[16];
u32 handle;
strncpy(namebuf, name, 12);
namebuf[8] = 0;
strcat(namebuf, ".mus");
MakeISOName(isoname, namebuf);
file = FS_FindIN(isoname);
if (!file) {
return CMD_STATUS_FAILED_TO_OPEN;
}
handle = snd_BankLoadEx(get_file_path(file), 0, 0xcfcc0, 0x61a80);
snd_ResolveBankXREFS();
*bank_handle = handle;
return CMD_STATUS_DONE;
}
// CD_WaitReturn: don't need it
// FS_Find: common
// FS_FindIN: common
// FS_GetLength: common
LoadStackEntry* FS_OpenWad(FileRecord* fr, int offset) {
// CpuSuspendIntr();
LoadStackEntry* selected = nullptr;
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
selected = sLoadStack + i;
selected->fr = fr;
break;
}
}
// CpuResumeIntr(local_20[0]);
if (!selected) {
printf("======================================================================\n");
printf("IOP: iso_cd FS_OpenWad: stack full\n");
printf("======================================================================\n");
ASSERT_NOT_REACHED();
}
selected->uses_blzo = 0;
selected->size_after_decompression = 0;
selected->read_bytes = 0;
// selected->cd_offset = fr->location + offset;
selected->cd_offset = offset;
return selected;
}
void FS_Close(LoadStackEntry* lse) {
if (lse->uses_blzo == 1) {
SpLargeBuffer->ptr = nullptr;
SpLargeBuffer->current_buffer_base_ptr = nullptr;
SpLargeBuffer->allocated_pages = FreePagesList(SpMemoryBuffers, SpLargeBuffer->allocated_pages);
SignalSema(BlzoSema);
if (StopPluginStreams == 1) {
StopPluginStreams = 0;
}
}
if (lse == sReadInfo) {
sReadInfo = (LoadStackEntry*)0x0;
ReadPagesCancelRead = 1;
}
lse->fr = nullptr;
}
u32 FS_SyncRead() {
if (sReadInfo) {
sReadInfo = nullptr;
return CMD_STATUS_IN_PROGRESS;
} else {
return CMD_STATUS_READ_ERR;
}
}
// FS_StoreSoundBankInIOP: stub
// FS_LoadSoundBankFromIOP: stub
// FS_LoadSoundBankFromEE: stub
// FS_PollDrive: not needed.
// CdReturn: not needed thread
// DoCdReadPages: inlined
// CheckPagesReady: inline/not used
} // namespace jak2
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include "common/common_types.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/jak2/iso.h"
#include "game/overlord/jak2/pages.h"
namespace jak2 {
void iso_cd_init_globals();
extern IsoFs iso_cd;
extern s32 StopPluginStreams;
} // namespace jak2
+698
View File
@@ -0,0 +1,698 @@
#include "iso_queue.h"
#include <cstring>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "game/overlord/common/iso.h"
#include "game/overlord/jak2/dma.h"
#include "game/overlord/jak2/iso.h"
#include "game/overlord/jak2/spustreams.h"
#include "game/overlord/jak2/vag.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
namespace jak2 {
/// The data structure containing all memory pages.
PageList* SpMemoryBuffers = nullptr;
u8* ScratchPadMemory = nullptr;
constexpr int N_BUFFERS = 3 + 11; // ??
static Buffer sBuffer[N_BUFFERS];
static Buffer* sFreeBuffer = nullptr;
static Buffer* sFreeStrBuffer = nullptr;
u32 BuffersAlloc = 0;
u32 StrBuffersAlloc = 0;
u32 AllocdBuffersCount = 0;
u32 NextBuffer = 0;
u32 AllocdStrBuffersCount = 0;
u32 NextStrBuffer = 0;
int sSema = 0;
int vag_cmd_cnt = 0;
VagCmd vag_cmds[16];
u32 vag_cmd_used = 0;
u32 max_vag_cmd_cnt = 0;
PriStackEntry gPriStack[N_PRIORITIES];
std::string gPriEntryNames[N_PRIORITIES][PRI_STACK_LENGTH]; // my addition for debug
void ReturnMessage(CmdHeader* param_1);
void FreeVAGCommand(VagCmd* param_1);
void iso_queue_init_globals() {
memset(sBuffer, 0, sizeof(sBuffer));
memset(gPriStack, 0, sizeof(gPriStack));
memset(vag_cmds, 0, sizeof(vag_cmds));
ScratchPadMemory = nullptr;
SpMemoryBuffers = nullptr;
sFreeBuffer = nullptr;
sFreeStrBuffer = nullptr;
BuffersAlloc = 0;
StrBuffersAlloc = 0;
AllocdBuffersCount = 0;
NextBuffer = 0;
sSema = 0;
vag_cmd_cnt = 0;
vag_cmd_used = 0;
max_vag_cmd_cnt = 0;
}
void InitBuffers() {
SpMemoryBuffers = (PageList*)ScratchPadMemory;
ScratchPadMemory += sizeof(PageList);
InitPagedMemory(SpMemoryBuffers, 0x12, 0x8000);
Buffer* next_buffer = sBuffer;
for (int i = 0; i < 3; i++) {
next_buffer++;
sBuffer[i].next = next_buffer;
sBuffer[i].decomp_buffer = nullptr;
sBuffer[i].decompressed_size = 0;
sBuffer[i].unk_12 = 0;
sBuffer[i].data_buffer_idx = -1;
sBuffer[i].use_mode = 1;
sBuffer[i].plist = SpMemoryBuffers;
sBuffer[i].num_pages = 1;
sBuffer[i].unk_32 = 0;
sBuffer[i].free_pages = 0;
sBuffer[i].page = nullptr;
sBuffer[i].unk_44 = 0;
};
sBuffer[2].next = nullptr;
next_buffer = sBuffer + 4;
sFreeBuffer = sBuffer;
BuffersAlloc = 0;
for (int i = 0; i < 8; i++) {
sBuffer[i + 3].next = next_buffer;
next_buffer++;
sBuffer[i + 3].decomp_buffer = nullptr;
sBuffer[i + 3].decompressed_size = 0;
sBuffer[i + 3].unk_12 = 0;
sBuffer[i + 3].data_buffer_idx = -1;
sBuffer[i + 3].use_mode = 2;
sBuffer[i + 3].plist = SpMemoryBuffers;
sBuffer[i + 3].num_pages = 1;
sBuffer[i + 3].unk_32 = 2;
sBuffer[i + 3].free_pages = 0;
sBuffer[i + 3].page = nullptr;
sBuffer[i + 3].unk_44 = 0;
};
sBuffer[10].next = nullptr;
sFreeStrBuffer = sBuffer + 3;
StreamSRAM[0] = 0x5040;
StrBuffersAlloc = 0;
TrapSRAM[0] = 0x9040;
snd_SRAMMarkUsed(0x5040, 0x4040);
StreamSRAM[1] = 0x9080;
TrapSRAM[1] = 0xd080;
snd_SRAMMarkUsed(0x9080, 0x4040);
StreamSRAM[2] = 0xd0c0;
TrapSRAM[2] = 0x110c0;
snd_SRAMMarkUsed(0xd0c0, 0x4040);
StreamSRAM[3] = 0x11100;
TrapSRAM[3] = 0x15100;
snd_SRAMMarkUsed(0x11100, 0x4040);
for (int i = 0; i < 4; i++) {
if (DMA_SendToSPUAndSync(VAG_SilentLoop, 0x30, TrapSRAM[i], 0, 1)) {
ASSERT_NOT_REACHED();
break;
}
DelayThread(1000);
}
SemaParam param;
param.attr = 1;
param.init_count = 1;
param.max_count = 1;
param.option = 0;
sSema = CreateSema(&param);
if (sSema < 0) {
printf("IOP: ======================================================================\n");
printf("IOP: iso_queue InitBuffers: Can\'t create semaphore\n");
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
}
u32 AllocDataBuffer(u32* param_1, u32 param_2) {
bool bVar1;
u32 uVar2;
u32 uVar3;
int iVar4;
u32 uVar5;
uVar5 = 0xffffffff;
bVar1 = false;
uVar3 = uVar5;
if (param_1 == &BuffersAlloc) {
if (AllocdBuffersCount < param_2) {
if ((BuffersAlloc & 1 << (NextBuffer & 0x1f)) == 0) {
bVar1 = true;
} else {
iVar4 = 0;
if (0 < (int)param_2) {
do {
NextBuffer = NextBuffer + 1;
if (param_2 <= NextBuffer) {
NextBuffer = 0;
}
iVar4 = iVar4 + 1;
if ((BuffersAlloc & 1 << (NextBuffer & 0x1f)) == 0) {
bVar1 = true;
iVar4 = param_2 + 1;
}
} while (iVar4 < (int)param_2);
}
}
uVar5 = NextBuffer;
if (bVar1) {
BuffersAlloc = BuffersAlloc | 1 << (NextBuffer & 0x1f);
AllocdBuffersCount = AllocdBuffersCount + 1;
NextBuffer = NextBuffer + 1;
uVar3 = uVar5;
if (param_2 <= NextBuffer) {
NextBuffer = 0;
}
}
}
} else {
uVar3 = 0xffffffff;
if ((param_1 == &StrBuffersAlloc) && (uVar3 = uVar5, AllocdStrBuffersCount < param_2)) {
if ((StrBuffersAlloc & 1 << (NextStrBuffer & 0x1f)) == 0) {
bVar1 = true;
} else {
iVar4 = 0;
if (0 < (int)param_2) {
do {
NextStrBuffer = NextStrBuffer + 1;
if (param_2 <= NextStrBuffer) {
NextStrBuffer = 0;
}
iVar4 = iVar4 + 1;
if ((StrBuffersAlloc & 1 << (NextStrBuffer & 0x1f)) == 0) {
bVar1 = true;
iVar4 = param_2 + 1;
}
} while (iVar4 < (int)param_2);
}
}
uVar2 = NextStrBuffer;
if (bVar1) {
StrBuffersAlloc = StrBuffersAlloc | 1 << (NextStrBuffer & 0x1f);
AllocdStrBuffersCount = AllocdStrBuffersCount + 1;
NextStrBuffer = NextStrBuffer + 1;
uVar3 = uVar2;
if (param_2 <= NextStrBuffer) {
NextStrBuffer = 0;
}
}
}
}
return uVar3;
}
Buffer* AllocateBuffer(int param_1, VagCmd* param_2, int /*param_3*/) {
PageList** ppPVar1;
int* piVar2;
int iVar3;
int iVar4;
int iVar5;
Buffer* pBVar6;
Buffer* pBVar7;
Page* pPVar8;
// if (param_3 == 1) {
// CpuSuspendIntr(local_28);
//}
pBVar6 = (Buffer*)0x0;
pPVar8 = (Page*)0x0;
if (param_1 == 1) {
if ((sFreeBuffer != (Buffer*)0x0) &&
(iVar3 = AllocDataBuffer(&BuffersAlloc, 3), pBVar7 = sFreeBuffer, -1 < iVar3)) {
ppPVar1 = &sFreeBuffer->plist;
piVar2 = &sFreeBuffer->num_pages;
sFreeBuffer->data_buffer_idx = iVar3;
sFreeBuffer->use_mode = 1;
sFreeBuffer = sFreeBuffer->next;
pPVar8 = (Page*)AllocPages(*ppPVar1, *piVar2);
if (pPVar8 != (Page*)0x0) {
pBVar7->page = pPVar8;
pBVar7->free_pages = pPVar8->free_pages;
pBVar7->decomp_buffer = (uint8_t*)pPVar8->buffer;
}
goto LAB_00006a0c;
}
LAB_00006a28:
pBVar7 = pBVar6;
if (pPVar8 != (Page*)0x0)
goto LAB_00006a44;
} else {
if (((param_1 != 2) || (sFreeStrBuffer == (Buffer*)0x0)) ||
(iVar3 = AllocDataBuffer(&StrBuffersAlloc, 8), pBVar7 = sFreeStrBuffer, iVar3 < 0))
goto LAB_00006a28;
sFreeStrBuffer->data_buffer_idx = iVar3;
sFreeStrBuffer->use_mode = 2;
pBVar6 = (param_2->header).callback_buffer;
pPVar8 = (Page*)0x0;
if (param_2->xfer_size == 0) {
iVar5 = sFreeStrBuffer->num_pages;
} else {
iVar4 = sFreeStrBuffer->plist->page_size;
iVar3 = param_2->xfer_size + iVar4 + -1;
if (iVar4 == 0) {
ASSERT_NOT_REACHED();
// trap(0x1c00);
}
if (pBVar6 == (Buffer*)0x0) {
iVar5 = 0;
} else {
iVar5 = -pBVar6->page->free_pages;
}
iVar5 = iVar3 / iVar4 + iVar5;
if (iVar5 < 0) {
iVar5 = 0;
}
}
pBVar6 = sFreeStrBuffer->next;
if (iVar5 != 0) {
if ((param_2->status_bytes[BYTE10] == '\0') || (param_2->unk_232 == '\0')) {
if (param_2->status_bytes[BYTE4] == '\0') {
iVar3 = sFreeStrBuffer->num_pages;
} else {
iVar3 = sFreeStrBuffer->unk_32;
}
if (iVar5 < iVar3) {
iVar3 = iVar5;
}
} else {
iVar3 = iVar5;
if (3 < iVar5) {
iVar3 = 3;
}
}
ppPVar1 = &sFreeStrBuffer->plist;
sFreeStrBuffer = sFreeStrBuffer->next;
pPVar8 = (Page*)AllocPages(*ppPVar1, iVar3);
pBVar6 = sFreeStrBuffer;
}
sFreeStrBuffer = pBVar6;
if (pPVar8 != (Page*)0x0) {
pBVar7->page = pPVar8;
pBVar7->free_pages = pPVar8->free_pages;
pBVar7->decomp_buffer = (uint8_t*)pPVar8->buffer;
}
LAB_00006a0c:
if (pPVar8 != (Page*)0x0) {
pBVar7->decompressed_size = 0;
pBVar7->next = (Buffer*)0x0;
pBVar7->unk_12 = pPVar8->buffer;
pBVar6 = pBVar7;
goto LAB_00006a28;
}
}
if (pBVar7) {
FreeBuffer(pBVar7, 0);
pBVar7 = (Buffer*)0x0;
}
LAB_00006a44:
// if (param_3 == 1) {
// CpuResumeIntr(local_28[0]);
//}
return pBVar7;
}
void FreeBuffer(Buffer* param_1, int /*param_2*/) {
Buffer* pBVar1;
Page* pPVar2;
int iVar3;
u32 uVar4;
// if (param_2 == 1) {
// CpuSuspendIntr(local_18);
//}
iVar3 = param_1->use_mode;
if (iVar3 == 1) {
if ((BuffersAlloc & 1 << (param_1->data_buffer_idx & 0x1fU)) != 0) {
pPVar2 = FreePagesList(param_1->plist, param_1->page);
pBVar1 = sFreeBuffer;
uVar4 = param_1->data_buffer_idx;
param_1->page = pPVar2;
param_1->data_buffer_idx = -1;
param_1->decompressed_size = 0;
param_1->unk_12 = 0;
sFreeBuffer = param_1;
param_1->use_mode = 0;
param_1->next = pBVar1;
BuffersAlloc = BuffersAlloc & ~(1 << (uVar4 & 0x1f));
AllocdBuffersCount = AllocdBuffersCount + -1;
}
} else if (((1 < iVar3) && (iVar3 == 2)) &&
((StrBuffersAlloc & 1 << (param_1->data_buffer_idx & 0x1fU)) != 0)) {
pPVar2 = FreePagesList(param_1->plist, param_1->page);
param_1->page = pPVar2;
param_1->decompressed_size = 0;
param_1->unk_12 = 0;
pBVar1 = param_1;
param_1->next = sFreeStrBuffer;
sFreeStrBuffer = pBVar1;
StrBuffersAlloc = StrBuffersAlloc & ~(1 << (param_1->data_buffer_idx & 0x1fU));
AllocdStrBuffersCount = AllocdStrBuffersCount + -1;
param_1->data_buffer_idx = -1;
param_1->use_mode = 0;
}
// if (param_2 == 1) {
// CpuResumeIntr(local_18[0]);
//}
}
void ReleaseMessage(CmdHeader* param_1, int param_2) {
Buffer* pBVar1;
int iVar2;
PriStackEntry* pPVar3;
int iVar4;
int iVar5;
PriStackEntry* pPVar6;
pBVar1 = param_1->callback_buffer;
while (pBVar1 != (Buffer*)0x0) {
pBVar1 = param_1->callback_buffer;
param_1->callback_buffer = pBVar1->next;
FreeBuffer(pBVar1, param_2);
pBVar1 = param_1->callback_buffer;
}
if (param_1->lse != (LoadStackEntry*)0x0) {
// (*(code*)isofs->close)();
isofs->close(param_1->lse);
}
// if (param_2 == 1) {
// CpuSuspendIntr(local_18);
//}
iVar5 = 0;
pPVar6 = gPriStack;
LAB_00006d0c:
iVar4 = 0;
pPVar3 = pPVar6;
if (pPVar6->count < 1)
goto LAB_00006da0;
do {
if (pPVar3->entries[0] == param_1)
break;
iVar4 = iVar4 + 1;
pPVar3 = (PriStackEntry*)(pPVar3->entries + 1);
} while (iVar4 < pPVar6->count);
iVar2 = pPVar6->count + -1;
if (pPVar6->count <= iVar4)
goto LAB_00006da0;
pPVar6->count = iVar2;
if (iVar4 < iVar2) {
do {
iVar5 = iVar4 + 1;
pPVar6->entries[iVar4] = pPVar6->entries[iVar4 + 1];
iVar4 = iVar5;
} while (iVar5 < pPVar6->count);
}
if (param_2 != 1) {
return;
}
goto LAB_00006dbc;
LAB_00006da0:
iVar5 = iVar5 + 1;
pPVar6 = pPVar6 + 1;
if (3 < iVar5) {
if (param_2 == 1) {
LAB_00006dbc:;
// CpuResumeIntr(local_18[0]);
}
return;
}
goto LAB_00006d0c;
}
void DisplayQueue() {
for (int pri = 0; pri < N_PRIORITIES; pri++) {
for (int cmd = 0; cmd < (int)gPriStack[pri].count; cmd++) {
lg::debug(" PRI {} elt {} {} @ #x{:X}", pri, cmd, gPriEntryNames[pri][cmd],
(u64)gPriStack[pri].entries[cmd]);
}
}
}
int QueueMessage(CmdHeader* param_1, int param_2, const char* param_3, int param_4) {
int uVar1;
// undefined4 local_20[2];
if (param_4 == 1) {
// CpuSuspendIntr(local_20);
}
if (gPriStack[param_2].count == 8) {
param_1->status = 2;
// CpuResumeIntr(local_20[0]);
ReturnMessage(param_1);
uVar1 = 0;
} else {
gPriStack[param_2].entries[gPriStack[param_2].count] = param_1;
gPriEntryNames[param_2][gPriStack[param_2].count] = param_3;
gPriStack[param_2].count = gPriStack[param_2].count + 1;
if (param_4 == 1) {
// CpuResumeIntr(local_20[0]);
}
uVar1 = 1;
}
return uVar1;
}
void UnqueueMessage(CmdHeader* param_1, int param_2) {
int iVar1;
PriStackEntry* pPVar2;
int iVar3;
PriStackEntry* pPVar4;
int iVar5;
if (param_2 == 1) {
// CpuSuspendIntr(local_18);
}
iVar5 = 0;
pPVar4 = gPriStack;
LAB_00007088:
iVar3 = 0;
pPVar2 = pPVar4;
if (pPVar4->count < 1)
goto LAB_0000711c;
do {
if (pPVar2->entries[0] == param_1)
break;
iVar3 = iVar3 + 1;
pPVar2 = (PriStackEntry*)(pPVar2->entries + 1);
} while (iVar3 < pPVar4->count);
iVar1 = pPVar4->count + -1;
if (pPVar4->count <= iVar3)
goto LAB_0000711c;
pPVar4->count = iVar1;
if (iVar3 < iVar1) {
do {
iVar5 = iVar3 + 1;
pPVar4->entries[iVar3] = pPVar4->entries[iVar3 + 1];
iVar3 = iVar5;
} while (iVar5 < pPVar4->count);
}
if (param_2 != 1) {
return;
}
goto LAB_00007138;
LAB_0000711c:
iVar5 = iVar5 + 1;
pPVar4 = pPVar4 + 1;
if (3 < iVar5) {
if (param_2 == 1) {
LAB_00007138:;
// CpuResumeIntr(local_18[0]);
}
return;
}
goto LAB_00007088;
}
CmdHeader* GetMessage() {
CmdHeader* pCVar1;
int iVar2;
CmdHeader** ppCVar3;
PriStackEntry* iVar4;
int iVar5;
iVar5 = 3;
iVar4 = gPriStack + 3;
do {
iVar2 = iVar4->count + -1;
if (-1 < iVar2) {
ppCVar3 = iVar4->entries + iVar4->count + -1;
do {
pCVar1 = *ppCVar3;
if ((((pCVar1->lse != (LoadStackEntry*)0x0) && (pCVar1->status == -1)) &&
(pCVar1->unk_24 != 0)) &&
((pCVar1->callback_buffer == (Buffer*)0x0 ||
(pCVar1->callback_buffer->next == (Buffer*)0x0)))) {
return pCVar1;
}
iVar2 = iVar2 + -1;
ppCVar3 = ppCVar3 + -1;
} while (-1 < iVar2);
}
iVar5 = iVar5 + -1;
iVar4 = iVar4 + -1;
if (iVar5 < 0) {
return (CmdHeader*)0x0;
}
} while (true);
}
void ProcessMessageData() {
int iVar1;
CmdHeader* pCVar2;
Buffer* pBVar3;
int iVar4;
CmdHeader** ppCVar5;
PriStackEntry* iVar6;
int iVar7;
iVar7 = 2;
iVar6 = gPriStack + 2;
do {
iVar4 = iVar6->count + -1;
if (-1 < iVar4) {
ppCVar5 = iVar6->entries + iVar6->count + -1;
do {
pCVar2 = *ppCVar5;
if ((pCVar2 != (CmdHeader*)0x0) && (pCVar2->unk_24 != 0)) {
iVar1 = pCVar2->status;
if (iVar1 == -1) {
pBVar3 = pCVar2->callback_buffer;
if (pBVar3 != (Buffer*)0x0) {
if (pCVar2->callback != ProcessVAGData) {
iVar1 = (pCVar2->callback)(pCVar2, pBVar3);
pCVar2->status = iVar1;
if (pBVar3->decompressed_size == 0) {
pCVar2->callback_buffer = pBVar3->next;
FreeBuffer(pBVar3, 1);
}
}
iVar1 = pCVar2->status;
}
if (iVar1 == -1)
goto LAB_00007308;
}
ReleaseMessage(pCVar2, 1);
ReturnMessage(pCVar2);
iVar6 = iVar6 + 1;
iVar7 = iVar7 + 1;
break;
}
LAB_00007308:
iVar4 = iVar4 + -1;
ppCVar5 = ppCVar5 + -1;
} while (-1 < iVar4);
}
iVar7 = iVar7 + -1;
iVar6 = iVar6 + -1;
if (iVar7 < 0) {
return;
}
} while (true);
}
void ReturnMessage(CmdHeader* param_1) {
if (param_1->mbx_to_reply == 0) {
if (param_1->thread_id == 0) {
FreeVAGCommand((VagCmd*)param_1);
} else {
WakeupThread(param_1->thread_id);
}
} else {
SendMbx(param_1->mbx_to_reply, param_1);
}
}
VagCmd* GetVAGCommand() {
int iVar1;
u32 uVar2;
VagCmd* pRVar3;
do {
while (vag_cmd_cnt == 0x1f) {
DelayThread(100);
}
do {
iVar1 = WaitSema(sSema);
uVar2 = 0;
pRVar3 = vag_cmds;
} while (iVar1 != 0);
do {
if (((int)vag_cmd_used >> (uVar2 & 0x1f) & 1U) == 0) {
vag_cmd_used = vag_cmd_used | 1 << (uVar2 & 0x1f);
vag_cmd_cnt = vag_cmd_cnt + 1;
if ((int)max_vag_cmd_cnt < vag_cmd_cnt) {
max_vag_cmd_cnt = vag_cmd_cnt;
}
SignalSema(sSema);
return pRVar3;
}
uVar2 = uVar2 + 1;
pRVar3 = pRVar3 + 1;
} while ((int)uVar2 < 0x1f);
SignalSema(sSema);
} while (true);
}
void FreeVAGCommand(VagCmd* param_1) {
int iVar1;
u32 uVar2;
// uVar2 = (param_1 + -0x17e50) * 0x781948b1 >> 2;
// kinda sus
uVar2 = (param_1 - vag_cmds) / sizeof(VagCmd);
if ((uVar2 < 0x1f) && (((int)vag_cmd_used >> (uVar2 & 0x1f) & 1U) != 0)) {
do {
iVar1 = WaitSema(sSema);
} while (iVar1 != 0);
vag_cmd_used = vag_cmd_used & ~(1 << (uVar2 & 0x1f));
vag_cmd_cnt = vag_cmd_cnt + -1;
SignalSema(sSema);
}
}
uint8_t* CheckForIsoPageBoundaryCrossing(Buffer* param_1) {
Page* new_page;
uint8_t* iVar1;
uint8_t* our_ptr;
uint8_t* page_end;
our_ptr = param_1->decomp_buffer;
page_end = (uint8_t*)param_1->page->ptr;
if (page_end <= our_ptr) {
new_page = StepTopPage(param_1->plist, param_1->page);
param_1->page = new_page;
if (new_page != (Page*)0x0) {
iVar1 = new_page->buffer;
param_1->unk_12 = iVar1;
param_1->decomp_buffer = page_end + (iVar1 - (our_ptr + -1));
}
}
return param_1->decomp_buffer;
}
void FreeDataBuffer(u32* param_1, u32 param_2) {
if (param_1 == &BuffersAlloc) {
BuffersAlloc = BuffersAlloc & ~(1 << (param_2 & 0x1f));
AllocdBuffersCount = AllocdBuffersCount + -1;
} else if (param_1 == &StrBuffersAlloc) {
AllocdStrBuffersCount = AllocdStrBuffersCount + -1;
StrBuffersAlloc = StrBuffersAlloc & ~(1 << (param_2 & 0x1f));
}
}
} // namespace jak2
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <string>
#include "common/common_types.h"
#include "game/overlord/jak2/iso.h"
#include "game/overlord/jak2/pages.h"
namespace jak2 {
extern uint8_t* ScratchPadMemory;
void iso_queue_init_globals();
extern PageList* SpMemoryBuffers;
void ReleaseMessage(CmdHeader* param_1, int param_2);
int QueueMessage(CmdHeader* param_1, int param_2, const char* param_3, int param_4);
void DisplayQueue();
uint8_t* CheckForIsoPageBoundaryCrossing(Buffer* param_1);
void InitBuffers();
void FreeBuffer(Buffer* param_1, int param_2);
Buffer* AllocateBuffer(int param_1, VagCmd* param_2, int param_3);
void UnqueueMessage(CmdHeader* param_1, int param_2);
void ReturnMessage(CmdHeader* param_1);
CmdHeader* GetMessage();
VagCmd* GetVAGCommand();
constexpr int N_PRIORITIES = 4; // number of queued commands per priority
constexpr int PRI_STACK_LENGTH = 8; // number of queued commands per priority
struct PriStackEntry {
CmdHeader* entries[PRI_STACK_LENGTH];
int count;
};
extern std::string gPriEntryNames[N_PRIORITIES][PRI_STACK_LENGTH]; // my addition for debug
extern PriStackEntry gPriStack[N_PRIORITIES];
} // namespace jak2
+127
View File
@@ -0,0 +1,127 @@
#include "list.h"
#include <cstdio>
#include "common/util/Assert.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak2 {
bool InitList(List* head, u32 elt_count, int elt_size) {
ListNode* buf_ptr;
int iVar1;
ListNode** ppLVar2;
u32 elt_idx;
SemaParam local_20;
head->elt_count = elt_count;
buf_ptr = (ListNode*)AllocSysMemory(0, elt_count * elt_size, 0);
head->buffer = (u8*)buf_ptr;
if (!buf_ptr) {
printf("IOP: ======================================================================\n");
printf("IOP: list InitList: no memory for list %s\n", head->name);
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
elt_idx = 0;
head->next = buf_ptr;
// suspicious pointer math ahead.
if (elt_count != 0) {
ppLVar2 = &buf_ptr->prev;
do {
ppLVar2[1] = (ListNode*)0x0;
if (elt_idx < elt_count - 1) {
buf_ptr->next = (ListNode*)((u8*)&buf_ptr->next + elt_size);
} else {
buf_ptr->next = (ListNode*)0x0;
}
if (elt_idx == 0) {
*ppLVar2 = (ListNode*)0x0;
} else {
*ppLVar2 = (ListNode*)((u8*)buf_ptr - elt_size);
}
ppLVar2 = (ListNode**)((u8*)ppLVar2 + elt_size);
elt_idx = elt_idx + 1;
buf_ptr = (ListNode*)((u8*)&buf_ptr->next + elt_size);
} while (elt_idx < elt_count);
}
head->maybe_any_in_use = 0;
head->unk2_init0 = 0;
local_20.attr = 1;
local_20.init_count = 1;
local_20.max_count = 1;
local_20.option = 0;
iVar1 = CreateSema(&local_20);
head->sema = iVar1;
if (iVar1 < 0) {
printf("IOP: ======================================================================\n");
printf("IOP: list InitList: can\'t create semaphore for list %s\n", head->name);
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
return head->buffer != (void*)0x0;
}
ListNode* AddToCircularList(List* param_1) {
int iVar1;
ListNode* pLVar2;
iVar1 = param_1->elt_count;
pLVar2 = param_1->next;
while (iVar1 != 0) {
iVar1 = iVar1 + -1;
if (pLVar2->in_use != 1)
goto LAB_00011c14;
pLVar2 = pLVar2->next;
}
if (pLVar2->in_use == 1) {
pLVar2 = param_1->next->next;
param_1->next = pLVar2;
pLVar2 = pLVar2->prev;
}
LAB_00011c14:
param_1->maybe_any_in_use = 1;
pLVar2->in_use = 1;
return pLVar2;
}
void MakeCircularList(List* lst) {
ListNode* pLVar1;
ListNode* pLVar2;
int iVar3;
ListNode* pLVar4;
pLVar4 = lst->next;
iVar3 = lst->elt_count;
pLVar1 = pLVar4->next;
pLVar2 = pLVar4;
while (true) {
if (pLVar1 == (ListNode*)0x0) {
if (iVar3 != 0) {
pLVar2->next = pLVar4;
pLVar4->prev = pLVar2;
}
return;
}
if (iVar3 == 0)
break;
pLVar2 = pLVar2->next;
pLVar1 = pLVar2->next;
iVar3 = iVar3 + -1;
}
}
void BreakCircularList(List* param_1) {
ListNode* pLVar1;
ListNode* pLVar2;
pLVar2 = param_1->next;
pLVar1 = pLVar2->prev;
if (pLVar1) {
pLVar1->next = nullptr;
pLVar2->prev = nullptr;
}
}
} // namespace jak2
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "common/common_types.h"
namespace jak2 {
struct ListNode;
struct List {
char name[8];
int sema; // 12
int maybe_any_in_use; // 16
int elt_count; // 20
int unk2_init0; // 24
ListNode* next;
u8* buffer;
};
struct ListNode {
ListNode* next;
ListNode* prev;
int in_use;
};
bool InitList(List* head, u32 elt_count, int elt_size);
} // namespace jak2
+348
View File
@@ -0,0 +1,348 @@
### overlord.c
start
done, removed ramdisk stuff.
ExitIOP
done (just loops forever)
### iso_cd.c
was ported. calling cd callbacks is a little sus.
## ramdisk.c
ramdisk is believed unused, we will try without it for now
InitRamdisk
Thread_Server
RPC_Ramdisk
gcc2_compiled.
__gnu_compiled_c
gNumFiles
gMemUsed
gMemSize
gRPCBuf
gMem
gReturnBuffer
### fakeiso.c
We make this up. Snd/Music loading is a tiny bit different but Ziemas has patched this.
Should move these to common once we figure out how to deal with FileRecord changes.
### deviso.c
Empty file
### isocommon.c
All can be common. Additionally, some of these are implemented in FileUtils already, and are used in the decompiler.
ISONameFromAnimationName
technically is game specific, but we've already patched it
MakeISOName
UnmakeISOName
### isodesc.c
Empty file!
### dma.c
Changed a bit for jak 2. there's now DmaVagCmd, which is used for dmaing stuff to the SPU. TODO
The EE stuff has also changed and has a semaphore. However, we will ignore this and use the same instant dma as jak 1.
Downstream stuff that looks at the semaphore will need to be patched.
SpuDmaIntr
TODO
DMA_SendToEE
moved to common, instant dma, removed semaphore stuff
DMA_SendToSPUAndSync
TODO. different
DmaCancelThisVagCmd
TODO. different
EeDmaIntr
Not ported yet, just calls iSignalSema on the ee dma semaphore. Don't think we need this.
### iso.c
Note that jak 2 drops the chained buffer system from jak 1 and uses the "pages" system instead.
InitISOFS
IsoQueueVagStream
IsoPlayVagStream
ISOThread
RunDGOStateMachine
LoadDGO
CopyData
FindISOFile
FindVAGFile
GetISOFileLength
NullCallback
IsoStopVagStream
CopyDataToIOP
CopyDataToEE
DGOThread
RPC_DGO
LoadNextDGO
CancelDGO
InitDriver
SetVagClock
gcc2_compiled.
__gnu_compiled_c
iso_thread
dgo_mbx
dgo_thread
str_thread
play_thread
sync_mbx
iso_init_flag
_not_on_stack_sync
sLoadDgo
sRPCBuf
### iso_queue.c
InitBuffers
AllocDataBuffer
AllocateBuffer
FreeBuffer
ReleaseMessage
AllocIsoPages
FreeIsoPages
QueueMessage
UnqueueMessage
GetMessage
ProcessMessageData
ReturnMessage
GetVAGCommand
FreeVAGCommand
CheckForIsoPageBoundaryCrossing
FreeDataBuffer
gcc2_compiled.
__gnu_compiled_c
AllocdBuffersCount
NextBuffer
AllocdStrBuffersCount
NextStrBuffer
VAG_SilentLoop
sFreeBuffer
sFreeStrBuffer
sSema
vag_cmd_used
vag_cmd_cnt
max_vag_cmd_cnt
vag_cmds
### stream.c
RPC_STR
RPC_PLAY
STRThread
PLAYThread
gcc2_compiled.
__gnu_compiled_c
sCache
### srpc.c
RPC_Player
RPC_Loader
VBlank_Handler
Thread_Player
Thread_Loader
SetVagStreamName
SetVagName
gcc2_compiled.
__gnu_compiled_c
gInfoEE
gMusic
languages.8
dmaid
info
gPlayerBuf
gLoaderBuf
### vag.c
InitVagCmds
SmartAllocVagCmd
TerminateVAG
PauseVAG
UnPauseVAG
RestartVag
SetVAGVol
SetVagStreamsNoStart
InitVAGCmd
SetVagStreamsNotScanned
RemoveVagCmd
FindFreeVagCmd
FindNotQueuedVagCmd
FindWhosPlaying
FindVagStreamId
FindVagStreamPluginId
FindVagStreamName
FindThisVagStream
AnyVagRunning
FreeVagCmd
SetNewVagCmdPri
HowManyBelowThisPriority
StopVAG
VAG_MarkLoopEnd
VAG_MarkLoopStart
CalculateVAGPitch
PauseVagStreams
UnPauseVagStreams
SetAllVagsVol
CalculateVAGVolumes
gcc2_compiled.
__gnu_compiled_c
sbank.c
InitBanks
AllocateBankName
LookupBank
gcc2_compiled.
__gnu_compiled_c
gBanks
gCommonBank
gGunBank
gBoardBank
gLevelBanks
### ssound.c
InitSound
AllocateSound
CalculateFalloffVolume
CalculateAngle
SetEarTrans
SndMemAlloc
LookupSound
CleanSounds
UpdateVolume
GetVolume
GetPan
KillSoundsInGroup
SetCurve
SetMusicVol
SetBufferMem
ReleaseBufferMem
SndMemFree
gcc2_compiled.
__gnu_compiled_c
sqrt_table
atan_table
gSounds
gEarTrans
gCamTrans
gCamAngle
last_tick.26
common_bank
common_bank_mem
other_bank
other_bank_mem
mmd_bank
mmd_bank_mem
buffer_mem
### /usr/home/agavin/src/jak2/libs/common/soundcommon.c
ReadBankSoundNames
strcpy_toupper
gcc2_compiled.
__gnu_compiled_c
### iso_api.c
EEVagAndVagwad
QueueVAGStream
LoadISOFileToIOP
LoadISOFileToEE
LoadISOFileChunkToEE
PauseVAGStreams
UnpauseVAGStreams
SetVAGStreamPitch
SetDialogVolume
LoadSoundBank
LoadMusic
UnLoadMusic
PluginVagAndVagWad
gcc2_compiled.
__gnu_compiled_c
### /usr/home/agavin/src/jak2/libs/common/minilzo.c
lzo1x_decompress
__lzo_init2
gcc2_compiled.
__gnu_compiled_c
__lzo_init_done
plugin.c
Init989Plugins
QueueVagStream989
NullPlugin989
PlayQueuedVagStream989
StopVagStream989
SetVagStreamVolume989
StopEmAll989
SetStreamLfo989
gcc2_compiled.
__gnu_compiled_c
PluginId
### streamlfo.c
SineLfo
InitSineLfo
InitRandLfo
InitStreamLfoHandler
RandomLfo
RemoveLfoStreamFromList
CheckLfoList
UpdateLfoVars
RandomLfoSetPitchVars
RandomLfoWaitForPitch
SineLfoSetPitchVars
SineLfoWaitForPitch
StreamLfo
InitStreamLfoList
AddToCircularLfoStreamList
FindLfoStreamInList
gcc2_compiled.
__gnu_compiled_c
sine
_seed
### streamlist.c
InsertVagStreamInList
QueueNewStreamsFromList
CheckPlayList
StreamListThread
InitVagStreamList
FindVagStreamInList
GetVagStreamInList
RemoveVagStreamFromList
EmptyVagStreamList
MergeVagStreamLists
gcc2_compiled.
__gnu_compiled_c
### spustreams.c
ProcessVAGData
GetVAGStreamPos
CheckVAGStreamProgress
CheckVagStreamsProgress
StopVagStream
UpdateIsoBuffer
InitSpuStreamsThread
WakeSpuStreamsUp
GetSpuRamAddress
bswap
ProcessStreamData
gcc2_compiled.
__gnu_compiled_c
StreamsThread
### pages.c
InitPagedMemory
AllocPagesBytes
AllocPages
FreePagesList
StepTopPage
FromPagesCopy
gcc2_compiled.
__gnu_compiled_c
### list.c
InitList
AddToCircularList
MakeCircularList
BreakCircularList
gcc2_compiled.
+122
View File
@@ -0,0 +1,122 @@
#include "overlord.h"
#include <cstdio>
#include <cstring>
#include "game/overlord/common/sbank.h"
#include "game/overlord/jak1/ramdisk.h"
#include "game/overlord/jak2/iso.h"
#include "game/overlord/jak2/iso_queue.h"
#include "game/overlord/jak2/srpc.h"
#include "game/overlord/jak2/ssound.h"
#include "game/sce/iop.h"
namespace jak2 {
using namespace iop;
u8* ScratchPadMemoryBase;
namespace {
// believed unused global
s32 SndPlayThread;
} // namespace
/*!
* Entry point for the overlord. This runs InitISOFS, registers the vblank handler, and starts up
* the threads. It returns and assumes that the IOP kernel runs the threads.
*/
int start_overlord(int, const char* const*) {
sceSifInit();
sceSifInitRpc(0);
printf("IOP: =========Startup===(%x)====\n", 0);
// removed memory prints.
ScratchPadMemory = (u8*)AllocScratchPad(0);
ScratchPadMemoryBase = ScratchPadMemory;
// removed allocation check code.
InitBanks();
InitSound_overlord();
jak1::InitRamdisk(); // ramdisk believed unused.
RegisterVblankHandler(0, 0x20, VBlank_Handler, 0);
// ramdisk believed unused.
ThreadParam param;
param.entry = jak1::Thread_Server;
param.attr = TH_C;
param.initPriority = 0x7a;
param.stackSize = 0x800;
param.option = 0;
strcpy(param.name, "Server"); // added
auto thread_server = CreateThread(&param);
if (thread_server <= 0) {
return 1;
}
param.entry = Thread_Player;
param.attr = TH_C;
param.initPriority = 100;
param.stackSize = 0x800;
param.option = 0;
strcpy(param.name, "Player"); // added
auto thread_player = CreateThread(&param);
if (thread_player <= 0) {
return 1;
}
param.entry = Thread_Loader;
param.attr = 0x73;
param.initPriority = TH_C;
param.stackSize = 0x1000;
param.option = 0;
SndPlayThread = thread_player;
auto thread_loader = CreateThread(&param);
if (thread_loader <= 0) {
return 1;
}
InitISOFS(/*argv[1], argv[2]*/);
StartThread(thread_server, 0);
StartThread(thread_player, 0);
StartThread(thread_loader, 0);
printf("IOP: =========After inits=============\n");
// removed memory printing code
return 0;
}
static s32 gargc;
static const char* const* gargv;
static bool* init_complete;
static u32 call_start() {
start_overlord(gargc, gargv);
*init_complete = true;
while (true) {
SleepThread();
}
return 0;
}
int start_overlord_wrapper(int argc, const char* const* argv, bool* signal) {
ThreadParam param = {};
gargc = argc;
gargv = argv;
init_complete = signal;
param.attr = TH_C;
param.initPriority = 0;
param.stackSize = 0x800;
param.option = 0;
strcpy(param.name, "start"); // added for debug
param.entry = call_start;
auto start_thread = CreateThread(&param);
StartThread(start_thread, 0);
return 0;
}
} // namespace jak2
+7
View File
@@ -0,0 +1,7 @@
#pragma once
namespace jak2 {
int start_overlord_wrapper(int argc, const char* const* argv, bool* signal);
} // namespace jak2
+204
View File
@@ -0,0 +1,204 @@
#include "pages.h"
#include <cstdio>
#include <cstring>
#include "common/util/Assert.h"
#include "game/overlord/jak2/iso_queue.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak2 {
void InitPagedMemory(PageList* pool, int page_count, int page_size) {
// this is such a hack...
// we assume that we're allocated on the scratchpad, and can jump bump the pointer again.
pool->pages = (Page*)(pool + 1);
pool->page_count = page_count;
pool->page_size = page_size;
ScratchPadMemory = ScratchPadMemory + page_count * sizeof(Page);
int fixed_page_size = page_size;
if (page_size < 0) {
fixed_page_size = page_size + 0x7ff;
}
pool->sector_per_page = fixed_page_size >> 0xb;
pool->free_pages = page_count;
pool->page_memory = nullptr;
uintptr_t addr = (uintptr_t)AllocSysMemory(0, page_count * page_size + 0x100, nullptr);
addr += 0x3f;
addr &= ~uintptr_t(63);
u8* mem = (u8*)addr;
if (mem == 0) {
printf("======================================================================\n");
printf("IOP: pages InitPagedMemory: no memory for pages\n");
printf("======================================================================\n");
ASSERT_NOT_REACHED();
}
Page* page = pool->pages;
pool->page_memory = mem;
if (0 < page_count) {
for (int i = 0; i < page_count; i++) {
page->state = PageState::FREE;
page->buffer = mem;
page->maybe_page_id = i;
page->free_pages = 0;
page->ptr = mem + page_size - 1;
page->next = nullptr;
page->prev = nullptr;
page->end_page_first_only = nullptr;
page++;
mem += page_size;
}
}
}
/*!
* Allocate a list of pages that contain at least size_bytes in total.
*/
Page* AllocPagesBytes(PageList* page_list, u32 size_bytes) {
u32 num_pages = (size_bytes + page_list->page_size - 1) / page_list->page_size;
return AllocPages(page_list, num_pages);
}
/*!
* Allocate a list of pages.
*/
Page* AllocPages(PageList* page_list, u32 num_pages) {
if (page_list->page_count < num_pages) {
printf("======================================================================\n");
printf("IOP: pages AllocPages: %d pages requested %d maximum pages\n", num_pages,
page_list->page_count);
printf("======================================================================\n");
ASSERT_NOT_REACHED();
}
if (page_list->free_pages < num_pages) {
printf("======================================================================\n");
printf("IOP: pages AllocPages: need %d only %d free pages\n", num_pages, page_list->free_pages);
printf("======================================================================\n");
num_pages = page_list->free_pages;
ASSERT_NOT_REACHED(); // added.
}
Page* first_page = nullptr;
if (num_pages == 0) {
first_page = nullptr;
} else {
Page* prev_page = nullptr;
int page_idx_in_page_list = 0;
u32 added_pages = 0;
Page* iter = page_list->pages;
do {
if (iter->state == PageState::FREE) {
added_pages++;
page_list->free_pages = page_list->free_pages + -1;
iter->state = PageState::ALLOCATED_EMPTY;
iter->pages_after_this = num_pages - added_pages;
if (!first_page) {
iter->prev = nullptr;
first_page = iter;
} else {
prev_page->next = iter;
iter->prev = prev_page;
}
iter->end_page_first_only = nullptr;
prev_page = iter;
}
page_idx_in_page_list++;
iter++;
} while ((page_idx_in_page_list < MAX_PAGES_IN_POOL) && (added_pages < num_pages));
prev_page->next = nullptr;
first_page->end_page_first_only = prev_page;
first_page->free_pages = added_pages;
}
return first_page;
}
/*!
* Return the linked list of pages to the page list.
*/
Page* FreePagesList(PageList* page_list, Page* pages) {
if (pages) {
if (pages->prev) {
printf("======================================================================\n");
printf("IOP: pages FreePages: First page %d is not top of list\n", pages->maybe_page_id);
printf("======================================================================\n");
ASSERT_NOT_REACHED();
}
Page* next;
do {
next = pages->next;
pages->prev = nullptr;
pages->next = nullptr;
pages->end_page_first_only = nullptr;
pages->state = PageState::FREE;
page_list->free_pages = page_list->free_pages + 1;
pages = next;
} while (next);
}
return nullptr;
}
/*!
* Free the "top" page (the first one)
*/
Page* StepTopPage(PageList* param_1, Page* top_page) {
Page* result = nullptr;
if (top_page) {
// if we're first, shouldn't have a prev.
if (top_page->prev) {
printf("======================================================================\n");
printf("IOP: pages StepTopPage: Page %d is not top of list\n", top_page->maybe_page_id);
printf("======================================================================\n");
ASSERT_NOT_REACHED();
}
// new top page
result = top_page->next;
if (result) {
// set up new top page.
result->free_pages = top_page->free_pages + -1;
result->prev = nullptr;
result->end_page_first_only = top_page->end_page_first_only;
}
// return to pool
top_page->next = nullptr;
top_page->state = PageState::FREE;
param_1->free_pages = param_1->free_pages + 1;
}
return result;
}
/*!
* Copy bytes from pages
*/
void FromPagesCopy(const Page* page, const u8* page_ptr, u8* dest, int bytes_to_copy) {
Page* next_page;
const auto* page_data_end = page->ptr;
do {
auto n_this_time = (page_data_end - page_ptr) + 1;
do {
while (true) {
if (!bytes_to_copy) {
return;
}
if (n_this_time <= bytes_to_copy)
break;
memcpy(dest, page_ptr, bytes_to_copy);
bytes_to_copy = 0;
}
memcpy(dest, page_ptr, n_this_time);
dest = dest + n_this_time;
next_page = page->next;
bytes_to_copy = bytes_to_copy - n_this_time;
} while (!next_page);
page_ptr = (uint8_t*)next_page->buffer;
page_data_end = next_page->ptr;
page = next_page;
} while (true);
}
} // namespace jak2
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include "common/common_types.h"
namespace jak2 {
enum class PageState { FREE = 0, ALLOCATED_EMPTY = 3, ALLOCATED_FILLED = 4, SIX = 6 };
/*!
* A linked list of pages associated with a single read.
* Each "page" points to a buffer of memory.
*/
struct Page {
// FREE = belongs to the pool, ALLOCATED_EMPTY = in a chain, but no data
PageState state;
int maybe_page_id;
// how many pages in the chain after this one
int pages_after_this;
// how many pages are ALLOCATED_EMPTY in the chain?
int free_pages;
// the memory for the page
u8* buffer;
u8* ptr;
Page* prev;
Page* next;
Page* end_page_first_only;
};
struct PageList {
u32 page_count;
u32 page_size;
u32 sector_per_page; // round down
u32 free_pages;
u8* page_memory;
Page* pages;
};
constexpr int MAX_PAGES_IN_POOL = 0x12;
void InitPagedMemory(PageList* pool, int page_count, int page_size);
Page* AllocPagesBytes(PageList* page_list, u32 size_bytes);
Page* AllocPages(PageList* page_list, u32 num_pages);
Page* FreePagesList(PageList* page_list, Page* pages);
void FromPagesCopy(const Page* page, const u8* page_ptr, u8* dest, int bytes_to_copy);
Page* StepTopPage(PageList* param_1, Page* top_page);
} // namespace jak2
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "game/overlord/jak2/iso.h"
namespace jak2 {
void spusstreams_init_globals();
void WakeSpuStreamsUp();
void InitSpuStreamsThread();
int ProcessVAGData(CmdHeader* param_1_in, Buffer* param_2);
void StopVagStream(VagCmd* param_1, int param_2);
u32 GetSpuRamAddress(VagCmd* param_1);
} // namespace jak2
+614
View File
@@ -0,0 +1,614 @@
#include "srpc.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "game/common/loader_rpc_types.h"
#include "game/common/player_rpc_types.h"
#include "game/overlord/common/soundcommon.h"
#include "game/overlord/common/srpc.h"
#include "game/overlord/common/ssound.h"
#include "game/overlord/jak2/iso_api.h"
#include "game/overlord/jak2/spustreams.h"
#include "game/overlord/jak2/ssound.h"
#include "game/overlord/jak2/vag.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
namespace jak2 {
// korean inserted
static const char* languages[] = {"ENG", "FRE", "GER", "SPA", "ITA", "JAP", "KOR", "UKE"};
static u32 gInfoEE = 0;
static u32 IopTicks = 0;
static SoundIopInfo info;
static uint8_t gPlayerBuf[0x50 * 128];
static uint8_t gLoaderBuf[0x50];
void srpc_init_globals() {}
void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
if (!gSoundEnable) {
return nullptr;
}
// gFreeMem = QueryTotalFreeMemSize();
if (!PollSema(gSema)) {
if (gMusic) {
if (!gMusicPause && !LookupSound(666)) {
Sound* music = AllocateSound();
if (music != nullptr) {
gMusicFade = 0;
gMusicFadeDir = 1;
SetMusicVol();
music->sound_handle = snd_PlaySoundVolPanPMPB(gMusic, 0, 0x400, -1, 0, 0);
music->id = 666;
music->is_music = 1;
}
}
}
SignalSema(gSema);
}
SetMusicVol();
Sound* music = LookupSound(666);
if (music != nullptr) {
snd_SetSoundVolPan(music->sound_handle, 0x7FFFFFFF, 0);
}
constexpr int kMsgSize = 0x50;
static_assert(sizeof(SoundRpcCommand) == kMsgSize);
int n_messages = size / kMsgSize;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
if (!gSoundEnable) {
return nullptr;
}
while (n_messages > 0) {
switch (cmd->j2command) {
case Jak2SoundCommand::play: {
if (!cmd->play.sound_id) {
break;
}
auto sound = LookupSound(cmd->play.sound_id);
if (sound != nullptr) {
// update
sound->params = cmd->play.parms;
sound->is_music = false;
SFXUserData data{};
s32 found = snd_GetSoundUserData(0, nullptr, -1, sound->name, &data);
if ((sound->params.mask & 0x40) == 0) {
s16 fo_min = 5;
if (found && data.data[0])
fo_min = data.data[0];
sound->params.fo_min = fo_min;
}
if ((sound->params.mask & 0x80) == 0) {
s16 fo_max = 30;
if (found && data.data[1])
fo_max = data.data[1];
sound->params.fo_max = fo_max;
}
if ((sound->params.mask & 0x100) == 0) {
s16 fo_curve = 2;
if (found && data.data[2])
fo_curve = data.data[2];
sound->params.fo_curve = fo_curve;
}
UpdateVolume(sound);
snd_SetSoundPitchModifier(sound->sound_handle, sound->params.pitch_mod);
if (sound->params.mask & 0x4) {
snd_SetSoundPitchBend(sound->sound_handle, sound->params.bend);
}
if (sound->params.mask & 0x800) {
snd_SetSoundReg(sound->sound_handle, 0, sound->params.reg[0]);
}
if (sound->params.mask & 0x1000) {
snd_SetSoundReg(sound->sound_handle, 1, sound->params.reg[1]);
}
if (sound->params.mask & 0x2000) {
snd_SetSoundReg(sound->sound_handle, 2, sound->params.reg[2]);
}
} else {
// new sound
sound = AllocateSound();
if (sound == nullptr) {
// no free sounds
break;
}
strcpy_toupper(sound->name, cmd->play.name);
// TODO update params struct
sound->params = cmd->play.parms;
sound->is_music = false;
sound->bank_entry = nullptr;
SFXUserData data{};
s32 found = snd_GetSoundUserData(0, nullptr, -1, sound->name, &data);
if ((sound->params.mask & 0x40) == 0) {
s16 fo_min = 5;
if (found && data.data[0])
fo_min = data.data[0];
sound->params.fo_min = fo_min;
}
if ((sound->params.mask & 0x80) == 0) {
s16 fo_max = 30;
if (found && data.data[1])
fo_max = data.data[1];
sound->params.fo_max = fo_max;
}
if ((sound->params.mask & 0x100) == 0) {
s16 fo_curve = 2;
if (found && data.data[2])
fo_curve = data.data[2];
sound->params.fo_curve = fo_curve;
}
// lg::warn("RPC: PLAY {} v:{}, p:{}", sound->name, GetVolume(sound), GetPan(sound));
s32 handle = snd_PlaySoundByNameVolPanPMPB(0, nullptr, sound->name, GetVolume(sound),
GetPan(sound), sound->params.pitch_mod,
sound->params.bend);
sound->sound_handle = handle;
if (handle != 0) {
sound->id = cmd->play.sound_id;
if (sound->params.mask & 0x800) {
snd_SetSoundReg(sound->sound_handle, 0, sound->params.reg[0]);
}
if (sound->params.mask & 0x1000) {
snd_SetSoundReg(sound->sound_handle, 1, sound->params.reg[1]);
}
if (sound->params.mask & 0x2000) {
snd_SetSoundReg(sound->sound_handle, 2, sound->params.reg[2]);
}
}
}
} break;
case Jak2SoundCommand::pause_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_PauseSound(sound->sound_handle);
} else {
auto* vs = FindVagStreamId(cmd->sound_id.sound_id);
if (vs) {
PauseVAG(vs, 1);
}
}
// TODO vag
} break;
case Jak2SoundCommand::stop_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_StopSound(sound->sound_handle);
} else {
auto* vs = FindVagStreamId(cmd->sound_id.sound_id);
if (vs) {
StopVagStream(vs, 1);
}
}
// TODO vag
} break;
case Jak2SoundCommand::continue_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_ContinueSound(sound->sound_handle);
} else {
auto* vs = FindVagStreamId(cmd->sound_id.sound_id);
if (vs) {
UnPauseVAG(vs, 1);
}
}
// TODO vag
} break;
case Jak2SoundCommand::set_param: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
u32 mask = cmd->param.parms.mask;
if (sound != nullptr) {
if (mask & 1) {
if (mask & 0x10) {
sound->auto_time = cmd->param.auto_time;
sound->new_volume = cmd->param.parms.volume;
} else {
sound->params.volume = cmd->param.parms.volume;
}
}
if (mask & 0x20) {
sound->params.trans = cmd->param.parms.trans;
}
if (mask & 0x21) {
UpdateVolume(sound);
}
if (mask & 2) {
sound->params.pitch_mod = cmd->param.parms.pitch_mod;
if (mask & 0x10) {
snd_AutoPitch(sound->sound_handle, sound->params.pitch_mod, cmd->param.auto_time,
cmd->param.auto_from);
} else {
snd_SetSoundPitchModifier(sound->sound_handle, cmd->param.parms.pitch_mod);
}
}
if (mask & 4) {
sound->params.bend = cmd->param.parms.bend;
if (mask & 0x10) {
snd_AutoPitchBend(sound->sound_handle, sound->params.bend, cmd->param.auto_time,
cmd->param.auto_from);
} else {
snd_SetSoundPitchBend(sound->sound_handle, cmd->param.parms.bend);
}
}
if (mask & 0x400) {
sound->params.priority = cmd->param.parms.priority;
}
if (mask & 0x8) {
sound->params.group = cmd->param.parms.group;
}
if (mask & 0x40) {
sound->params.fo_min = cmd->param.parms.fo_min;
}
if (mask & 0x80) {
sound->params.fo_max = cmd->param.parms.fo_max;
}
if (mask & 0x100) {
sound->params.fo_curve = cmd->param.parms.fo_curve;
}
if (mask & 0x800) {
sound->params.reg[0] = cmd->param.parms.reg[0];
snd_SetSoundReg(sound->sound_handle, 0, cmd->param.parms.reg[0]);
}
if (mask & 0x1000) {
sound->params.reg[1] = cmd->param.parms.reg[1];
snd_SetSoundReg(sound->sound_handle, 1, cmd->param.parms.reg[1]);
}
if (mask & 0x2000) {
sound->params.reg[2] = cmd->param.parms.reg[2];
snd_SetSoundReg(sound->sound_handle, 2, cmd->param.parms.reg[2]);
}
} else {
auto* vs = FindVagStreamId(cmd->param.sound_id);
if (vs) {
if (mask & 0x2) {
SetVAGStreamPitch(cmd->param.sound_id, cmd->param.parms.pitch_mod);
}
}
}
// TODO vag
} break;
case Jak2SoundCommand::set_master_volume: {
u32 group = cmd->master_volume.group.group;
// FIXME array of set volumes
for (int i = 0; i < 32; i++) {
if (((group >> i) & 1) != 0) {
if (i == 1) {
// gMusicVol = cmd->master_volume.volume;
MasterVolume[1] = cmd->master_volume.volume;
} else if (i == 2) {
MasterVolume[2] = cmd->master_volume.volume;
SetDialogVolume(cmd->master_volume.volume);
} else {
MasterVolume[i] = cmd->master_volume.volume;
snd_SetMasterVolume(i, cmd->master_volume.volume);
SetAllVagsVol(i);
}
}
}
} break;
case Jak2SoundCommand::pause_group: {
snd_PauseAllSoundsInGroup(cmd->group.group);
if (cmd->group.group & 2) {
gMusicPause = 1;
}
if (cmd->group.group & 4) {
PauseVagStreams();
}
} break;
case Jak2SoundCommand::stop_group: {
KillSoundsInGroup(cmd->group.group);
if (cmd->group.group & 4) {
VagCmd local_178;
local_178.header.cmd_kind = 0x402;
local_178.header.mbx_to_reply = 0;
local_178.header.thread_id = 0;
local_178.vag_dir_entry = nullptr;
local_178.name[0] = '\0';
local_178.unk_136 = 0;
local_178.id = 0;
local_178.priority = 0;
StopVagStream(&local_178, 1);
}
} break;
case Jak2SoundCommand::continue_group: {
snd_ContinueAllSoundsInGroup(cmd->group.group);
if (cmd->group.group & 2) {
gMusicPause = 0;
}
if (cmd->group.group & 4) {
UnPauseVagStreams();
}
} break;
case Jak2SoundCommand::set_midi_reg: {
if (cmd->midi_reg.reg == 16) {
snd_SetGlobalExcite(cmd->midi_reg.value);
} else {
Sound* sound = LookupSound(666);
if (sound != nullptr) {
snd_SetMIDIRegister(sound->sound_handle, cmd->midi_reg.reg, cmd->midi_reg.value);
}
}
} break;
case Jak2SoundCommand::set_reverb: {
lg::warn("RPC_Player: unimplemented set_reverb");
// TODO reverb
} break;
case Jak2SoundCommand::set_ear_trans: {
SetEarTrans(&cmd->ear_trans_j2.ear_trans0, &cmd->ear_trans_j2.ear_trans1,
&cmd->ear_trans_j2.cam_trans, cmd->ear_trans_j2.cam_angle);
} break;
case Jak2SoundCommand::shutdown: {
gSoundEnable = 0;
} break;
case Jak2SoundCommand::set_fps: {
gFPS = cmd->fps.fps;
} break;
default:
ASSERT_MSG(false, fmt::format("Unhandled RPC Player command {}", int(cmd->j2command)));
}
n_messages--;
cmd++;
}
return nullptr;
}
void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
constexpr int kMsgSize = 0x50;
static_assert(sizeof(SoundRpcCommand) == kMsgSize);
int n_messages = size / kMsgSize;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
if (!gSoundEnable) {
return nullptr;
}
while (n_messages > 0) {
switch (cmd->j2command) {
case Jak2SoundCommand::load_bank: {
if (LookupBank(cmd->load_bank.bank_name)) {
break;
}
auto bank = AllocateBankName(cmd->load_bank.bank_name);
if (bank == nullptr) {
break;
}
strncpy(bank->name, cmd->load_bank.bank_name, 16);
bank->in_use = true;
bank->unk4 = 0;
LoadSoundBank(cmd->load_bank.bank_name, bank);
} break;
case Jak2SoundCommand::load_music: {
while (WaitSema(gSema))
;
if (gMusic) {
UnLoadMusic(&gMusic);
}
LoadMusic(cmd->load_bank.bank_name, &gMusic);
SignalSema(gSema);
} break;
case Jak2SoundCommand::unload_bank: {
auto bank = LookupBank(cmd->load_bank.bank_name);
if (!bank) {
break;
}
auto handle = bank->bank_handle;
if (!bank->unk4) {
bank->in_use = false;
}
bank->in_use = false;
snd_UnloadBank(handle);
snd_ResolveBankXREFS();
} break;
case Jak2SoundCommand::get_irx_version: {
cmd->irx_version.major = 4;
cmd->irx_version.minor = 0;
gInfoEE = cmd->irx_version.ee_addr;
return data;
} break;
case Jak2SoundCommand::set_language: {
gLanguage = languages[cmd->set_language.langauge_id];
} break;
case Jak2SoundCommand::list_sounds: {
// Not present in real jak2 overlord
PrintActiveSounds();
} break;
case Jak2SoundCommand::unload_music: {
while (WaitSema(gSema))
;
if (gMusic) {
UnLoadMusic(&gMusic);
}
SignalSema(gSema);
} break;
case Jak2SoundCommand::set_stereo_mode: {
s32 mode = cmd->stereo_mode.stereo_mode;
if (mode == 0) {
snd_SetPlayBackMode(1);
} else if (mode == 1) {
snd_SetPlayBackMode(2);
} else if (mode == 2) {
snd_SetPlayBackMode(0);
}
} break;
default:
ASSERT_MSG(false, fmt::format("Unhandled RPC Loader command {}", int(cmd->j2command)));
}
n_messages--;
cmd++;
}
return nullptr;
}
int VBlank_Handler(void*) {
bool bVar1;
int iVar2;
int iVar3;
uint8_t uVar4;
IopTicks = IopTicks + 1;
if (gSoundEnable == 0) {
return 1;
}
iWakeupThread(StreamThread);
if (gMusicFadeDir < 0) {
gMusicFade = gMusicFade + -0x200;
if (-1 < gMusicFade)
goto LAB_00008d9c;
gMusicFade = 0;
} else {
if ((gMusicFadeDir < 1) || (gMusicFade = gMusicFade + 0x400, gMusicFade < 0x10001))
goto LAB_00008d9c;
gMusicFade = 0x10000;
}
gMusicFadeDir = 0;
LAB_00008d9c:
if (gInfoEE != 0) {
gFrameNum = gFrameNum + 1;
// instant dma
// if (dmaid != 0) {
// iVar2 = sceSifDmaStat();
// if (-1 < iVar2) {
// return 1;
// }
// dmaid = 0;
// }
for (int i = 0; i < 4; i++) {
u32 status_bits = 0;
u32 pos;
for (int j = 0; j < 24; j++) {
if (VagCmds[i].status_bytes[j]) {
status_bits |= (1 << j);
}
}
if (VagCmds[i].unk_232) {
status_bits |= (1 << 24);
}
if (VagCmds[i].byte6 && VagCmds[i].sb_paused == 0) {
VagCmds[i].unk_192 += CalculateVAGPitch(0x400, VagCmds[i].unk_256_pitch2) / gFPS;
}
if (VagCmds[i].sb_playing == 0 && VagCmds[i].byte5) {
pos = 0;
} else {
pos = VagCmds[i].unk_200;
}
info.stream_status[i] = status_bits;
info.stream_position[i] = pos;
// printf("positions: %d\n", pos);
info.stream_id[i] = VagCmds[i].id;
}
info.iop_ticks = IsoThreadCounter;
info.frame = gFrameNum;
info.freemem = 100 /*gFreeMem*/;
info.freemem2 = QueryTotalFreeMemSize();
info.nocd = 0 /*gNoCD*/;
info.dirtycd = 0 /*gDirtyCD*/;
info.diskspeed[0] = 0 /*gDiskSpeed*/;
info.diskspeed[1] = 0 /*DAT_00013488*/;
info.lastspeed = 0 /*gLastSpeed*/;
info.dupseg = 0 /*gDupSeg*/;
iVar2 = 1;
do {
iVar3 = snd_GetVoiceStatus(iVar2);
uVar4 = '\0';
if (iVar3 == 1) {
uVar4 = 0xff;
}
info.chinfo[iVar2] = uVar4;
bVar1 = iVar2 < 0x30;
iVar2 = iVar2 + 1;
} while (bVar1);
LookupSound(0x29a); // lol idk
/*
local_38 = &info;
local_30 = 0x250;
local_2c = 0;
local_34 = gInfoEE;
dmaid = sceSifSetDma(&local_38,1);
*/
sceSifDmaData dma;
dma.data = &info;
dma.addr = (void*)(uintptr_t)gInfoEE;
dma.size = 0x250;
dma.mode = 0;
/*dmaid =*/sceSifSetDma(&dma, 1);
}
return 1;
}
u32 Thread_Player() {
sceSifQueueData dq;
sceSifServeData serve;
// set up RPC
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, PLAYER_RPC_ID[g_game_version], RPC_Player, gPlayerBuf, nullptr, nullptr,
&dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
u32 Thread_Loader() {
sceSifQueueData dq;
sceSifServeData serve;
// set up RPC
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, LOADER_RPC_ID[g_game_version], RPC_Loader, gLoaderBuf, nullptr, nullptr,
&dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
void SetVagStreamName(VagCmd* param_1, int param_2, int param_3) {
// undefined4 local_18 [2];
if (param_3 == 1) {
// CpuSuspendIntr(local_18);
}
if (param_2 == 0) {
info.stream_name[param_1->idx_in_cmd_arr].dat[0] = '\0';
} else {
strncpy(info.stream_name[param_1->idx_in_cmd_arr].dat, param_1->name, 0x30);
}
if (param_3 == 1) {
// CpuResumeIntr(local_18[0]);
}
}
void SetVagName(int param_1, char* param_2, int param_3) {
// CpuSuspendIntr(local_18);
if (param_3 == 0) {
info.stream_name[param_1].dat[0] = '\0';
} else {
strncpy(info.stream_name[param_1].dat, param_2, 0x30);
}
// CpuResumeIntr(local_18[0]);
}
} // namespace jak2
+153
View File
@@ -0,0 +1,153 @@
#pragma once
#include "common/common_types.h"
#include "game/overlord/common/srpc.h"
namespace jak2 {
enum class Jak2SoundCommand : u16 {
iop_store = 0,
iop_free = 1,
load_bank = 2,
load_bank_from_iop = 3,
load_bank_from_ee = 4,
load_music = 5,
unload_bank = 6,
play = 7,
pause_sound = 8,
stop_sound = 9,
continue_sound = 10,
set_param = 11,
set_master_volume = 12,
pause_group = 13,
stop_group = 14,
continue_group = 15,
get_irx_version = 16,
set_falloff_curve = 17,
set_sound_falloff = 18,
reload_info = 19,
set_language = 20,
set_flava = 21,
set_midi_reg = 22,
set_reverb = 23,
set_ear_trans = 24,
shutdown = 25,
list_sounds = 26,
unload_music = 27,
set_fps = 28,
boot_load = 29,
game_load = 30,
num_tests = 31,
num_testruns = 32,
num_sectors = 33,
num_streamsectors = 34,
num_streambanks = 35,
track_pitch = 36,
linvel_nom = 37,
linvel_stm = 38,
seek_nom = 39,
seek_stm = 40,
read_seq_nom = 41,
read_seq_stm = 42,
read_spr_nom = 43,
read_spr_stm = 44,
read_spr_strn_nom = 45,
rand_stm_abort = 46,
rand_nom_abort = 47,
iop_mem = 48,
cancel_dgo = 49,
set_stereo_mode = 50,
};
struct SoundRpcCommand {
u16 rsvd1;
Jak2SoundCommand j2command;
union {
SoundRpcGetIrxVersion irx_version;
SoundRpcBankCommand load_bank;
SoundRpcSetLanguageCommand set_language;
SoundRpcPlayCommand play;
SoundRpcSoundIdCommand sound_id;
SoundRpcSetFPSCommand fps;
SoundRpcSetEarTrans ear_trans;
SoundRpc2SetEarTrans ear_trans_j2;
SoundRpcSetReverb reverb;
SoundRpcSetFallof fallof;
SoundRpcSetFallofCurve fallof_curve;
SoundRpcGroupCommand group;
SoundRpcSetFlavaCommand flava;
SoundRpcMasterVolCommand master_volume;
SoundRpcSetParamCommand param;
SoundRpcStereoMode stereo_mode;
SoundRpcSetMidiReg midi_reg;
SoundRpcSetMirrror mirror;
u8 max_size[0x4C]; // Temporary
};
};
/*
*
((frame uint32 :offset-assert 0)
(strpos int32 :offset-assert 4)
(str-id uint32 :offset-assert 8)
(str-id-sign int32 :offset 8)
(freemem uint32 :offset-assert 12)
(chinfo uint8 48 :offset-assert 16)
(freemem2 uint32 :offset-assert 64)
(nocd uint32 :offset-assert 68)
(dirtycd uint32 :offset-assert 72)
(diskspeed uint32 2 :offset-assert 76)
(lastspeed uint32 :offset-assert 84)
(dupseg int32 :offset-assert 88)
(times int32 41 :offset-assert 92)
(times-seq uint32 :offset-assert 256)
(iop-ticks uint32 :offset-assert 260)
(stream-position uint32 4 :offset 272)
(stream-status stream-status 4 :offset-assert 288)
(stream-name sound-stream-name 4 :inline :offset-assert 304)
(stream-id sound-id 4 :offset-assert 496)
(stream-id-int32 int32 4 :offset 496) ;; ughhhh...
(music-register uint8 17 :offset-assert 512)
(music-excite int8 :offset 528)
(ramdisk-name uint8 48 :offset-assert 529)
*/
struct StreamName {
char dat[48];
};
struct SoundIopInfo {
u32 frame;
s32 strpos;
u32 std_id;
u32 freemem;
u8 chinfo[48];
u32 freemem2;
u32 nocd;
u32 dirtycd;
u32 diskspeed[2];
u32 lastspeed;
s32 dupseg;
u32 times[41];
u32 times_seq;
u32 iop_ticks;
u32 p;
u32 q;
s32 stream_position[4];
s32 stream_status[4];
StreamName stream_name[4];
s32 stream_id[4];
u8 music_register[17];
char ramdisk_name[48];
char pad[3];
char more_padding[12];
};
static_assert(sizeof(SoundIopInfo) == 0x250);
struct VagCmd;
void SetVagStreamName(VagCmd* param_1, int param_2, int param_3);
void srpc_init_globals();
int VBlank_Handler(void*);
u32 Thread_Player();
u32 Thread_Loader();
} // namespace jak2
+175
View File
@@ -0,0 +1,175 @@
#include "ssound.h"
#include "common/util/Assert.h"
#include "game/overlord/common/srpc.h"
#include "game/overlord/common/ssound.h"
#include "game/overlord/jak2/spustreams.h"
#include "game/overlord/jak2/streamlist.h"
#include "game/overlord/jak2/vag.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
namespace jak2 {
VolumePair gPanTable[361];
s32 StreamThread = 0;
void ssound_init_globals() {
StreamThread = 0;
}
void SetBufferMem(void*, int) {}
void ReleaseBufferMem() {}
void InitSound_overlord() {
for (auto& s : gSounds) {
s.id = 0;
}
SetCurve(2, 0, 0);
SetCurve(9, 0, 0);
SetCurve(11, 0, 0);
SetCurve(10, 0, 0);
SetCurve(3, 4096, 0);
SetCurve(4, 0, 4096);
SetCurve(5, 2048, 0);
SetCurve(6, 2048, 2048);
SetCurve(7, -4096, 0);
SetCurve(8, -2048, 0);
// need this one for PC sound to start up
snd_StartSoundSystem();
// there's a bunch of stuff that we don't use.
StreamVoice[0] = 0;
StreamVoice[1] = 1;
StreamVoice[2] = 2;
StreamVoice[3] = 3; // TODO idk what im doing.
for (int i = 0; i < 91; i++) {
s16 opposing_front = static_cast<s16>(((i * 0x33ff) / 0x5a) + 0xc00);
s16 rear_right = static_cast<s16>(((i * -0x2800) / 0x5a) + 0x3400);
s16 rear_left = static_cast<s16>(((i * -0xbff) / 0x5a) + 0x3fff);
gPanTable[90 - i].left = 0x3FFF;
gPanTable[180 - i].left = opposing_front;
gPanTable[270 - i].left = rear_right;
gPanTable[360 - i].left = rear_left;
gPanTable[i].right = opposing_front;
gPanTable[90 + i].right = 0x3FFF;
gPanTable[180 + i].right = rear_left;
gPanTable[270 + i].right = rear_right;
}
snd_SetPanTable((s16*)gPanTable);
snd_SetPlayBackMode(2);
SemaParam local_58;
local_58.attr = 1;
local_58.init_count = 1;
local_58.max_count = 1;
local_58.option = 0;
gSema = CreateSema(&local_58);
if (gSema < 0) {
printf("IOP: ======================================================================\n");
printf("IOP: ssound InitSound: can\'t create semaphore\n");
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
// Init989Plugins(); TODO
// InitStreamLfoHandler(); TODO
InitVagStreamList(&PluginStreamsList, 4, "plugin");
InitVagStreamList(&EEStreamsList, 4, "ee");
InitVagStreamList(&EEPlayList, 8, "play");
InitVagStreamList(&RequestedStreamsList, 8, "streams");
InitVagStreamList(&NewStreamsList, 4, "new");
ThreadParam local_30;
local_30.attr = 0x2000000;
local_30.entry = StreamListThread;
local_30.initPriority = 0x78;
local_30.stackSize = 0x1000;
local_30.option = 0;
StreamThread = CreateThread(&local_30);
if (StreamThread < 1) {
printf("IOP: ======================================================================\n");
printf("IOP: ssound InitSound: can\'t create streamlist thread\n");
printf("IOP: ======================================================================\n");
ASSERT_NOT_REACHED();
}
StartThread(StreamThread, 0);
}
void SetMusicVol() {
int vol = (MasterVolume[1] * gMusicFade >> 0x10) * gMusicTweak >> 7;
snd_SetMasterVolume(1, vol);
snd_SetMasterVolume(2, vol);
}
void SetEarTrans(Vec3w* ear_trans0, Vec3w* ear_trans1, Vec3w* cam_trans, s32 cam_angle) {
// some assuming that this is the same in jak2...
s32 tick = snd_GetTick();
u32 delta = tick - sLastTick;
sLastTick = tick;
gEarTrans[0] = *ear_trans0;
gEarTrans[1] = *ear_trans1;
gCamTrans = *cam_trans;
gCamAngle = cam_angle;
for (auto& s : gSounds) {
if (s.id != 0 && s.is_music == 0) {
if (s.auto_time != 0) {
UpdateAutoVol(&s, delta);
}
UpdateLocation(&s);
}
}
// SetVAGVol();
int iVar2 = 0;
auto* cmd = VagCmds;
// piVar6 = &VagCmds[0].vol_multiplier;
do {
if (cmd->unk_136 == 0x0) {
LAB_0000c388:
SetVAGVol(cmd, 1);
} else if ((cmd->sb_scanned == '\0') || (cmd->byte8 != '\0')) {
if (cmd->byte20 == '\0') {
if ((u32)cmd->vol_multiplier < 0x11) {
cmd->vol_multiplier = 0;
} else {
cmd->vol_multiplier = cmd->vol_multiplier - 0x10;
}
SetVAGVol(cmd, 1);
if (cmd->vol_multiplier != 0)
goto LAB_0000c398;
}
LAB_0000c378:
StopVagStream(cmd, 1);
} else {
if (snd_SoundIsStillPlaying(cmd->id) != 0)
goto LAB_0000c388;
if (cmd->byte20 != '\0')
goto LAB_0000c378;
// CpuSuspendIntr(local_30);
cmd->byte8 = '\x01';
// CpuResumeIntr(local_30[0]);
}
LAB_0000c398:
cmd = cmd + 1;
// piVar6 = piVar6 + 0x51;
iVar2 = iVar2 + 1;
if (3 < iVar2) {
return;
}
} while (true);
}
} // namespace jak2
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "game/overlord/common/ssound.h"
namespace jak2 {
void ssound_init_globals();
void SetBufferMem(void*, int);
void ReleaseBufferMem();
void SetMusicVol();
void SetEarTrans(Vec3w* ear_trans0, Vec3w* ear_trans1, Vec3w* cam_trans, s32 cam_angle);
void InitSound_overlord();
extern s32 StreamThread;
extern VolumePair gPanTable[361];
} // namespace jak2
+359
View File
@@ -0,0 +1,359 @@
#include "stream.h"
#include <cstring>
#include "common/util/FileUtil.h"
#include "game/common/play_rpc_types.h"
#include "game/common/str_rpc_types.h"
#include "game/overlord/common/iso.h"
#include "game/overlord/common/iso_api.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/jak2/iso.h"
#include "game/overlord/jak2/iso_api.h"
#include "game/overlord/jak2/streamlist.h"
#include "game/overlord/jak2/vag.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak2 {
static RPC_Str_Cmd_Jak2 sSTRBuf;
static RPC_Play_Cmd_Jak2 sPLAYBuf[2]; // called sRPCBuf2
struct CacheEntry {
FileRecord* fr = nullptr;
s32 countdown = 0;
StrFileHeaderJ2 header;
};
constexpr int STR_INDEX_CACHE_SIZE = 4;
CacheEntry sCache[STR_INDEX_CACHE_SIZE];
void stream_init_globals() {
memset(&sSTRBuf, 0, sizeof(RPC_Str_Cmd_Jak2));
memset(&sPLAYBuf, 0, sizeof(RPC_Play_Cmd_Jak2) * 2);
}
/*!
* The STR RPC handler.
*/
void* RPC_STR(unsigned int /*fno*/, void* _cmd, int /*y*/) {
auto* cmd = (RPC_Str_Cmd_Jak2*)_cmd;
if (cmd->section < 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->basename);
if (file_record == nullptr) {
// file not found!
printf("[OVERLORD STR] Failed to find file %s for loading.\n", cmd->basename);
cmd->result = STR_RPC_RESULT_ERROR;
} else {
// load directly to the EE
cmd->maxlen = LoadISOFileToEE(file_record, cmd->address, cmd->maxlen);
if (cmd->maxlen) {
// 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];
file_util::ISONameFromAnimationName(animation_iso_name, cmd->basename);
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 (%s)\n", cmd->basename,
animation_iso_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, (u8*)&sCache[oldest_idx].header,
sizeof(StrFileHeaderJ2))) {
printf("[OVERLORD STR] Failed to load chunk file header for animation %s\n",
cmd->basename);
cmd->result = 1;
return cmd;
}
}
// load data, using the cached header to find the location of the chunk.
if (!LoadISOFileChunkToEE(file_record, cmd->address,
sCache[cache_entry].header.sizes[cmd->section],
sCache[cache_entry].header.sectors[cmd->section])) {
printf("[OVERLORD STR] Failed to load chunk %d for animation %s\n", cmd->section,
cmd->basename);
cmd->result = 1;
} else {
// successful load!
cmd->maxlen = sCache[cache_entry].header.sizes[cmd->section];
cmd->result = 0;
}
}
}
// don't remember why we changed this...
return cmd;
// return nullptr;
}
void* RPC_PLAY([[maybe_unused]] unsigned int fno, void* _cmd, int size) {
// uint16_t uVar1;
VagCmd* pRVar2;
VagStrListNode* iVar3;
VagStrListNode* iVar4;
// RPC_Play_Cmd_Jak2* pRVar3;
// char* __src;
int iVar5;
// int iVar6;
// uint uVar7;
// RPC_Play_Cmd_Jak2* pRVar8;
// int iVar10;
VagStrListNode list_node;
// int local_30;
// int local_2c;
// if (size < 0) {
// size = size + 0xff;
// }
// local_30 = size >> 8;
// local_2c = 0;
int n_messages = size / sizeof(RPC_Play_Cmd_Jak2);
auto* cmd_iter = (RPC_Play_Cmd_Jak2*)_cmd;
for (int i = 0; i < n_messages; i++) {
// printf("RPC_PLAY message %d\n", i);
auto cmd_result = cmd_iter->result;
if (cmd_result == 1) {
for (int s = 0; s < 4; s++) {
if (cmd_iter->names[s].chars[0]) {
strncpy(list_node.name, cmd_iter->names[s].chars, 0x30);
list_node.id = cmd_iter->id[s];
WaitSema(EEStreamsList.sema);
RemoveVagStreamFromList(&list_node, &EEStreamsList);
SignalSema(EEStreamsList.sema);
WaitSema(EEPlayList.sema);
RemoveVagStreamFromList(&list_node, &EEPlayList);
SignalSema(EEPlayList.sema);
}
}
} else {
iVar5 = 9;
if (cmd_result == 2) {
// uVar7 = 0;
// iVar6 = 0x20;
WaitSema(EEStreamsList.sema);
EmptyVagStreamList(&EEStreamsList);
for (int s = 0; s < 4; s++) {
if (cmd_iter->names[s].chars[0] && cmd_iter->id[s]) {
// printf("got queue command %d: %s %d\n", s, cmd_iter->names[s].chars,
// cmd_iter->id[s]);
strncpy(list_node.name, cmd_iter->names[s].chars, 0x30);
list_node.id = cmd_iter->id[s];
list_node.unk_76 = cmd_iter->address & 1 << (s & 0x1f) & 0xf;
list_node.unk_72 = 0;
list_node.unk_80 = cmd_iter->address & 0x10 << (s & 0x1f) & 0xf0;
list_node.prio = iVar5;
pRVar2 = FindThisVagStream(list_node.name, list_node.id);
if (pRVar2 != 0x0) {
pRVar2->unk_288 = list_node.unk_76;
pRVar2->unk_292 = list_node.unk_80;
if (pRVar2->unk_288 != 0) {
pRVar2->byte10 = '\x01';
}
if (pRVar2->unk_292 != 0) {
pRVar2->unk_232 = '\x01';
}
}
InsertVagStreamInList(&list_node, &EEStreamsList);
}
if (iVar5 == 8) {
iVar5 = 2;
} else if (0 < iVar5) {
iVar5 = iVar5 + -1;
}
}
/*
pRVar3 = cmd_iter;
pRVar8 = cmd_iter;
do {
if ((pRVar8->names[0].data[0] != '\0') && (pRVar3->id[0] != 0)) {
strncpy(list_node.name, (char*)((int)cmd_iter->id + iVar6 + -0x10), 0x30);
list_node.id = pRVar3->id[0];
list_node.unk_76 = cmd_iter->address & 1 << (uVar7 & 0x1f) & 0xf;
list_node.unk_72 = 0;
list_node.unk_80 = cmd_iter->address & 0x10 << (uVar7 & 0x1f) & 0xf0;
list_node.prio = iVar5;
pRVar2 = FindThisVagStream(list_node.name, list_node.id);
if (pRVar2 != (RealVagCmd*)0x0) {
pRVar2->unk_288 = list_node.unk_76;
pRVar2->unk_292 = list_node.unk_80;
if (pRVar2->unk_288 != 0) {
pRVar2->byte10 = '\x01';
}
if (pRVar2->unk_292 != 0) {
pRVar2->unk_232 = '\x01';
}
}
InsertVagStreamInList(&list_node, (List*)EEStreamsList);
}
if (iVar5 == 8) {
iVar5 = 2;
} else if (0 < iVar5) {
iVar5 = iVar5 + -1;
}
iVar6 = iVar6 + 0x30;
pRVar3 = (RPC_Play_Cmd*)&pRVar3->address;
uVar7 = uVar7 + 1;
pRVar8 = (RPC_Play_Cmd*)(pRVar8->names[0].data + 0x10);
} while ((int)uVar7 < 4);
*/
SignalSema(EEStreamsList.sema);
} else if (cmd_result == 0) {
iVar5 = 9;
for (int s = 0; s < 4; s++) {
if (cmd_iter->names[s].chars[0] && cmd_iter->id[s]) {
// __src = (char*)((int)cmd_iter->id + iVar10 + -0x10);
strncpy(list_node.name, cmd_iter->names[s].chars, 0x30);
list_node.id = cmd_iter->id[s];
list_node.unk_68 = 0;
list_node.unk_72 = 0;
list_node.prio = iVar5;
pRVar2 = FindThisVagStream(cmd_iter->names[s].chars, cmd_iter->id[s]);
if ((pRVar2 == 0x0) || (pRVar2->byte4 == '\0')) {
printf(" didn't exist, looks like it needs to be added!\n");
WaitSema(EEPlayList.sema);
iVar3 = (VagStrListNode*)FindVagStreamInList(&list_node, &EEPlayList);
if (iVar3 == (VagStrListNode*)0x0) {
printf("node also doesn't exist, adding it!\n");
iVar4 = (VagStrListNode*)InsertVagStreamInList(&list_node, &EEPlayList);
iVar4->id = list_node.id;
iVar4->prio = list_node.prio;
iVar4->unk_72 = list_node.unk_72;
iVar4->unk_76 = 0;
iVar4->unk_80 = 0;
iVar4->unk_92 = 0;
iVar4->unk_68 = list_node.unk_68;
strncpy(iVar4->name, list_node.name, 0x30);
}
SignalSema(EEPlayList.sema);
}
}
if (iVar5 == 8) {
iVar5 = 2;
} else if (0 < iVar5) {
iVar5 = iVar5 + -1;
}
}
/*
iVar6 = 0;
iVar10 = 0x20;
pRVar3 = cmd_iter;
pRVar8 = cmd_iter;
do {
if ((pRVar8->names[0].data[0] != '\0') && (pRVar3->id[0] != 0)) {
__src = (char*)((int)cmd_iter->id + iVar10 + -0x10);
strncpy(list_node.name, __src, 0x30);
list_node.id = pRVar3->id[0];
list_node.unk_68 = 0;
list_node.unk_72 = 0;
list_node.prio = iVar5;
pRVar2 = FindThisVagStream(__src, pRVar3->id[0]);
if ((pRVar2 == (RealVagCmd*)0x0) || (pRVar2->byte4 == '\0')) {
WaitSema(EEPlayList._12_4_);
iVar3 = (VagStrListNode*)FindVagStreamInList(&list_node, (List*)EEPlayList);
if (iVar3 == (VagStrListNode*)0x0) {
iVar4 = (VagStrListNode*)InsertVagStreamInList(&list_node, (List*)EEPlayList);
strncpy(iVar4->name, list_node.name, 0x30);
iVar4->id = list_node.id;
iVar4->prio = list_node.prio;
iVar4->unk_72 = list_node.unk_72;
iVar4->unk_76 = 0;
iVar4->unk_80 = 0;
iVar4->unk_92 = 0;
iVar4->unk_68 = list_node.unk_68;
}
SignalSema(EEPlayList._12_4_);
}
}
if (iVar5 == 8) {
iVar5 = 2;
} else if (0 < iVar5) {
iVar5 = iVar5 + -1;
}
iVar10 = iVar10 + 0x30;
pRVar3 = (RPC_Play_Cmd*)&pRVar3->address;
iVar6 = iVar6 + 1;
pRVar8 = (RPC_Play_Cmd*)(pRVar8->names[0].data + 0x10);
} while (iVar6 < 4);
*/
}
}
cmd_iter = cmd_iter + 1;
}
return _cmd;
}
/*!
* Run the STR RPC handler.
*/
u32 STRThread() {
sceSifQueueData dq;
sceSifServeData serve;
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, STR_RPC_ID[g_game_version], RPC_STR, &sSTRBuf, nullptr, nullptr, &dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
sceSifServeData* gserve = nullptr;
u32 PLAYThread() {
sceSifQueueData dq;
sceSifServeData serve;
gserve = &serve;
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, PLAY_RPC_ID[g_game_version], RPC_PLAY, sPLAYBuf, nullptr, nullptr, &dq);
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
} // namespace jak2
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "common/common_types.h"
namespace jak2 {
void stream_init_globals();
u32 STRThread();
u32 PLAYThread();
} // namespace jak2
+3
View File
@@ -0,0 +1,3 @@
#include "streamlfo.h"
namespace jak2 {}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
namespace jak2 {}
+633
View File
@@ -0,0 +1,633 @@
#include "streamlist.h"
#include <cstdio>
#include <cstring>
#include "game/overlord/jak2/vag.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak2 {
List PluginStreamsList;
List LfoList;
List EEPlayList;
List RequestedStreamsList;
List NewStreamsList;
List EEStreamsList;
void init_globals_streamlist() {
memset(&PluginStreamsList, 0, sizeof(PluginStreamsList));
memset(&LfoList, 0, sizeof(LfoList));
memset(&EEPlayList, 0, sizeof(EEPlayList));
memset(&RequestedStreamsList, 0, sizeof(RequestedStreamsList));
memset(&NewStreamsList, 0, sizeof(NewStreamsList));
memset(&EEStreamsList, 0, sizeof(EEStreamsList));
}
// TODO
void CheckLfoList(void*) {}
void RemoveLfoStreamFromList(void*, void*) {}
VagStrListNode* InsertVagStreamInList(VagStrListNode* param_1, List* param_2) {
int iVar1;
u32 uVar2;
u32 uVar3;
VagStrListNode* pLVar3;
VagStrListNode* pVVar4;
VagStrListNode* pVVar5;
VagStrListNode* pVVar6;
uVar2 = 0;
uVar3 = param_2->elt_count;
pLVar3 = (VagStrListNode*)param_2->next;
pVVar5 = (VagStrListNode*)0x0;
if (uVar3 != 0) {
do {
if (pLVar3->id == 0) {
uVar2 = uVar3;
pVVar5 = pLVar3;
}
pLVar3 = (VagStrListNode*)(pLVar3->list).next;
uVar2 = uVar2 + 1;
} while (uVar2 < uVar3);
pLVar3 = (VagStrListNode*)param_2->next;
}
pVVar6 = (VagStrListNode*)0x0;
if (pVVar5 == (VagStrListNode*)0x0) {
printf("IOP: ======================================================================\n");
printf("IOP: streamlist InsertVagStreamInList: no free spot in list %s\n", param_2->name);
printf("IOP: ======================================================================\n");
} else {
uVar2 = 0;
if (uVar3 != 0) {
do {
pVVar4 = pLVar3;
uVar2 = uVar2 + 1;
if (pVVar4->prio < param_1->prio) {
param_2->maybe_any_in_use = 1;
(pVVar5->list).in_use = 1;
strncpy(pVVar5->name, param_1->name, 0x30);
pVVar5->id = param_1->id;
pVVar5->unk_68 = param_1->unk_68;
pVVar5->unk_76 = param_1->unk_76;
pVVar5->unk_80 = param_1->unk_80;
pVVar5->prio = param_1->prio;
iVar1 = param_1->unk_72;
pVVar5->unk_92 = 0;
pVVar5->unk_72 = iVar1;
pVVar5->vol_multiplier = param_1->vol_multiplier;
pVVar5->unk_100 = param_1->unk_100;
if (pVVar6 == (VagStrListNode*)0x0) {
if (pVVar5 == pVVar4) {
return pVVar5;
}
((pVVar5->list).next)->prev = (pVVar5->list).prev;
((pVVar5->list).prev)->next = (pVVar5->list).next;
(pVVar4->list).prev = &pVVar5->list;
(pVVar5->list).next = &pVVar4->list;
(pVVar5->list).prev = (ListNode*)0x0;
param_2->next = &pVVar5->list;
return pVVar5;
}
if (pVVar5 == pVVar4) {
return pVVar5;
}
((pVVar5->list).prev)->next = (pVVar5->list).next;
((pVVar5->list).next)->prev = (pVVar5->list).prev;
(pVVar5->list).next = (pVVar6->list).next;
(pVVar6->list).next = &pVVar5->list;
(pVVar4->list).prev = &pVVar5->list;
(pVVar5->list).prev = &pVVar6->list;
return pVVar5;
}
pLVar3 = (VagStrListNode*)(pVVar4->list).next;
pVVar6 = pVVar4;
} while (uVar2 < uVar3);
}
}
return pVVar5;
}
void QueueNewStreamsFromList(List* list) {
int iVar1;
u32 uVar2;
VagCmd* pRVar3;
// undefined4 *puVar4;
VagStrListNode* pVVar5;
VagStrListNode* pvVar6;
u32 uVar6;
u32 uVar7;
SetVagStreamsNotScanned();
iVar1 = NewStreamsList.elt_count;
uVar6 = 0;
uVar7 = 0;
if (NewStreamsList.elt_count != 0) {
// puVar4 = (undefined4 *)((int)NewStreamsList.buffer + 8);
pvVar6 = (VagStrListNode*)NewStreamsList.buffer;
do {
strncpy(pvVar6->name, "free", 0x30);
// puVar4[0xe] = 0;
pvVar6->id = 0;
// puVar4[0x10] = 0;
pvVar6->unk_72 = 0;
// puVar4[0x13] = 0;
pvVar6->prio = 0;
// puVar4[0x11] = 0;
pvVar6->unk_76 = 0;
// puVar4[0x12] = 0;
pvVar6->unk_80 = 0;
// puVar4[0x14] = 0;
pvVar6->unk_88 = 0;
// puVar4[0x15] = 0;
pvVar6->unk_92 = 0;
// puVar4[0x16] = 0;
pvVar6->vol_multiplier = 0;
// puVar4[0x17] = 0;
pvVar6->unk_100 = 0;
// *puVar4 = 0;
pvVar6->list.in_use = 0;
// puVar4 = puVar4 + 0x1a;
uVar6 = uVar6 + 1;
pvVar6 = pvVar6 + 1;
} while (uVar6 < (u32)iVar1);
}
uVar6 = 0;
NewStreamsList.maybe_any_in_use = 0;
do {
do {
pVVar5 = (VagStrListNode*)0x0;
if (uVar7 < (u32)list->elt_count) {
pVVar5 = (VagStrListNode*)list->next;
for (uVar2 = uVar7; uVar2 != 0; uVar2 = uVar2 - 1) {
pVVar5 = (VagStrListNode*)(pVVar5->list).next;
}
}
uVar7 = uVar7 + 1;
if (pVVar5 == (VagStrListNode*)0x0) {
uVar6 = 4;
goto LAB_0000ef9c;
}
} while (pVVar5->id == 0);
pRVar3 = FindThisVagStream(pVVar5->name, pVVar5->id);
if (pRVar3 == 0x0) {
pRVar3 = FindThisVagStream(pVVar5->name, pVVar5->id);
if (pRVar3 != 0x0)
goto LAB_0000ef9c;
InsertVagStreamInList(pVVar5, &NewStreamsList);
uVar6 = uVar6 + 1;
} else {
pRVar3->sb_scanned = '\x01';
if (pRVar3->stereo_sibling != 0x0) {
pRVar3->stereo_sibling->sb_scanned = '\x01';
}
if (pVVar5->prio == pRVar3->priority) {
LAB_0000ef9c:
uVar6 = uVar6 + 1;
} else {
SetNewVagCmdPri(pRVar3, pVVar5->prio, 1);
uVar6 = uVar6 + 1;
}
}
if (3 < uVar6) {
return;
}
} while (true);
}
void CheckPlayList(List* param_1) {
VagCmd* pRVar1;
int iVar2;
VagStrListNode* pLVar3;
VagStrListNode* pVVar3;
VagStrListNode* pVVar4;
u32 uVar5;
u32 uVar6;
VagStrListNode* pLVar7;
int iVar7;
iVar7 = param_1->elt_count;
pLVar7 = (VagStrListNode*)param_1->next;
joined_r0x0000f00c:
do {
while (true) {
if (iVar7 == 0) {
return;
}
iVar7 = iVar7 + -1;
if (pLVar7->id != 0)
break;
pLVar7 = (VagStrListNode*)(pLVar7->list).next;
}
pRVar1 = FindThisVagStream(pLVar7->name, pLVar7->id);
} while (pRVar1 == 0x0);
uVar5 = 0;
if (pRVar1->byte4 == '\0')
goto code_r0x0000f058;
uVar6 = param_1->elt_count;
pVVar3 = (VagStrListNode*)param_1->next;
pVVar4 = (VagStrListNode*)0x0;
if (uVar6 != 0) {
do {
if ((pVVar3->id == pLVar7->id) &&
(iVar2 = strncmp(pVVar3->name, pLVar7->name, 0x30), iVar2 == 0)) {
pVVar4 = pVVar3;
uVar5 = uVar6;
}
pVVar3 = (VagStrListNode*)(pVVar3->list).next;
uVar5 = uVar5 + 1;
} while (uVar5 < uVar6);
}
goto LAB_0000f144;
code_r0x0000f058:
if (((pRVar1->sb_playing != '\0') || (pRVar1->byte6 != '\0')) && (pRVar1->byte23 == '\0')) {
IsoPlayVagStream(pRVar1, 1);
uVar5 = 0;
uVar6 = param_1->elt_count;
pLVar3 = (VagStrListNode*)param_1->next;
pVVar4 = (VagStrListNode*)0x0;
if (uVar6 != 0) {
do {
if ((pLVar3->id == pLVar7->id) &&
(iVar2 = strncmp(pLVar3->name, pLVar7->name, 0x30), iVar2 == 0)) {
pVVar4 = pLVar3;
uVar5 = uVar6;
}
pLVar3 = (VagStrListNode*)(pLVar3->list).next;
uVar5 = uVar5 + 1;
} while (uVar5 < uVar6);
}
LAB_0000f144:
if (pVVar4 != (VagStrListNode*)0x0) {
(pVVar4->list).in_use = 0;
strncpy(pVVar4->name, "free", 0x30);
pVVar4->id = 0;
pVVar4->unk_72 = 0;
pVVar4->prio = 0;
pVVar4->unk_76 = 0;
pVVar4->unk_80 = 0;
pVVar4->unk_88 = 0;
pVVar4->unk_92 = 0;
pVVar4->vol_multiplier = 0;
pVVar4->unk_100 = 0;
param_1->maybe_any_in_use = 1;
}
}
goto joined_r0x0000f00c;
}
u32 StreamListThread() {
int iVar1;
int iVar2;
VagStrListNode* pVVar3;
// undefined4* puVar4;
VagStrListNode* pVVar5;
VagStrListNode* pLVar6;
VagStrListNode* pvVar6;
u32 uVar7;
u32 uVar8;
VagStrListNode* pLVar10;
u32 uVar9;
do {
do {
SleepThread();
} while (RequestedStreamsList.unk2_init0 != 0);
uVar8 = 0;
WaitSema(RequestedStreamsList.sema);
iVar1 = RequestedStreamsList.elt_count;
if (RequestedStreamsList.elt_count != 0) {
// puVar4 = (undefined4*)((int)RequestedStreamsList.buffer + 8);
pvVar6 = (VagStrListNode*)RequestedStreamsList.buffer;
do {
strncpy(pvVar6->name, "free", 0x30);
// puVar4[0xe] = 0;
pvVar6->id = 0;
// puVar4[0x10] = 0;
pvVar6->unk_72 = 0;
// puVar4[0x13] = 0;
pvVar6->prio = 0;
// puVar4[0x11] = 0;
pvVar6->unk_76 = 0;
// puVar4[0x12] = 0;
pvVar6->unk_80 = 0;
// puVar4[0x14] = 0;
pvVar6->unk_88 = 0;
// puVar4[0x15] = 0;
pvVar6->unk_92 = 0;
// puVar4[0x16] = 0;
pvVar6->vol_multiplier = 0;
// puVar4[0x17] = 0;
pvVar6->unk_100 = 0;
// *puVar4 = 0;
pvVar6->list.in_use = 0;
// puVar4 = puVar4 + 0x1a;
uVar8 = uVar8 + 1;
pvVar6++;
// pvVar6 = (void*)((int)pvVar6 + 0x68);
} while (uVar8 < (u32)iVar1);
}
uVar8 = 0;
RequestedStreamsList.maybe_any_in_use = 0;
uVar9 = 0;
WaitSema(PluginStreamsList.sema);
LAB_0000f2c4:
do {
iVar1 = RequestedStreamsList.elt_count;
pVVar3 = (VagStrListNode*)0x0;
uVar7 = uVar8;
pVVar5 = (VagStrListNode*)PluginStreamsList.next;
if (uVar8 < (u32)PluginStreamsList.elt_count) {
for (; pVVar3 = pVVar5, uVar7 != 0; uVar7 = uVar7 - 1) {
pVVar5 = (VagStrListNode*)(pVVar3->list).next;
}
}
uVar8 = uVar8 + 1;
if (pVVar3 != (VagStrListNode*)0x0) {
if (pVVar3->id == 0)
goto LAB_0000f2c4;
uVar7 = 0;
pLVar10 = (VagStrListNode*)0x0;
pVVar5 = (VagStrListNode*)RequestedStreamsList.next;
if (RequestedStreamsList.elt_count != 0) {
do {
if ((pVVar5->id == pVVar3->id) &&
(iVar2 = strncmp(pVVar5->name, pVVar3->name, 0x30), iVar2 == 0)) {
uVar7 = iVar1;
pLVar10 = pVVar5;
}
pVVar5 = (VagStrListNode*)(pVVar5->list).next;
uVar7 = uVar7 + 1;
} while (uVar7 < (u32)iVar1);
}
if (pLVar10 == (VagStrListNode*)0x0) {
InsertVagStreamInList(pVVar3, &RequestedStreamsList);
}
}
uVar9 = uVar9 + 1;
} while (uVar9 < 4);
uVar8 = 0;
SignalSema(PluginStreamsList.sema);
uVar9 = 0;
WaitSema(EEStreamsList.sema);
LAB_0000f3c0:
do {
iVar1 = RequestedStreamsList.elt_count;
pVVar3 = (VagStrListNode*)0x0;
uVar7 = uVar8;
pVVar5 = (VagStrListNode*)EEStreamsList.next;
if (uVar8 < (u32)EEStreamsList.elt_count) {
for (; pVVar3 = pVVar5, uVar7 != 0; uVar7 = uVar7 - 1) {
pVVar5 = (VagStrListNode*)(pVVar3->list).next;
}
}
uVar8 = uVar8 + 1;
if (pVVar3 != (VagStrListNode*)0x0) {
if (pVVar3->id == 0)
goto LAB_0000f3c0;
uVar7 = 0;
pVVar5 = (VagStrListNode*)0x0;
pLVar6 = (VagStrListNode*)RequestedStreamsList.next;
if (RequestedStreamsList.elt_count != 0) {
do {
if ((pLVar6->id == pVVar3->id) &&
(iVar2 = strncmp(pLVar6->name, pVVar3->name, 0x30), iVar2 == 0)) {
uVar7 = iVar1;
pVVar5 = pLVar6;
}
pLVar6 = (VagStrListNode*)(pLVar6->list).next;
uVar7 = uVar7 + 1;
} while (uVar7 < (u32)iVar1);
}
if (pVVar5 == (VagStrListNode*)0x0) {
InsertVagStreamInList(pVVar3, &RequestedStreamsList);
}
}
uVar9 = uVar9 + 1;
} while (uVar9 < 4);
SignalSema(EEStreamsList.sema);
RequestedStreamsList.unk2_init0 = 1;
SignalSema(RequestedStreamsList.sema);
WaitSema(EEPlayList.sema);
CheckPlayList(&EEPlayList);
SignalSema(EEPlayList.sema);
WaitSema(LfoList.sema);
CheckLfoList(&LfoList);
SignalSema(LfoList.sema);
} while (true);
return 0;
}
bool InitVagStreamList(List* param_1, u32 param_2, const char* param_3) {
// uint8_t* piVar1;
u32 uVar1;
VagStrListNode* pLVar3;
strncpy(param_1->name, param_3, 8);
InitList(param_1, param_2, sizeof(VagStrListNode));
pLVar3 = (VagStrListNode*)param_1->next;
uVar1 = 0;
if (param_2 != 0) {
// piVar1 = (uint8_t*)&pLVar3->unk_100;
do {
// *(undefined4*)(piVar1 + -0x5c) = 0;
pLVar3->list.in_use = 0;
strncpy(pLVar3->name, "free", 0x30);
// *(undefined4*)(piVar1 + -0x24) = 0;
pLVar3->id = 0;
// *(undefined4*)(piVar1 + -0x1c) = 0;
pLVar3->unk_72 = 0;
// *(undefined4*)(piVar1 + -0x10) = 0;
pLVar3->prio = 0;
// *(undefined4*)(piVar1 + -0x18) = 0;
pLVar3->unk_76 = 0;
// *(undefined4*)(piVar1 + -0x14) = 0;
pLVar3->unk_80 = 0;
// *(undefined4*)(piVar1 + -0xc) = 0;
pLVar3->unk_88 = 0;
// *(undefined4*)(piVar1 + -8) = 0;
pLVar3->unk_92 = 0;
// *(undefined4*)(piVar1 + -4) = 0;
pLVar3->vol_multiplier = 0;
// *(undefined4*)piVar1 = 0;
pLVar3->unk_100 = 0;
// piVar1 = piVar1 + 0x68;
uVar1 = uVar1 + 1;
pLVar3 = pLVar3 + 1;
} while (uVar1 < param_2);
}
return param_1->buffer != (void*)0x0;
}
VagStrListNode* FindVagStreamInList(VagStrListNode* param_1, List* param_2) {
int iVar1;
VagStrListNode* pLVar2;
u32 uVar2;
u32 uVar3;
VagStrListNode* pVVar4;
uVar2 = 0;
uVar3 = param_2->elt_count;
pLVar2 = (VagStrListNode*)param_2->next;
pVVar4 = (VagStrListNode*)0x0;
if (uVar3 != 0) {
do {
if ((pLVar2->id == param_1->id) &&
(iVar1 = strncmp(pLVar2->name, param_1->name, 0x30), iVar1 == 0)) {
uVar2 = uVar3;
pVVar4 = pLVar2;
}
pLVar2 = (VagStrListNode*)(pLVar2->list).next;
uVar2 = uVar2 + 1;
} while (uVar2 < uVar3);
}
return pVVar4;
}
ListNode* GetVagStreamInList(u32 param_1, List* param_2) {
ListNode* pLVar1;
pLVar1 = (ListNode*)0x0;
if ((param_1 < (u32)param_2->elt_count) && (pLVar1 = param_2->next, param_1 != 0)) {
do {
pLVar1 = pLVar1->next;
param_1 = param_1 - 1;
} while (param_1 != 0);
}
return pLVar1;
}
void RemoveVagStreamFromList(VagStrListNode* param_1, List* param_2) {
int iVar1;
VagStrListNode* pLVar2;
VagStrListNode* pVVar2;
u32 uVar3;
u32 uVar4;
uVar3 = 0;
uVar4 = param_2->elt_count;
pLVar2 = (VagStrListNode*)param_2->next;
pVVar2 = (VagStrListNode*)0x0;
if (uVar4 != 0) {
do {
if ((pLVar2->id == param_1->id) &&
(iVar1 = strncmp(pLVar2->name, param_1->name, 0x30), iVar1 == 0)) {
pVVar2 = pLVar2;
uVar3 = uVar4;
}
pLVar2 = (VagStrListNode*)(pLVar2->list).next;
uVar3 = uVar3 + 1;
} while (uVar3 < uVar4);
}
if (pVVar2 != (VagStrListNode*)0x0) {
(pVVar2->list).in_use = 0;
strncpy(pVVar2->name, "free", 0x30);
pVVar2->id = 0;
pVVar2->unk_72 = 0;
pVVar2->prio = 0;
pVVar2->unk_76 = 0;
pVVar2->unk_80 = 0;
pVVar2->unk_88 = 0;
pVVar2->unk_92 = 0;
pVVar2->vol_multiplier = 0;
pVVar2->unk_100 = 0;
param_2->maybe_any_in_use = 1;
}
}
void EmptyVagStreamList(List* param_1) {
// undefined4 *puVar1;
VagStrListNode* pvVar2;
u32 uVar3;
u32 uVar4;
uVar4 = param_1->elt_count;
pvVar2 = (VagStrListNode*)param_1->buffer;
uVar3 = 0;
if (uVar4 != 0) {
// puVar1 = (undefined4 *)((int)pvVar2 + 8);
do {
strncpy(pvVar2->name, "free", 0x30);
// puVar1[0xe] = 0;
// puVar1[0x10] = 0;
// puVar1[0x13] = 0;
// puVar1[0x11] = 0;
// puVar1[0x12] = 0;
// puVar1[0x14] = 0;
// puVar1[0x15] = 0;
// puVar1[0x16] = 0;
// puVar1[0x17] = 0;
// *puVar1 = 0;
// puVar1 = puVar1 + 0x1a;
pvVar2->id = 0;
pvVar2->unk_72 = 0;
pvVar2->prio = 0;
pvVar2->unk_76 = 0;
pvVar2->unk_80 = 0;
pvVar2->unk_88 = 0;
pvVar2->unk_92 = 0;
pvVar2->vol_multiplier = 0;
pvVar2->unk_100 = 0;
uVar3 = uVar3 + 1;
pvVar2++;
} while (uVar3 < uVar4);
}
param_1->maybe_any_in_use = 1;
}
void MergeVagStreamLists(List* param_1, List* param_2) {
u32 uVar1;
int iVar2;
VagStrListNode* pVVar3;
VagStrListNode* pLVar4;
u32 uVar4;
VagStrListNode* pVVar5;
u32 uVar6;
u32 uVar7;
uVar6 = 0;
uVar7 = 0;
do {
do {
pVVar3 = (VagStrListNode*)0x0;
if (uVar6 < (u32)param_1->elt_count) {
pVVar3 = (VagStrListNode*)param_1->next;
for (uVar1 = uVar6; uVar1 != 0; uVar1 = uVar1 - 1) {
pVVar3 = (VagStrListNode*)(pVVar3->list).next;
}
}
uVar6 = uVar6 + 1;
if (pVVar3 == (VagStrListNode*)0x0)
goto LAB_0000f930;
} while (pVVar3->id == 0);
uVar4 = param_2->elt_count;
uVar1 = 0;
pLVar4 = (VagStrListNode*)param_2->next;
pVVar5 = (VagStrListNode*)0x0;
if (uVar4 != 0) {
do {
if ((pLVar4->id == pVVar3->id) &&
(iVar2 = strncmp(pLVar4->name, pVVar3->name, 0x30), iVar2 == 0)) {
uVar1 = uVar4;
pVVar5 = pLVar4;
}
pLVar4 = (VagStrListNode*)(pLVar4->list).next;
uVar1 = uVar1 + 1;
} while (uVar1 < uVar4);
}
if (pVVar5 == (VagStrListNode*)0x0) {
InsertVagStreamInList(pVVar3, param_2);
}
LAB_0000f930:
uVar7 = uVar7 + 1;
if (3 < uVar7) {
return;
}
} while (true);
}
} // namespace jak2
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include "game/overlord/jak2/list.h"
namespace jak2 {
struct VagStrListNode {
ListNode list;
char name[48];
int unk_60;
int id;
int unk_68;
int unk_72;
int unk_76;
int unk_80;
int prio;
int unk_88;
int unk_92;
int vol_multiplier;
int unk_100;
};
struct LfoListNode {
ListNode list;
int unk_12;
int unk_16;
int unk_20;
int unk_24;
int unk_28;
int unk_32;
int id;
int plugin_id;
};
extern List PluginStreamsList;
extern List LfoList;
extern List EEPlayList;
extern List RequestedStreamsList;
extern List NewStreamsList;
extern List EEStreamsList;
void init_globals_streamlist();
void RemoveVagStreamFromList(VagStrListNode* param_1, List* param_2);
void EmptyVagStreamList(List* param_1);
VagStrListNode* InsertVagStreamInList(VagStrListNode* param_1, List* param_2);
VagStrListNode* FindVagStreamInList(VagStrListNode* param_1, List* param_2);
void QueueNewStreamsFromList(List* list);
bool InitVagStreamList(List* param_1, u32 param_2, const char* param_3);
u32 StreamListThread();
void RemoveLfoStreamFromList(void*, void*);
} // namespace jak2
File diff suppressed because it is too large Load Diff
+145
View File
@@ -0,0 +1,145 @@
#pragma once
#include "common/common_types.h"
#include "game/overlord/common/iso.h"
#include "game/overlord/common/ssound.h"
#include "game/overlord/jak2/iso.h"
#include "game/overlord/jak2/list.h"
namespace jak2 {
enum VagCmdByte {
// BYTE1 = 1, // init to 0 (playing?)
// PAUSED = 2, // init to 1 (paused?)
BYTE4 = 4, // streaming?
BYTE5 = 5, // init to 0 (maybe something to do with setting sdsetswitch stuff.
BYTE6 = 6, // init to 0
// 8
// 9
BYTE10 = 10, // 10
BYTE11 = 11, // try not to interrupt this one with SmartAllocVagCmd.
// 12
// 13
// 14
// 15
BYTE23_NOSTART = 23, // start??
};
struct VagCmd {
CmdHeader header; // 0 to ??
FileRecord* file_record; // 40
VagDirEntry* vag_dir_entry; // 44
VagCmd* stereo_sibling; // 48
u8* dma_iop_mem_ptr; // 52
int dma_chan; // 56 - which spu dma channel to use. Not needed for PC.
int safe_to_change_dma_fields; // 60 - set to 0 when DMA is processing data from this command.
int spu_addr_to_start_playing; // 64 - address to use when starting stream.
char name[48]; // 68
int spu_stream_dma_mem_addr; // 120 - address we should DMA to (or just DMA'd to)
int spu_trap_mem_addr; // 124
int voice; // 128
int idx_in_cmd_arr; // 132 (index in VagCmds)
int unk_136; // 136
int unk_140; // 140
int unk_176; // 176
int unk_180; // 180
int unk_184; // 184
int unk_188; // 188
int unk_192; // 192 pitch ramping stuff maybe
int unk_196; // 196
int unk_200; // 200 pos
int unk_204; // 204
union {
u8 status_bytes[24]; // 208
struct {
u8 byte0;
u8 sb_playing;
u8 sb_paused;
u8 byte3;
u8 byte4;
u8 byte5;
u8 byte6;
u8 sb_scanned;
u8 byte8;
u8 byte9;
u8 byte10;
u8 byte11;
u8 byte12;
u8 byte13;
u8 byte14;
u8 byte15;
u8 byte16;
u8 byte17;
u8 sb_odd_buffer_dma_complete;
u8 sb_even_buffer_dma_complete; // set when even buffer's spu dma finishes
u8 byte20;
u8 byte21;
u8 byte22;
u8 byte23;
};
};
u8 unk_232; // 232 wtf is this
int unk_236; // 236
int num_processed_chunks; // 240 (where "processed" means that they were added to dma command)
int xfer_size; // 244
int unk_248; // 248
int pitch1; // 252
int unk_256_pitch2; // 256
int unk_260; // 260
int unk_264; // 264 (init to 0x4000)
int unk_268; // 268
int vol_multiplier; // 272
int id; // 276
int plugin_id; // 280
int priority; // 284
int unk_288; // 288
int unk_292; // 292
int unk_296; // 296
Vec3w vec3; // 300
int fo_min; // 312 (init to 5)
int fo_max; // 316 (init to 316)
int fo_curve; // 320 (init to 1)
};
struct VagCmdPriListEntry {
VagCmd* cmds[4];
};
constexpr int N_VAG_CMDS = 4;
extern VagCmd VagCmds[N_VAG_CMDS];
extern int StreamSRAM[N_VAG_CMDS];
extern int TrapSRAM[N_VAG_CMDS];
extern int StreamVoice[N_VAG_CMDS];
extern int ActiveVagStreams;
extern int MasterVolume[17];
void vag_init_globals();
VagCmd* FindThisVagStream(const char* name, int id);
VagCmd* FindNotQueuedVagCmd();
int CalculateVAGPitch(int param_1, int param_2);
void UnPauseVAG(VagCmd* param_1, int param_2);
int HowManyBelowThisPriority(int pri, int disable_intr);
VagCmd* SmartAllocVagCmd(VagCmd* cmd);
void InitVAGCmd(VagCmd* param_1, int param_2);
void RemoveVagCmd(VagCmd* cmd, int param_2);
void FreeVagCmd(VagCmd* cmd, int param_2);
void SetNewVagCmdPri(VagCmd* cmd, int new_pri, int param_3);
VagCmd* FindVagStreamName(const char* name);
void TerminateVAG(VagCmd* cmd, int param_2);
void PauseVAG(VagCmd* cmd, int param_2);
int AnyVagRunning();
void InitVagCmds();
void VAG_MarkLoopEnd(int8_t* data, int offset);
void VAG_MarkLoopStart(int8_t* param_1);
void RestartVag(VagCmd* param_1, int param_2, int param_3);
void SetVagStreamsNoStart(int param_1, int param_2);
void PauseVagStreams();
void UnPauseVagStreams();
VagCmd* FindVagStreamId(int id);
void SetVAGVol(VagCmd* cmd, int param_2);
void SetAllVagsVol(int vol);
void SetVagStreamsNotScanned();
} // namespace jak2
-258
View File
@@ -1,258 +0,0 @@
#pragma once
#include "ssound.h"
#include "common/common_types.h"
void srpc_init_globals();
extern const char* gLanguage;
extern s32 gVAG_Id;
constexpr int MUSIC_TWEAK_COUNT = 32;
struct MusicTweaks {
u32 TweakCount;
struct {
char MusicName[12];
s32 VolumeAdjust;
} MusicTweak[MUSIC_TWEAK_COUNT];
};
enum class Jak1SoundCommand : u16 {
LOAD_BANK = 0,
LOAD_MUSIC = 1,
UNLOAD_BANK = 2,
PLAY = 3,
PAUSE_SOUND = 4,
STOP_SOUND = 5,
CONTINUE_SOUND = 6,
SET_PARAM = 7,
SET_MASTER_VOLUME = 8,
PAUSE_GROUP = 9,
STOP_GROUP = 10,
CONTINUE_GROUP = 11,
GET_IRX_VERSION = 12,
SET_FALLOFF_CURVE = 13,
SET_SOUND_FALLOFF = 14,
RELOAD_INFO = 15,
SET_LANGUAGE = 16,
SET_FLAVA = 17,
SET_REVERB = 18,
SET_EAR_TRANS = 19,
SHUTDOWN = 20,
LIST_SOUNDS = 21,
UNLOAD_MUSIC = 22,
MIRROR_MODE = 201,
};
enum class Jak2SoundCommand : u16 {
iop_store = 0,
iop_free = 1,
load_bank = 2,
load_bank_from_iop = 3,
load_bank_from_ee = 4,
load_music = 5,
unload_bank = 6,
play = 7,
pause_sound = 8,
stop_sound = 9,
continue_sound = 10,
set_param = 11,
set_master_volume = 12,
pause_group = 13,
stop_group = 14,
continue_group = 15,
get_irx_version = 16,
set_falloff_curve = 17,
set_sound_falloff = 18,
reload_info = 19,
set_language = 20,
set_flava = 21,
set_midi_reg = 22,
set_reverb = 23,
set_ear_trans = 24,
shutdown = 25,
list_sounds = 26,
unload_music = 27,
set_fps = 28,
boot_load = 29,
game_load = 30,
num_tests = 31,
num_testruns = 32,
num_sectors = 33,
num_streamsectors = 34,
num_streambanks = 35,
track_pitch = 36,
linvel_nom = 37,
linvel_stm = 38,
seek_nom = 39,
seek_stm = 40,
read_seq_nom = 41,
read_seq_stm = 42,
read_spr_nom = 43,
read_spr_stm = 44,
read_spr_strn_nom = 45,
rand_stm_abort = 46,
rand_nom_abort = 47,
iop_mem = 48,
cancel_dgo = 49,
set_stereo_mode = 50,
};
struct SoundRpcGetIrxVersion {
u32 major;
u32 minor;
u32 ee_addr;
};
struct SoundRpcBankCommand {
u8 pad[12];
char bank_name[16];
};
struct SoundRpcSetLanguageCommand {
u32 langauge_id; // game_common_types.h, Language
};
struct SoundRpcPlayCommand {
u32 sound_id;
u32 pad[2];
char name[16];
SoundParams parms;
};
struct SoundRpcSetParamCommand {
u32 sound_id;
SoundParams parms;
s32 auto_time;
s32 auto_from;
};
struct SoundRpcSoundIdCommand {
u32 sound_id;
};
struct SoundRpcSetFlavaCommand {
u8 flava;
};
struct SoundRpcSetReverb {
u8 core;
s32 reverb;
u32 left;
u32 right;
};
struct SoundRpcSetEarTrans {
Vec3w ear_trans;
Vec3w cam_trans;
s32 cam_angle;
};
struct SoundRpc2SetEarTrans {
Vec3w ear_trans1;
Vec3w ear_trans0;
Vec3w cam_trans;
s32 cam_angle;
};
struct SoundRpcSetFPSCommand {
u8 fps;
};
struct SoundRpcSetFallof {
u8 pad[12];
char name[16];
s32 curve;
s32 min;
s32 max;
};
struct SoundRpcSetFallofCurve {
s32 curve;
s32 falloff;
s32 ease;
};
struct SoundRpcGroupCommand {
u8 group;
};
struct SoundRpcMasterVolCommand {
SoundRpcGroupCommand group;
s32 volume;
};
struct SoundRpcStereoMode {
s32 stereo_mode;
};
struct SoundRpcSetMidiReg {
s32 reg;
s32 value;
};
struct SoundRpcSetMirrror {
u8 value;
};
struct SoundRpcCommand {
u16 rsvd1;
union {
Jak1SoundCommand j1command;
Jak2SoundCommand j2command;
};
union {
SoundRpcGetIrxVersion irx_version;
SoundRpcBankCommand load_bank;
SoundRpcSetLanguageCommand set_language;
SoundRpcPlayCommand play;
SoundRpcSoundIdCommand sound_id;
SoundRpcSetFPSCommand fps;
SoundRpcSetEarTrans ear_trans;
SoundRpc2SetEarTrans ear_trans_j2;
SoundRpcSetReverb reverb;
SoundRpcSetFallof fallof;
SoundRpcSetFallofCurve fallof_curve;
SoundRpcGroupCommand group;
SoundRpcSetFlavaCommand flava;
SoundRpcMasterVolCommand master_volume;
SoundRpcSetParamCommand param;
SoundRpcStereoMode stereo_mode;
SoundRpcSetMidiReg midi_reg;
SoundRpcSetMirrror mirror;
u8 max_size[0x4C]; // Temporary
};
};
static_assert(sizeof(SoundRpcCommand) == 0x50);
struct SoundIopInfo {
u32 frame;
s32 strpos;
u32 std_id;
u32 freemem;
u8 chinfo[48];
u32 freemem2;
u32 nocd;
u32 dirtycd;
u32 diskspeed[2];
u32 lastspeed;
s32 dupseg;
u32 times[41];
u32 times_seq;
u8 pad[10]; // pad up to transfer size
};
extern MusicTweaks gMusicTweakInfo;
extern s32 gMusicTweak;
u32 Thread_Loader();
u32 Thread_Player();
s32 VBlank_Handler(void*);
// added for PC port
extern u32 gMusicFadeHack;
-374
View File
@@ -1,374 +0,0 @@
/*!
* @file stream.cpp
* OVERLORD streaming driver.
* Supports loading a file directly to the EE, or loading chunks of a chunked file.
*/
#include "stream.h"
#include <cstring>
#include "common/util/Assert.h"
#include "game/common/play_rpc_types.h"
#include "game/common/str_rpc_types.h"
#include "game/overlord/iso.h"
#include "game/overlord/iso_api.h"
#include "game/overlord/isocommon.h"
#include "game/overlord/srpc.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
using namespace iop;
static RPC_Str_Cmd_Jak1 sSTRBufJak1;
static RPC_Str_Cmd_Jak2 sSTRBufJak2;
static RPC_Play_Cmd_Jak1 sPLAYBufJak1[2];
static RPC_Play_Cmd_Jak2 sPLAYBufJak2[2];
void* RPC_STR_jak1(unsigned int fno, void* _cmd, int y);
void* RPC_STR_jak2(unsigned int fno, void* _cmd, int y);
void* RPC_PLAY_jak1(unsigned int fno, void* _cmd, int y);
void* RPC_PLAY_jak2(unsigned int fno, void* _cmd, int y);
static constexpr int PLAY_MSG_SIZE = 0x40;
static u32 global_vag_count = 0;
/*!
* 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 CacheEntryJ1 {
// 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.
StrFileHeaderJ1 header;
};
struct CacheEntryJ2 {
FileRecord* fr = nullptr;
s32 countdown = 0;
StrFileHeaderJ2 header;
};
// the actual header cache.
constexpr int STR_INDEX_CACHE_SIZE = 4;
CacheEntryJ1 sCacheJ1[STR_INDEX_CACHE_SIZE];
CacheEntryJ2 sCacheJ2[STR_INDEX_CACHE_SIZE];
void stream_init_globals() {
memset(&sSTRBufJak1, 0, sizeof(RPC_Str_Cmd_Jak1));
memset(&sSTRBufJak2, 0, sizeof(RPC_Str_Cmd_Jak2));
memset(&sPLAYBufJak1, 0, sizeof(RPC_Play_Cmd_Jak1) * 2);
memset(&sPLAYBufJak2, 0, sizeof(RPC_Play_Cmd_Jak2) * 2);
}
/*!
* Run the STR RPC handler.
*/
u32 STRThread() {
sceSifQueueData dq;
sceSifServeData serve;
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
if (g_game_version == GameVersion::Jak1) {
sceSifRegisterRpc(&serve, STR_RPC_ID[g_game_version], RPC_STR_jak1, &sSTRBufJak1, nullptr,
nullptr, &dq);
} else if (g_game_version == GameVersion::Jak2) {
sceSifRegisterRpc(&serve, STR_RPC_ID[g_game_version], RPC_STR_jak2, &sSTRBufJak2, nullptr,
nullptr, &dq);
} else {
ASSERT_MSG(false, "unsupported game version in STRThread initialization!");
}
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
u32 PLAYThread() {
sceSifQueueData dq;
sceSifServeData serve;
CpuDisableIntr();
sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId());
if (g_game_version == GameVersion::Jak1) {
sceSifRegisterRpc(&serve, PLAY_RPC_ID[g_game_version], RPC_PLAY_jak1, sPLAYBufJak1, nullptr,
nullptr, &dq);
} else if (g_game_version == GameVersion::Jak2) {
sceSifRegisterRpc(&serve, PLAY_RPC_ID[g_game_version], RPC_PLAY_jak2, sPLAYBufJak2, nullptr,
nullptr, &dq);
} else {
ASSERT_MSG(false, "unsupported game version in PLAYThread initialization!");
}
CpuEnableIntr();
sceSifRpcLoop(&dq);
return 0;
}
/*!
* The STR RPC handler.
*/
void* RPC_STR_jak1(unsigned int fno, void* _cmd, int y) {
(void)fno;
(void)y;
auto* cmd = (RPC_Str_Cmd_Jak1*)_cmd;
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 && sCacheJ1[cache_entry].fr != file_record) {
sCacheJ1[cache_entry].countdown--;
if (sCacheJ1[cache_entry].countdown < oldest) {
oldest_idx = cache_entry;
oldest = sCacheJ1[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;
sCacheJ1[oldest_idx].fr = file_record;
sCacheJ1[oldest_idx].countdown = INT32_MAX - 1;
if (!LoadISOFileToIOP(file_record, &sCacheJ1[oldest_idx].header, sizeof(StrFileHeaderJ1))) {
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,
sCacheJ1[cache_entry].header.sizes[cmd->chunk_id],
sCacheJ1[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 = sCacheJ1[cache_entry].header.sizes[cmd->chunk_id];
cmd->result = 0;
}
}
}
return cmd;
}
/*!
* The STR RPC handler.
*/
void* RPC_STR_jak2(unsigned int fno, void* _cmd, int y) {
(void)fno;
(void)y;
auto* cmd = (RPC_Str_Cmd_Jak2*)_cmd;
if (cmd->section < 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->basename);
if (file_record == nullptr) {
// file not found!
printf("[OVERLORD STR] Failed to find file %s for loading.\n", cmd->basename);
cmd->result = STR_RPC_RESULT_ERROR;
} else {
// load directly to the EE
cmd->maxlen = LoadISOFileToEE(file_record, cmd->address, cmd->maxlen);
if (cmd->maxlen) {
// 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->basename);
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 (%s)\n", cmd->basename,
animation_iso_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 && sCacheJ2[cache_entry].fr != file_record) {
sCacheJ2[cache_entry].countdown--;
if (sCacheJ2[cache_entry].countdown < oldest) {
oldest_idx = cache_entry;
oldest = sCacheJ2[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;
sCacheJ2[oldest_idx].fr = file_record;
sCacheJ2[oldest_idx].countdown = INT32_MAX - 1;
if (!LoadISOFileToIOP(file_record, &sCacheJ2[oldest_idx].header, sizeof(StrFileHeaderJ2))) {
printf("[OVERLORD STR] Failed to load chunk file header for animation %s\n",
cmd->basename);
cmd->result = 1;
return cmd;
}
}
// load data, using the cached header to find the location of the chunk.
if (!LoadISOFileChunkToEE(file_record, cmd->address,
sCacheJ2[cache_entry].header.sizes[cmd->section],
sCacheJ2[cache_entry].header.sectors[cmd->section])) {
printf("[OVERLORD STR] Failed to load chunk %d for animation %s\n", cmd->section,
cmd->basename);
cmd->result = 1;
} else {
// successful load!
cmd->maxlen = sCacheJ2[cache_entry].header.sizes[cmd->section];
cmd->result = 0;
}
}
}
return cmd;
}
void* RPC_PLAY_jak1([[maybe_unused]] unsigned int fno, void* _cmd, int size) {
s32 n_messages = size / PLAY_MSG_SIZE;
char namebuf[16];
auto* cmd = (RPC_Play_Cmd_Jak1*)(_cmd);
while (n_messages > 0) {
if (cmd->name[0] == '$') {
char* name_part = &cmd->name[1];
size_t name_len = strlen(name_part);
if (name_len < 9) {
memset(namebuf, ' ', 8);
memcpy(namebuf, name_part, name_len);
} else {
memcpy(namebuf, name_part, 8);
}
// ASCII toupper
for (int i = 0; i < 8; i++) {
if (namebuf[i] >= 0x61 && namebuf[i] < 0x7b) {
namebuf[i] -= 0x20;
}
}
} else {
ISONameFromAnimationName(namebuf, cmd->name);
}
auto vag = FindVAGFile(namebuf);
memcpy(namebuf, "VAGWAD ", 8);
strcpy(&namebuf[8], gLanguage);
FileRecord* file = nullptr;
global_vag_count = (global_vag_count + 1) & 0x3f;
if (!cmd->result && global_vag_count == 0) {
namebuf[0] -= 3;
file = isofs->find_in(namebuf);
namebuf[0] += 3;
}
file = isofs->find_in(namebuf);
if (cmd->result == 0) {
PlayVAGStream(file, vag, cmd->address, 0x400, 1, nullptr);
} else if (cmd->result == 1) {
StopVAGStream(vag, 1);
} else {
QueueVAGStream(file, vag, 0, 1);
}
n_messages--;
cmd++;
}
return _cmd;
}
/*!
* This is just copied from Jak 1, and is totally wrong for jak 2.
* It does nothing.
*/
void* RPC_PLAY_jak2([[maybe_unused]] unsigned int fno, void* _cmd, int size) {
s32 n_messages = size / PLAY_MSG_SIZE;
char namebuf[16];
auto* cmd = (RPC_Play_Cmd_Jak2*)(_cmd);
while (n_messages > 0) {
if (cmd->names[0].chars[0] == '$') {
char* name_part = &cmd->names[0].chars[1];
size_t name_len = strlen(name_part);
if (name_len < 9) {
memset(namebuf, ' ', 8);
memcpy(namebuf, name_part, name_len);
} else {
memcpy(namebuf, name_part, 8);
}
// ASCII toupper
for (int i = 0; i < 8; i++) {
if (namebuf[i] >= 0x61 && namebuf[i] < 0x7b) {
namebuf[i] -= 0x20;
}
}
} else {
ISONameFromAnimationName(namebuf, cmd->names[0].chars);
}
n_messages--;
cmd++;
}
return _cmd;
}
+115 -44
View File
@@ -24,6 +24,7 @@
#include "runtime.h"
#include "common/cross_os_debug/xdbg.h"
#include "common/global_profiler/GlobalProfiler.h"
#include "common/goal_constants.h"
#include "common/log/log.h"
#include "common/util/FileUtil.h"
@@ -47,17 +48,30 @@
#include "game/kernel/jak2/kboot.h"
#include "game/kernel/jak2/klisten.h"
#include "game/kernel/jak2/kscheme.h"
#include "game/overlord/dma.h"
#include "game/overlord/fake_iso.h"
#include "game/overlord/iso.h"
#include "game/overlord/iso_cd.h"
#include "game/overlord/iso_queue.h"
#include "game/overlord/overlord.h"
#include "game/overlord/ramdisk.h"
#include "game/overlord/sbank.h"
#include "game/overlord/srpc.h"
#include "game/overlord/ssound.h"
#include "game/overlord/stream.h"
#include "game/overlord/common/fake_iso.h"
#include "game/overlord/common/iso.h"
#include "game/overlord/common/sbank.h"
#include "game/overlord/common/srpc.h"
#include "game/overlord/common/ssound.h"
#include "game/overlord/jak1/dma.h"
#include "game/overlord/jak1/fake_iso.h"
#include "game/overlord/jak1/iso.h"
#include "game/overlord/jak1/iso_queue.h"
#include "game/overlord/jak1/overlord.h"
#include "game/overlord/jak1/ramdisk.h"
#include "game/overlord/jak1/srpc.h"
#include "game/overlord/jak1/ssound.h"
#include "game/overlord/jak1/stream.h"
#include "game/overlord/jak2/dma.h"
#include "game/overlord/jak2/iso_cd.h"
#include "game/overlord/jak2/iso_queue.h"
#include "game/overlord/jak2/overlord.h"
#include "game/overlord/jak2/spustreams.h"
#include "game/overlord/jak2/srpc.h"
#include "game/overlord/jak2/ssound.h"
#include "game/overlord/jak2/stream.h"
#include "game/overlord/jak2/streamlist.h"
#include "game/overlord/jak2/vag.h"
#include "game/system/Deci2Server.h"
#include "game/system/iop_thread.h"
#include "game/system/vm/dmac.h"
@@ -183,6 +197,10 @@ void ee_runner(SystemThreadInterface& iface) {
jak1::klisten_init_globals();
jak2::klisten_init_globals();
jak2::vag_init_globals();
jak2::init_globals_streamlist();
kmemcard_init_globals();
kprint_init_globals_common();
@@ -211,7 +229,9 @@ void ee_runner(SystemThreadInterface& iface) {
/*!
* SystemThread function for running the IOP (separate I/O Processor)
*/
void iop_runner(SystemThreadInterface& iface) {
void iop_runner(SystemThreadInterface& iface, GameVersion version) {
prof().root_event();
prof().begin_event("iop-init");
IOP iop;
lg::debug("[IOP] Restart!");
iop.reset_allocator();
@@ -219,26 +239,41 @@ void iop_runner(SystemThreadInterface& iface) {
iop::LIBRARY_register(&iop);
Gfx::register_vsync_callback([&iop]() { iop.kernel.signal_vblank(); });
// todo!
dma_init_globals();
iso_init_globals();
fake_iso_init_globals();
// iso_api
iso_cd_init_globals();
iso_queue_init_globals();
// isocommon
// overlord
ramdisk_init_globals();
sbank_init_globals();
// soundcommon
srpc_init_globals();
// ssound
stream_init_globals();
jak1::dma_init_globals();
jak2::dma_init_globals();
iso_init_globals();
jak1::iso_init_globals();
jak2::iso_init_globals();
fake_iso_init_globals();
jak1::fake_iso_init_globals();
jak2::iso_cd_init_globals();
jak1::iso_queue_init_globals();
jak2::iso_queue_init_globals();
jak2::spusstreams_init_globals();
jak1::ramdisk_init_globals();
sbank_init_globals();
// soundcommon
jak1::srpc_init_globals();
jak2::srpc_init_globals();
srpc_init_globals();
ssound_init_globals();
jak2::ssound_init_globals();
jak1::stream_init_globals();
jak2::stream_init_globals();
prof().end_event();
iface.initialization_complete();
lg::debug("[IOP] Wait for OVERLORD to start...");
iop.wait_for_overlord_start_cmd();
{
auto p = scoped_prof("iop-wait-for-ee");
iop.wait_for_overlord_start_cmd();
}
if (iop.status == IOP_OVERLORD_INIT) {
lg::debug("[IOP] Run!");
} else {
@@ -251,9 +286,26 @@ void iop_runner(SystemThreadInterface& iface) {
// init
bool complete = false;
start_overlord_wrapper(iop.overlord_argc, iop.overlord_argv, &complete); // todo!
while (complete == false) {
iop.wait_run_iop(iop.kernel.dispatch());
{
auto p = scoped_prof("overlord-start");
switch (version) {
case GameVersion::Jak1:
jak1::start_overlord_wrapper(iop.overlord_argc, iop.overlord_argv, &complete);
break;
case GameVersion::Jak2:
jak2::start_overlord_wrapper(iop.overlord_argc, iop.overlord_argv, &complete);
break;
default:
ASSERT_NOT_REACHED();
}
}
{
auto p = scoped_prof("overlord-wait-for-init");
while (complete == false) {
prof().root_event();
iop.kernel.dispatch();
}
}
// unblock the EE, the overlord is set up!
@@ -261,9 +313,14 @@ void iop_runner(SystemThreadInterface& iface) {
// IOP Kernel loop
while (!iface.get_want_exit() && !iop.want_exit) {
prof().root_event();
// The IOP scheduler informs us of how many microseconds are left until it has something to do.
// So we can wait for that long or until something else needs it to wake up.
iop.wait_run_iop(iop.kernel.dispatch());
auto wait_duration = iop.kernel.dispatch();
if (wait_duration &&
*wait_duration - std::chrono::steady_clock::now() > std::chrono::microseconds(100)) {
iop.wait_run_iop(*wait_duration);
}
}
Gfx::clear_vsync_callback();
@@ -275,8 +332,6 @@ void iop_runner(SystemThreadInterface& iface) {
*/
void null_runner(SystemThreadInterface& iface) {
iface.initialization_complete();
return;
}
/*!
@@ -303,8 +358,6 @@ void dmac_runner(SystemThreadInterface& iface) {
}
VM::unsubscribe_component();
return;
}
/*!
@@ -312,6 +365,7 @@ void dmac_runner(SystemThreadInterface& iface) {
* GOAL kernel arguments are currently ignored.
*/
RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const char** argv) {
prof().root_event();
g_argc = argc;
g_argv = argv;
g_main_thread_id = std::this_thread::get_id();
@@ -323,19 +377,26 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
// set up discord stuff
gStartTime = time(nullptr);
init_discord_rpc();
{
auto p = scoped_prof("init-discord");
init_discord_rpc();
}
// initialize graphics first - the EE code will upload textures during boot and we
// want the graphics system to catch them.
if (enable_display) {
auto p = scoped_prof("init-gfx");
Gfx::Init(g_game_version);
}
// step 1: sce library prep
iop::LIBRARY_INIT();
ee::LIBRARY_INIT_sceCd();
ee::LIBRARY_INIT_sceDeci2();
ee::LIBRARY_INIT_sceSif();
{
auto p = scoped_prof("init-library");
iop::LIBRARY_INIT();
ee::LIBRARY_INIT_sceCd();
ee::LIBRARY_INIT_sceDeci2();
ee::LIBRARY_INIT_sceSif();
}
// step 2: system prep
VM::vm_prepare(); // our fake ps2 VM needs to be prepared
@@ -346,10 +407,20 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
auto& vm_dmac_thread = tm.create_thread("VM-DMAC");
// step 3: start the EE!
iop_thread.start(iop_runner);
deci_thread.start(deci2_runner);
ee_thread.start(ee_runner);
{
auto p = scoped_prof("iop-start");
iop_thread.start([=](SystemThreadInterface& sti) { iop_runner(sti, g_game_version); });
}
{
auto p = scoped_prof("deci-start");
deci_thread.start(deci2_runner);
}
{
auto p = scoped_prof("ee-start");
ee_thread.start(ee_runner);
}
if (VM::use) {
auto p = scoped_prof("dmac-start");
vm_dmac_thread.start(dmac_runner);
}
+19 -6
View File
@@ -82,6 +82,16 @@ void* AllocSysMemory(int type, unsigned long size, void* addr) {
return iop->iop_alloc(size);
}
/*!
* Allocate the 1 kB scratchpad memory. On PS2, this would give you a pointer to the actual
* scratchpad of the IOP, but this is just normal memory.
*/
void* AllocScratchPad(int mode) {
ASSERT(mode == 0);
constexpr int kScratchpadSize = 1024 * 16;
return iop->iop_alloc(kScratchpadSize);
}
/*!
* Create a new thread
*/
@@ -140,12 +150,6 @@ void sceSifRpcLoop(sceSifQueueData* pd) {
iop->kernel.rpc_loop(pd);
}
int sceCdRead(uint32_t logical_sector, uint32_t sectors, void* buf, sceCdRMode* mode) {
(void)mode;
iop->kernel.read_disc_sectors(logical_sector, sectors, buf);
return 1;
}
int sceCdSync(int mode) {
(void)mode;
return 0;
@@ -193,6 +197,10 @@ s32 PollMbx(MsgPacket** recvmsg, int mbxid) {
return iop->kernel.PollMbx((void**)recvmsg, mbxid);
}
s32 PeekMbx(s32 mbx) {
return iop->kernel.PeekMbx(mbx);
}
static int now = 0;
void GetSystemTime(SysClock* time) {
@@ -226,6 +234,11 @@ s32 WakeupThread(s32 thid) {
return 0;
}
s32 iWakeupThread(s32 thid) {
iop->kernel.iWakeupThread(thid);
return 0;
}
s32 RegisterVblankHandler(int edge, int priority, int (*handler)(void*), void* /*userdata*/) {
(void)edge;
(void)priority;
+5 -3
View File
@@ -20,7 +20,7 @@
#define SCECdComplete 0x02
#define SCECdNotReady 0x06
#define KE_OK 0
#define KE_SEMA_ZERO -419
#define KE_SEMA_ZERO (-419)
#define KE_SEMA_OVF -420
#define KE_MBOX_NOMSG -424
#define KE_WAIT_DELETE -425
@@ -76,7 +76,7 @@ struct MbxParam {
struct ThreadParam {
u32 attr;
u32 option;
void* entry;
u32 (*entry)();
int stackSize;
int initPriority;
@@ -94,6 +94,7 @@ struct SemaParam {
// void PS2_RegisterIOP(IOP *iop);
int QueryTotalFreeMemSize();
void* AllocSysMemory(int type, unsigned long size, void* addr);
void* AllocScratchPad(int mode);
int GetThreadId();
void CpuDisableIntr();
@@ -104,6 +105,7 @@ s32 CreateThread(ThreadParam* param);
s32 ExitThread();
s32 StartThread(s32 thid, u32 arg);
s32 WakeupThread(s32 thid);
s32 iWakeupThread(s32 thid);
void sceSifInitRpc(int mode);
void sceSifInitRpc(unsigned int mode);
@@ -117,7 +119,6 @@ void sceSifRegisterRpc(sceSifServeData* serve,
sceSifQueueData* qd);
void sceSifRpcLoop(sceSifQueueData* pd);
int sceCdRead(uint32_t logical_sector, uint32_t sectors, void* buf, sceCdRMode* mode);
int sceCdSync(int mode);
int sceCdGetError();
int sceCdGetDiskType();
@@ -129,6 +130,7 @@ u32 sceSifSetDma(sceSifDmaData* sdd, int len);
s32 SendMbx(int mbxid, void* sendmsg);
s32 PollMbx(MsgPacket** recvmsg, int mbxid);
s32 PeekMbx(s32 mbx);
s32 CreateMbx(MbxParam* param);
void GetSystemTime(SysClock* time);
+5 -5
View File
@@ -57,11 +57,11 @@ SFXBlock2::SFXBlock2(locator& loc, u32 id, BankTag* tag)
}
}
auto idx = 0;
for (auto& s : m_sounds) {
lg::warn("sound {} : {}", idx, s.name);
idx++;
}
// auto idx = 0;
// for (auto& s : m_sounds) {
// lg::warn("sound {} : {}", idx, s.name);
// idx++;
//}
}
std::optional<std::unique_ptr<sound_handler>> SFXBlock2::make_handler(voice_manager& vm,
+1 -2
View File
@@ -97,9 +97,8 @@ void voice::key_on() {
m_DecodeBuf.Reset();
m_CustomLoop = false;
// Console.WriteLn("SPU[%d]:VOICE[%d] Key On, SSA %08x", m_SPU.m_Id, m_Id, m_SSA);
// fmt::print("Key On {} {} {} {:x}\n",(void*)m_sample, m_Volume.left.Get(), m_Volume.right.Get(),
// m_ADSR.m_Reg.bits);
}
void voice::key_off() {
m_ADSR.Release();
// fmt::print("Key Off\n");
+1
View File
@@ -23,6 +23,7 @@ class voice {
s16_output run();
void key_on();
void key_off();
bool dead() {
+34 -4
View File
@@ -3,13 +3,14 @@
#include <cstring>
#include "common/common_types.h"
#include "common/util/Assert.h"
#include "game/sound/common/voice.h"
#include "third-party/fmt/core.h"
std::shared_ptr<snd::voice> voice;
u8 spu_memory[0xc060];
std::shared_ptr<snd::voice> voices[4];
u8 spu_memory[0x15160 * 10];
static sceSdTransIntrHandler trans_handler[2] = {nullptr, nullptr};
static void* userdata[2] = {nullptr, nullptr};
@@ -19,7 +20,13 @@ u32 sceSdGetSwitch(u32 entry) {
return 0;
}
snd::voice* voice_from_entry(u32 entry) {
u32 it = entry & 3;
return voices[it].get();
}
u32 sceSdGetAddr(u32 entry) {
auto* voice = voice_from_entry(entry);
if (!voice) {
return 0;
}
@@ -28,15 +35,33 @@ u32 sceSdGetAddr(u32 entry) {
// u32 reg = entry & ~0x3f;
// Only ever used for getting NAX
return voice->get_nax() << 1;
}
void sceSdSetSwitch(u32 entry, u32 value) {
void sceSdSetSwitch(u32 entry, u32 /*value*/) {
// we can ignore this, only used for vmix
u32 reg = entry & ~0x3f;
switch (reg) {
case 0x1500:
voice_from_entry(entry)->key_on();
voice_from_entry(entry + 1)->key_on();
break;
case 0x1600:
voice_from_entry(entry)->key_off();
break;
}
}
void sceSdkey_on_jak2_voice(int id) {
voice_from_entry(id)->key_on();
}
void sceSdkey_off_jak2_voice(int id) {
voice_from_entry(id)->key_off();
}
void sceSdSetAddr(u32 entry, u32 value) {
auto* voice = voice_from_entry(entry);
if (!voice) {
return;
}
@@ -51,10 +76,15 @@ void sceSdSetAddr(u32 entry, u32 value) {
case SD_VA_LSAX: {
voice->set_lsa(value >> 1);
} break;
default:
printf("unknown 0x%x\n", reg);
ASSERT_NOT_REACHED();
break;
}
}
void sceSdSetParam(u32 entry, u32 value) {
auto* voice = voice_from_entry(entry);
if (!voice) {
return;
}

Some files were not shown because too many files have changed in this diff Show More