mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-04-22 02:01:18 +00:00
Merge remote-tracking branch 'upstream/vi-fixes' into controllerpakimpl
This commit is contained in:
commit
826afd1c4f
11 changed files with 449 additions and 188 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 8781eb44acbf55cb6a109d2aa5529aadb95a419d
|
||||
Subproject commit c1a6dc93bfa5977de0ea256562058be4f1b73353
|
||||
|
|
@ -96,6 +96,7 @@ namespace recomp {
|
|||
};
|
||||
|
||||
std::string error_to_string(ModLoadError);
|
||||
void unmet_dependency_handler(uint8_t* rdram, recomp_context* ctx, uintptr_t arg);
|
||||
|
||||
enum class CodeModLoadError {
|
||||
Good,
|
||||
|
|
@ -133,6 +134,19 @@ namespace recomp {
|
|||
String
|
||||
};
|
||||
|
||||
enum class DependencyStatus {
|
||||
// Do not change these values as they're exposed in the mod API!
|
||||
|
||||
// The dependency was found and the version requirement was met.
|
||||
Found = 0,
|
||||
// The ID given is not a dependency of the mod in question.
|
||||
InvalidDependency = 1,
|
||||
// The dependency was not found.
|
||||
NotFound = 2,
|
||||
// The dependency was found, but the version requirement was not met.
|
||||
WrongVersion = 3
|
||||
};
|
||||
|
||||
struct ModFileHandle {
|
||||
virtual ~ModFileHandle() = default;
|
||||
virtual std::vector<char> read_file(const std::string& filepath, bool& exists) const = 0;
|
||||
|
|
@ -171,6 +185,7 @@ namespace recomp {
|
|||
struct Dependency {
|
||||
std::string mod_id;
|
||||
Version version;
|
||||
bool optional;
|
||||
};
|
||||
|
||||
struct ConfigOptionEnum {
|
||||
|
|
@ -363,6 +378,10 @@ namespace recomp {
|
|||
bool register_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest);
|
||||
ModContentTypeId get_code_content_type() const { return code_content_type_id; }
|
||||
bool is_content_runtime_toggleable(ModContentTypeId content_type) const;
|
||||
std::string get_mod_display_name(size_t mod_index) const;
|
||||
std::filesystem::path get_mod_path(size_t mod_index) const;
|
||||
std::pair<std::string, std::string> get_mod_import_info(size_t mod_index, size_t import_index) const;
|
||||
DependencyStatus is_dependency_met(size_t mod_index, const std::string& dependency_id) const;
|
||||
private:
|
||||
ModOpenError open_mod_from_manifest(ModManifest &manifest, std::string &error_param, const std::vector<ModContentTypeId> &supported_content_types, bool requires_manifest);
|
||||
ModOpenError open_mod_from_path(const std::filesystem::path& mod_path, std::string& error_param, const std::vector<ModContentTypeId>& supported_content_types, bool requires_manifest);
|
||||
|
|
@ -621,6 +640,10 @@ namespace recomp {
|
|||
size_t get_mod_order_index(size_t mod_index);
|
||||
ModContentTypeId register_mod_content_type(const ModContentType& type);
|
||||
bool register_mod_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest);
|
||||
std::string get_mod_display_name(size_t mod_index);
|
||||
std::filesystem::path get_mod_path(size_t mod_index);
|
||||
std::pair<std::string, std::string> get_mod_import_info(size_t mod_index, size_t import_index);
|
||||
DependencyStatus is_dependency_met(size_t mod_index, const std::string& dependency_id);
|
||||
|
||||
void register_config_exports();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,18 @@ void recomp_get_mod_folder_path(uint8_t* rdram, recomp_context* ctx) {
|
|||
return_string(rdram, ctx, std::filesystem::absolute(mod_folder_path).u8string());
|
||||
}
|
||||
|
||||
void recomp_get_mod_file_path(uint8_t* rdram, recomp_context* ctx, size_t mod_index) {
|
||||
std::filesystem::path mod_file_path = recomp::mods::get_mod_path(mod_index);
|
||||
|
||||
return_string(rdram, ctx, std::filesystem::absolute(mod_file_path).u8string());
|
||||
}
|
||||
|
||||
void recomp_is_dependency_met(uint8_t* rdram, recomp_context* ctx, size_t mod_index) {
|
||||
std::string dependency_id = _arg_string<0>(rdram, ctx);
|
||||
recomp::mods::DependencyStatus status = recomp::mods::is_dependency_met(mod_index, dependency_id);
|
||||
_return(ctx, static_cast<uint32_t>(status));
|
||||
}
|
||||
|
||||
void recomp::mods::register_config_exports() {
|
||||
recomp::overlays::register_ext_base_export("recomp_get_config_u32", recomp_get_config_u32);
|
||||
recomp::overlays::register_ext_base_export("recomp_get_config_double", recomp_get_config_double);
|
||||
|
|
@ -105,4 +117,6 @@ void recomp::mods::register_config_exports() {
|
|||
recomp::overlays::register_ext_base_export("recomp_change_save_file", recomp_change_save_file);
|
||||
recomp::overlays::register_base_export("recomp_get_save_file_path", recomp_get_save_file_path);
|
||||
recomp::overlays::register_base_export("recomp_get_mod_folder_path", recomp_get_mod_folder_path);
|
||||
recomp::overlays::register_ext_base_export("recomp_get_mod_file_path", recomp_get_mod_file_path);
|
||||
recomp::overlays::register_ext_base_export("recomp_is_dependency_met", recomp_is_dependency_met);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ const std::string authors_key = "authors";
|
|||
const std::string minimum_recomp_version_key = "minimum_recomp_version";
|
||||
const std::string enabled_by_default_key = "enabled_by_default";
|
||||
const std::string dependencies_key = "dependencies";
|
||||
const std::string optional_dependencies_key = "optional_dependencies";
|
||||
const std::string native_libraries_key = "native_libraries";
|
||||
const std::string config_schema_key = "config_schema";
|
||||
|
||||
|
|
@ -602,6 +603,26 @@ recomp::mods::ModOpenError recomp::mods::parse_manifest(ModManifest& ret, const
|
|||
error_param = dep_string;
|
||||
return ModOpenError::InvalidDependencyString;
|
||||
}
|
||||
cur_dep.optional = false;
|
||||
|
||||
size_t dependency_index = ret.dependencies.size();
|
||||
ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index);
|
||||
ret.dependencies.emplace_back(std::move(cur_dep));
|
||||
}
|
||||
|
||||
// Optional dependencies (optional)
|
||||
std::vector<std::string> optional_dep_strings{};
|
||||
current_error = try_get_vec<json::string_t>(optional_dep_strings, manifest_json, optional_dependencies_key, false, error_param);
|
||||
if (current_error != ModOpenError::Good) {
|
||||
return current_error;
|
||||
}
|
||||
for (const std::string& dep_string : optional_dep_strings) {
|
||||
Dependency cur_dep;
|
||||
if (!parse_dependency(dep_string, cur_dep)) {
|
||||
error_param = dep_string;
|
||||
return ModOpenError::InvalidDependencyString;
|
||||
}
|
||||
cur_dep.optional = true;
|
||||
|
||||
size_t dependency_index = ret.dependencies.size();
|
||||
ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index);
|
||||
|
|
|
|||
|
|
@ -505,18 +505,20 @@ recomp::mods::LiveRecompilerCodeHandle::LiveRecompilerCodeHandle(
|
|||
N64Recomp::LiveGenerator generator{ context.functions.size(), recompiler_inputs };
|
||||
std::vector<std::vector<uint32_t>> dummy_static_funcs{};
|
||||
|
||||
bool errored = false;
|
||||
|
||||
for (size_t func_index = 0; func_index < context.functions.size(); func_index++) {
|
||||
std::ostringstream dummy_ostream{};
|
||||
|
||||
if (!N64Recomp::recompile_function_live(generator, context, func_index, dummy_ostream, dummy_static_funcs, true)) {
|
||||
is_good = false;
|
||||
errored = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the code.
|
||||
recompiler_output = std::make_unique<N64Recomp::LiveGeneratorOutput>(generator.finish());
|
||||
is_good = recompiler_output->good;
|
||||
is_good = !errored && recompiler_output->good;
|
||||
}
|
||||
|
||||
void recomp::mods::LiveRecompilerCodeHandle::set_imported_function(size_t import_index, GenericFunction func) {
|
||||
|
|
@ -975,6 +977,45 @@ bool recomp::mods::ModContext::register_container_type(const std::string& extens
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string recomp::mods::ModContext::get_mod_display_name(size_t mod_index) const {
|
||||
return opened_mods[mod_index].manifest.display_name;
|
||||
}
|
||||
|
||||
std::filesystem::path recomp::mods::ModContext::get_mod_path(size_t mod_index) const {
|
||||
return opened_mods[mod_index].manifest.mod_root_path;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> recomp::mods::ModContext::get_mod_import_info(size_t mod_index, size_t import_index) const {
|
||||
const ModHandle& mod = opened_mods[mod_index];
|
||||
const N64Recomp::ImportSymbol& imported_func = mod.recompiler_context->import_symbols[import_index];
|
||||
const std::string& dependency_id = mod.recompiler_context->dependencies[imported_func.dependency_index];
|
||||
|
||||
return std::make_pair<std::string, std::string>(std::string{ dependency_id }, std::string{ imported_func.base.name });
|
||||
}
|
||||
|
||||
recomp::mods::DependencyStatus recomp::mods::ModContext::is_dependency_met(size_t mod_index, const std::string& dependency_id) const {
|
||||
const ModHandle& mod = opened_mods[mod_index];
|
||||
|
||||
auto find_dep = mod.manifest.dependencies_by_id.find(dependency_id);
|
||||
if (find_dep == mod.manifest.dependencies_by_id.end()) {
|
||||
return DependencyStatus::InvalidDependency;
|
||||
}
|
||||
|
||||
auto find_dep_mod = loaded_mods_by_id.find(dependency_id);
|
||||
if (find_dep_mod == loaded_mods_by_id.end()) {
|
||||
return DependencyStatus::NotFound;
|
||||
}
|
||||
|
||||
const Dependency& dep = mod.manifest.dependencies[find_dep->second];
|
||||
const ModHandle& dep_mod = opened_mods[find_dep_mod->second];
|
||||
|
||||
if (dep_mod.manifest.version < dep.version) {
|
||||
return DependencyStatus::WrongVersion;
|
||||
}
|
||||
|
||||
return DependencyStatus::Found;
|
||||
}
|
||||
|
||||
bool recomp::mods::ModContext::is_content_runtime_toggleable(ModContentTypeId content_type) const {
|
||||
assert(content_type.value < content_types.size());
|
||||
|
||||
|
|
@ -1026,7 +1067,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable
|
|||
if (mod_from_stack_it != opened_mods_by_id.end()) {
|
||||
const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second];
|
||||
for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) {
|
||||
if (!auto_enabled_mods.contains(dependency.mod_id)) {
|
||||
if (!dependency.optional && !auto_enabled_mods.contains(dependency.mod_id)) {
|
||||
auto_enabled_mods.emplace(dependency.mod_id);
|
||||
mod_stack.emplace_back(dependency.mod_id);
|
||||
|
||||
|
|
@ -1071,7 +1112,7 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable
|
|||
if (mod_from_stack_it != opened_mods_by_id.end()) {
|
||||
const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second];
|
||||
for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) {
|
||||
if (!new_auto_enabled_mods.contains(dependency.mod_id)) {
|
||||
if (!dependency.optional && !new_auto_enabled_mods.contains(dependency.mod_id)) {
|
||||
new_auto_enabled_mods.emplace(dependency.mod_id);
|
||||
mod_stack.emplace_back(dependency.mod_id);
|
||||
}
|
||||
|
|
@ -2038,25 +2079,28 @@ void recomp::mods::ModContext::check_dependencies(recomp::mods::ModHandle& mod,
|
|||
mod.disable_runtime_toggle();
|
||||
}
|
||||
for (const recomp::mods::Dependency& cur_dep : mod.manifest.dependencies) {
|
||||
// Look for the dependency in the loaded mod mapping.
|
||||
auto find_loaded_dep_it = loaded_mods_by_id.find(cur_dep.mod_id);
|
||||
if (find_loaded_dep_it == loaded_mods_by_id.end()) {
|
||||
errors.emplace_back(ModLoadError::MissingDependency, cur_dep.mod_id);
|
||||
continue;
|
||||
}
|
||||
if (!cur_dep.optional) {
|
||||
// Look for the dependency in the loaded mod mapping.
|
||||
auto find_loaded_dep_it = loaded_mods_by_id.find(cur_dep.mod_id);
|
||||
if (find_loaded_dep_it != loaded_mods_by_id.end()) {
|
||||
ModHandle& dep_mod = opened_mods[find_loaded_dep_it->second];
|
||||
if (cur_dep.version > dep_mod.manifest.version)
|
||||
{
|
||||
std::stringstream error_param_stream{};
|
||||
error_param_stream << "requires mod \"" << cur_dep.mod_id << "\" " <<
|
||||
(int)cur_dep.version.major << "." << (int)cur_dep.version.minor << "." << (int)cur_dep.version.patch << ", got " <<
|
||||
(int)dep_mod.manifest.version.major << "." << (int)dep_mod.manifest.version.minor << "." << (int)dep_mod.manifest.version.patch << "";
|
||||
errors.emplace_back(ModLoadError::WrongDependencyVersion, error_param_stream.str());
|
||||
}
|
||||
|
||||
ModHandle& dep_mod = opened_mods[find_loaded_dep_it->second];
|
||||
if (cur_dep.version > dep_mod.manifest.version)
|
||||
{
|
||||
std::stringstream error_param_stream{};
|
||||
error_param_stream << "requires mod \"" << cur_dep.mod_id << "\" " <<
|
||||
(int)cur_dep.version.major << "." << (int)cur_dep.version.minor << "." << (int)cur_dep.version.patch << ", got " <<
|
||||
(int)dep_mod.manifest.version.major << "." << (int)dep_mod.manifest.version.minor << "." << (int)dep_mod.manifest.version.patch << "";
|
||||
errors.emplace_back(ModLoadError::WrongDependencyVersion, error_param_stream.str());
|
||||
// Prevent the dependency from being toggled at runtime, as it's required for this mod.
|
||||
dep_mod.disable_runtime_toggle();
|
||||
}
|
||||
// Add an error for this mod if the dependency isn't optional.
|
||||
else {
|
||||
errors.emplace_back(ModLoadError::MissingDependency, cur_dep.mod_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent the dependency from being toggled at runtime, as it's required for this mod.
|
||||
dep_mod.disable_runtime_toggle();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2125,22 +2169,28 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::init_mod_code(uint8_t*
|
|||
int32_t cur_section_addr = load_address;
|
||||
for (size_t section_index = 0; section_index < mod_sections.size(); section_index++) {
|
||||
const auto& section = mod_sections[section_index];
|
||||
for (size_t i = 0; i < section.size; i++) {
|
||||
MEM_B(i, (gpr)cur_section_addr) = binary_data[section.rom_addr + i];
|
||||
// Do not load fixed address sections into mod memory. Use their address as-is.
|
||||
if (section.fixed_address) {
|
||||
mod.section_load_addresses[section_index] = section.ram_addr;
|
||||
}
|
||||
mod.section_load_addresses[section_index] = cur_section_addr;
|
||||
// Calculate the bss section's address based on the size of this section.
|
||||
cur_section_addr += section.size;
|
||||
// Zero the bss section.
|
||||
for (size_t i = 0; i < section.bss_size; i++) {
|
||||
MEM_B(i, (gpr)cur_section_addr) = 0;
|
||||
else {
|
||||
for (size_t i = 0; i < section.size; i++) {
|
||||
MEM_B(i, (gpr)cur_section_addr) = binary_data[section.rom_addr + i];
|
||||
}
|
||||
mod.section_load_addresses[section_index] = cur_section_addr;
|
||||
// Calculate the bss section's address based on the size of this section.
|
||||
cur_section_addr += section.size;
|
||||
// Zero the bss section.
|
||||
for (size_t i = 0; i < section.bss_size; i++) {
|
||||
MEM_B(i, (gpr)cur_section_addr) = 0;
|
||||
}
|
||||
// Calculate the next section's address based on the size of the bss section.
|
||||
cur_section_addr += section.bss_size;
|
||||
// Align the next section's address to 16 bytes.
|
||||
cur_section_addr = (cur_section_addr + 15) & ~15;
|
||||
// Add some empty space between mods to act as a buffer for misbehaving mods that have out of bounds accesses.
|
||||
cur_section_addr += 0x400;
|
||||
}
|
||||
// Calculate the next section's address based on the size of the bss section.
|
||||
cur_section_addr += section.bss_size;
|
||||
// Align the next section's address to 16 bytes.
|
||||
cur_section_addr = (cur_section_addr + 15) & ~15;
|
||||
// Add some empty space between mods to act as a buffer for misbehaving mods that have out of bounds accesses.
|
||||
cur_section_addr += 0x400;
|
||||
}
|
||||
|
||||
// Iterate over each section again after loading them to perform R_MIPS_32 relocations.
|
||||
|
|
@ -2203,7 +2253,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t*
|
|||
std::unordered_map<size_t, size_t> entry_func_hooks{};
|
||||
std::unordered_map<size_t, size_t> return_func_hooks{};
|
||||
|
||||
// Scan the replacements and check for any
|
||||
// Scan the replacements to handle hooks on the replaced functions.
|
||||
for (const auto& replacement : mod.recompiler_context->replacements) {
|
||||
// Check if there's a hook slot for the entry of this function.
|
||||
HookDefinition entry_def {
|
||||
|
|
@ -2362,14 +2412,24 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci
|
|||
}
|
||||
else {
|
||||
auto find_mod_it = loaded_mods_by_id.find(dependency_id);
|
||||
if (find_mod_it == loaded_mods_by_id.end()) {
|
||||
error_param = "Failed to find import dependency while loading code: " + dependency_id;
|
||||
// This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
|
||||
// is validated against the manifest's.
|
||||
return CodeModLoadError::InternalError;
|
||||
if (find_mod_it != loaded_mods_by_id.end()) {
|
||||
const auto& dependency = opened_mods[find_mod_it->second];
|
||||
did_find_func = dependency.get_export_function(imported_func.base.name, func_handle);
|
||||
}
|
||||
else {
|
||||
auto find_optional_it = mod.manifest.dependencies_by_id.find(dependency_id);
|
||||
if (find_optional_it != mod.manifest.dependencies_by_id.end()) {
|
||||
uintptr_t shim_argument = ((import_index & 0xFFFFFFFFu) << 32) | (mod_index & 0xFFFFFFFFu);
|
||||
func_handle = shim_functions.emplace_back(std::make_unique<N64Recomp::ShimFunction>(unmet_dependency_handler, shim_argument)).get()->get_func();
|
||||
did_find_func = true;
|
||||
}
|
||||
else {
|
||||
error_param = "Failed to find import dependency while loading code: " + dependency_id;
|
||||
// This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
|
||||
// is validated against the manifest's.
|
||||
return CodeModLoadError::InternalError;
|
||||
}
|
||||
}
|
||||
const auto& dependency = opened_mods[find_mod_it->second];
|
||||
did_find_func = dependency.get_export_function(imported_func.base.name, func_handle);
|
||||
}
|
||||
|
||||
if (!did_find_func) {
|
||||
|
|
@ -2387,6 +2447,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci
|
|||
GenericFunction func = mod.code_handle->get_function_handle(callback.function_index);
|
||||
size_t event_index = 0;
|
||||
bool did_find_event = false;
|
||||
bool optional = false;
|
||||
|
||||
if (dependency_id == N64Recomp::DependencyBaseRecomp) {
|
||||
event_index = recomp::overlays::get_base_event_index(dependency_event.event_name);
|
||||
|
|
@ -2398,23 +2459,40 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci
|
|||
did_find_event = mod.get_global_event_index(dependency_event.event_name, event_index);
|
||||
}
|
||||
else {
|
||||
// Check if the dependency is optional.
|
||||
auto find_mod_it = loaded_mods_by_id.find(dependency_id);
|
||||
if (find_mod_it == loaded_mods_by_id.end()) {
|
||||
error_param = "Failed to find callback dependency while loading code: " + dependency_id;
|
||||
// This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
|
||||
// is validated against the manifest's.
|
||||
return CodeModLoadError::InternalError;
|
||||
// Get the manifest's version of the dependency.
|
||||
auto find_manifest_dep = mod.manifest.dependencies_by_id.find(dependency_id);
|
||||
// This should always be found, but just in case validate that the find was successful.
|
||||
// This will get treated as an error if it wasn't found in the manifest.
|
||||
if (find_manifest_dep != mod.manifest.dependencies_by_id.end()) {
|
||||
const auto& manifest_dep = mod.manifest.dependencies[find_manifest_dep->second];
|
||||
if (manifest_dep.optional) {
|
||||
optional = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!optional) {
|
||||
error_param = "Failed to find callback dependency while loading code: " + dependency_id;
|
||||
// This should never happen, as dependencies are scanned before mod code is loaded and the symbol dependency list
|
||||
// is validated against the manifest's.
|
||||
return CodeModLoadError::InternalError;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const auto& dependency_mod = opened_mods[find_mod_it->second];
|
||||
did_find_event = dependency_mod.get_global_event_index(dependency_event.event_name, event_index);
|
||||
}
|
||||
const auto& dependency_mod = opened_mods[find_mod_it->second];
|
||||
did_find_event = dependency_mod.get_global_event_index(dependency_event.event_name, event_index);
|
||||
}
|
||||
|
||||
if (!did_find_event) {
|
||||
if (did_find_event) {
|
||||
recomp::mods::register_event_callback(event_index, mod_index, func);
|
||||
}
|
||||
else if (!optional) {
|
||||
error_param = dependency_id + ":" + dependency_event.event_name;
|
||||
return CodeModLoadError::InvalidCallbackEvent;
|
||||
}
|
||||
|
||||
recomp::mods::register_event_callback(event_index, mod_index, func);
|
||||
}
|
||||
|
||||
// Register hooks.
|
||||
|
|
@ -2503,3 +2581,18 @@ void recomp::mods::ModContext::unload_mods() {
|
|||
num_events = recomp::overlays::num_base_events();
|
||||
active_game = (size_t)-1;
|
||||
}
|
||||
|
||||
void recomp::mods::unmet_dependency_handler(uint8_t* rdram, recomp_context* ctx, uintptr_t arg) {
|
||||
size_t caller_mod_index = (arg >> 0) & uint64_t(0xFFFFFFFF);
|
||||
size_t import_index = (arg >> 32) & uint64_t(0xFFFFFFFF);
|
||||
|
||||
std::string mod_name = recomp::mods::get_mod_display_name(caller_mod_index);
|
||||
std::pair<std::string, std::string> import_info = recomp::mods::get_mod_import_info(caller_mod_index, import_index);
|
||||
|
||||
ultramodern::error_handling::message_box(
|
||||
(
|
||||
"Fatal error in mod \"" + mod_name + "\": Called function \"" + import_info.second + "\" in unmet optional dependency \"" + import_info.first + "\".\n"
|
||||
).c_str()
|
||||
);
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,26 @@ bool recomp::mods::register_mod_container_type(const std::string& extension, con
|
|||
return mod_context->register_container_type(extension, content_types, requires_manifest);
|
||||
}
|
||||
|
||||
std::string recomp::mods::get_mod_display_name(size_t mod_index) {
|
||||
std::lock_guard mod_lock{ mod_context_mutex };
|
||||
return mod_context->get_mod_display_name(mod_index);
|
||||
}
|
||||
|
||||
std::filesystem::path recomp::mods::get_mod_path(size_t mod_index) {
|
||||
std::lock_guard mod_lock{ mod_context_mutex };
|
||||
return mod_context->get_mod_path(mod_index);
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> recomp::mods::get_mod_import_info(size_t mod_index, size_t import_index) {
|
||||
std::lock_guard mod_lock{ mod_context_mutex };
|
||||
return mod_context->get_mod_import_info(mod_index, import_index);
|
||||
}
|
||||
|
||||
recomp::mods::DependencyStatus recomp::mods::is_dependency_met(size_t mod_index, const std::string& dependency_id) {
|
||||
std::lock_guard mod_lock{ mod_context_mutex };
|
||||
return mod_context->is_dependency_met(mod_index, dependency_id);
|
||||
}
|
||||
|
||||
bool check_hash(const std::vector<uint8_t>& rom_data, uint64_t expected_hash) {
|
||||
uint64_t calculated_hash = XXH3_64bits(rom_data.data(), rom_data.size());
|
||||
return calculated_hash == expected_hash;
|
||||
|
|
|
|||
|
|
@ -73,12 +73,21 @@ extern "C" void osGetCount_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r2 = osGetCount();
|
||||
}
|
||||
|
||||
extern "C" void osSetCount_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
osSetCount(ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osGetTime_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
uint64_t total_count = osGetTime();
|
||||
ctx->r2 = (int32_t)(total_count >> 32);
|
||||
ctx->r3 = (int32_t)(total_count >> 0);
|
||||
}
|
||||
|
||||
extern "C" void osSetTime_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
uint64_t t = ((uint64_t)(ctx->r4) << 32) | ((ctx->r5) & 0xFFFFFFFFu);
|
||||
osSetTime(t);
|
||||
}
|
||||
|
||||
extern "C" void osSetTimer_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
uint64_t countdown = ((uint64_t)(ctx->r6) << 32) | ((ctx->r7) & 0xFFFFFFFFu);
|
||||
uint64_t interval = load_doubleword(rdram, ctx->r29, 0x10);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#elif defined(__ANDROID__)
|
||||
# include "android/native_window.h"
|
||||
#elif defined(__linux__)
|
||||
# include "X11/Xlib.h"
|
||||
# undef None
|
||||
# undef Status
|
||||
# undef LockMask
|
||||
|
|
@ -29,6 +28,23 @@ struct SDL_Window;
|
|||
|
||||
namespace ultramodern {
|
||||
namespace renderer {
|
||||
struct ViRegs {
|
||||
unsigned int VI_STATUS_REG;
|
||||
unsigned int VI_ORIGIN_REG;
|
||||
unsigned int VI_WIDTH_REG;
|
||||
unsigned int VI_INTR_REG;
|
||||
unsigned int VI_V_CURRENT_LINE_REG;
|
||||
unsigned int VI_TIMING_REG;
|
||||
unsigned int VI_V_SYNC_REG;
|
||||
unsigned int VI_H_SYNC_REG;
|
||||
unsigned int VI_LEAP_REG;
|
||||
unsigned int VI_H_START_REG;
|
||||
unsigned int VI_V_START_REG;
|
||||
unsigned int VI_V_BURST_REG;
|
||||
unsigned int VI_X_SCALE_REG;
|
||||
unsigned int VI_Y_SCALE_REG;
|
||||
};
|
||||
ViRegs* get_vi_regs();
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Native HWND handle to the target window.
|
||||
|
|
@ -68,7 +84,7 @@ namespace ultramodern {
|
|||
|
||||
virtual void enable_instant_present() = 0;
|
||||
virtual void send_dl(const OSTask* task) = 0;
|
||||
virtual void update_screen(uint32_t vi_origin) = 0;
|
||||
virtual void update_screen() = 0;
|
||||
virtual void shutdown() = 0;
|
||||
virtual uint32_t get_display_framerate() const = 0;
|
||||
virtual float get_resolution_scale() const = 0;
|
||||
|
|
|
|||
|
|
@ -298,7 +298,9 @@ void osViSetYScale(float scale);
|
|||
PTR(void) osViGetNextFramebuffer();
|
||||
PTR(void) osViGetCurrentFramebuffer();
|
||||
u32 osGetCount();
|
||||
void osSetCount(u32 count);
|
||||
OSTime osGetTime();
|
||||
void osSetTime(OSTime t);
|
||||
int osSetTimer(RDRAM_ARG PTR(OSTimer) timer, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg);
|
||||
int osStopTimer(RDRAM_ARG PTR(OSTimer) timer);
|
||||
u32 osVirtualToPhysical(PTR(void) addr);
|
||||
|
|
|
|||
|
|
@ -27,23 +27,84 @@ struct SpTaskAction {
|
|||
OSTask task;
|
||||
};
|
||||
|
||||
struct SwapBuffersAction {
|
||||
uint32_t origin;
|
||||
struct ScreenUpdateAction {
|
||||
ultramodern::renderer::ViRegs regs;
|
||||
};
|
||||
|
||||
struct UpdateConfigAction {
|
||||
};
|
||||
|
||||
using Action = std::variant<SpTaskAction, SwapBuffersAction, UpdateConfigAction>;
|
||||
using Action = std::variant<SpTaskAction, ScreenUpdateAction, UpdateConfigAction>;
|
||||
|
||||
struct ViState {
|
||||
const OSViMode* mode;
|
||||
PTR(void) framebuffer;
|
||||
PTR(OSMesg) mq;
|
||||
OSMesg msg;
|
||||
uint32_t state;
|
||||
uint32_t control;
|
||||
int retrace_count = 1;
|
||||
};
|
||||
|
||||
#define VI_STATE_BLACK 0x20
|
||||
#define VI_STATE_REPEATLINE 0x40
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
std::thread thread;
|
||||
PTR(OSMesgQueue) mq = NULLPTR;
|
||||
PTR(void) current_buffer = NULLPTR;
|
||||
PTR(void) next_buffer = NULLPTR;
|
||||
OSMesg msg = (OSMesg)0;
|
||||
int retrace_count = 1;
|
||||
int cur_state;
|
||||
int field;
|
||||
ViState states[2];
|
||||
ultramodern::renderer::ViRegs regs;
|
||||
ultramodern::renderer::ViRegs update_screen_regs;
|
||||
|
||||
ViState* get_next_state() {
|
||||
return &states[cur_state ^ 1];
|
||||
}
|
||||
ViState* get_cur_state() {
|
||||
return &states[cur_state];
|
||||
}
|
||||
void update_vi() {
|
||||
ViState* next_state = get_next_state();
|
||||
const OSViMode* next_mode = next_state->mode;
|
||||
const OSViCommonRegs* common_regs = &next_mode->comRegs;
|
||||
const OSViFieldRegs* field_regs = &next_mode->fldRegs[field];
|
||||
PTR(void) framebuffer = osVirtualToPhysical(next_state->framebuffer);
|
||||
PTR(void) origin = framebuffer + field_regs->origin;
|
||||
|
||||
// Process the VI state flags.
|
||||
uint32_t hStart = common_regs->hStart;
|
||||
if (next_state->state & VI_STATE_BLACK) {
|
||||
hStart = 0;
|
||||
}
|
||||
|
||||
uint32_t yScale = field_regs->yScale;
|
||||
if (next_state->state & VI_STATE_REPEATLINE) {
|
||||
yScale = 0;
|
||||
origin = framebuffer;
|
||||
}
|
||||
|
||||
// TODO implement osViFade
|
||||
|
||||
// Update VI registers.
|
||||
regs.VI_ORIGIN_REG = origin;
|
||||
regs.VI_WIDTH_REG = common_regs->width;
|
||||
regs.VI_TIMING_REG = common_regs->burst;
|
||||
regs.VI_V_SYNC_REG = common_regs->vSync;
|
||||
regs.VI_H_SYNC_REG = common_regs->hSync;
|
||||
regs.VI_LEAP_REG = common_regs->leap;
|
||||
regs.VI_H_START_REG = hStart;
|
||||
regs.VI_V_START_REG = field_regs->vStart; // TODO implement osViExtendVStart
|
||||
regs.VI_V_BURST_REG = field_regs->vBurst;
|
||||
regs.VI_INTR_REG = field_regs->vIntr;
|
||||
regs.VI_X_SCALE_REG = common_regs->xScale; // TODO implement osViSetXScale
|
||||
regs.VI_Y_SCALE_REG = yScale; // TODO implement osViSetYScale
|
||||
regs.VI_STATUS_REG = next_state->control;
|
||||
|
||||
// Swap VI states.
|
||||
cur_state ^= 1;
|
||||
*get_next_state() = *get_cur_state();
|
||||
}
|
||||
} vi;
|
||||
struct {
|
||||
std::thread gfx_thread;
|
||||
|
|
@ -71,8 +132,11 @@ static struct {
|
|||
moodycamel::ConcurrentQueue<OSThread*> deleted_threads{};
|
||||
} events_context{};
|
||||
|
||||
ultramodern::renderer::ViRegs* ultramodern::renderer::get_vi_regs() {
|
||||
return &events_context.vi.update_screen_regs;
|
||||
}
|
||||
|
||||
extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) {
|
||||
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
|
||||
switch (event_id) {
|
||||
|
|
@ -96,9 +160,10 @@ extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_,
|
|||
|
||||
extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 retrace_count) {
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
events_context.vi.mq = mq_;
|
||||
events_context.vi.msg = msg;
|
||||
events_context.vi.retrace_count = retrace_count;
|
||||
ViState* next_state = events_context.vi.get_next_state();
|
||||
next_state->mq = mq_;
|
||||
next_state->msg = msg;
|
||||
next_state->retrace_count = retrace_count;
|
||||
}
|
||||
|
||||
uint64_t total_vis = 0;
|
||||
|
|
@ -107,7 +172,7 @@ uint64_t total_vis = 0;
|
|||
extern std::atomic_bool exited;
|
||||
extern moodycamel::LightweightSemaphore graphics_shutdown_ready;
|
||||
|
||||
void set_dummy_vi();
|
||||
void set_dummy_vi(bool odd);
|
||||
|
||||
void vi_thread_func() {
|
||||
ultramodern::set_native_thread_name("VI Thread");
|
||||
|
|
@ -116,7 +181,7 @@ void vi_thread_func() {
|
|||
ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Critical);
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
int remaining_retraces = events_context.vi.retrace_count;
|
||||
int remaining_retraces = 1;
|
||||
|
||||
while (!exited) {
|
||||
// Determine the next VI time (more accurate than adding 16ms each VI interrupt)
|
||||
|
|
@ -135,39 +200,42 @@ void vi_thread_func() {
|
|||
next = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
ultramodern::sleep_until(next);
|
||||
auto time_now = ultramodern::time_since_start();
|
||||
// Calculate how many VIs have passed
|
||||
uint64_t new_total_vis = (ultramodern::time_since_start() * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1;
|
||||
uint64_t new_total_vis = (time_now * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1;
|
||||
if (new_total_vis > total_vis + 1) {
|
||||
//printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
|
||||
}
|
||||
total_vis = new_total_vis;
|
||||
|
||||
remaining_retraces--;
|
||||
// If the game hasn't started yet, set a dummy VI mode and origin.
|
||||
if (!ultramodern::is_game_started()) {
|
||||
static bool odd = false;
|
||||
set_dummy_vi(odd);
|
||||
odd = !odd;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
// Queue a screen update for the graphics thread with the current VI register state.
|
||||
// Doing this before the VI update is equivalent to updating the screen after the previous frame's scanout finished.
|
||||
events_context.action_queue.enqueue(ScreenUpdateAction{ events_context.vi.regs });
|
||||
|
||||
// Update VI registers and swap VI modes.
|
||||
events_context.vi.update_vi();
|
||||
|
||||
// If the game has started, handle sending VI and AI events.
|
||||
if (ultramodern::is_game_started()) {
|
||||
remaining_retraces--;
|
||||
|
||||
uint8_t* rdram = events_context.rdram;
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
ViState* cur_state = events_context.vi.get_cur_state();
|
||||
if (remaining_retraces == 0) {
|
||||
remaining_retraces = events_context.vi.retrace_count;
|
||||
|
||||
if (ultramodern::is_game_started()) {
|
||||
if (events_context.vi.mq != NULLPTR) {
|
||||
if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) {
|
||||
//printf("Game skipped a VI frame!\n");
|
||||
}
|
||||
if (cur_state->mq != NULLPTR) {
|
||||
if (osSendMesg(PASS_RDRAM cur_state->mq, cur_state->msg, OS_MESG_NOBLOCK) == -1) {
|
||||
//printf("Game skipped a VI frame!\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
set_dummy_vi();
|
||||
static bool swap = false;
|
||||
uint32_t vi_origin = 0x400 + 0x280; // Skip initial RDRAM contents and add the usual origin offset
|
||||
// Offset by one FB every other frame so RT64 continues drawing
|
||||
if (swap) {
|
||||
vi_origin += 0x25800;
|
||||
}
|
||||
osViSwapBuffer(rdram, vi_origin);
|
||||
swap = !swap;
|
||||
}
|
||||
remaining_retraces = cur_state->retrace_count;
|
||||
}
|
||||
if (events_context.ai.mq != NULLPTR) {
|
||||
if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) {
|
||||
|
|
@ -298,19 +366,20 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
|
|||
sp_complete();
|
||||
ultramodern::measure_input_latency();
|
||||
|
||||
auto renderer_start = std::chrono::high_resolution_clock::now();
|
||||
[[maybe_unused]] auto renderer_start = std::chrono::high_resolution_clock::now();
|
||||
renderer_context->send_dl(&task_action->task);
|
||||
auto renderer_end = std::chrono::high_resolution_clock::now();
|
||||
[[maybe_unused]] auto renderer_end = std::chrono::high_resolution_clock::now();
|
||||
dp_complete();
|
||||
// printf("Renderer ProcessDList time: %d us\n", static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
|
||||
}
|
||||
else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) {
|
||||
events_context.vi.current_buffer = events_context.vi.next_buffer;
|
||||
renderer_context->update_screen(swap_action->origin);
|
||||
else if (const auto* screen_update_action = std::get_if<ScreenUpdateAction>(&action)) {
|
||||
events_context.vi.update_screen_regs = screen_update_action->regs;
|
||||
renderer_context->update_screen();
|
||||
display_refresh_rate = renderer_context->get_display_framerate();
|
||||
resolution_scale = renderer_context->get_resolution_scale();
|
||||
}
|
||||
else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) {
|
||||
(void)config_action;
|
||||
auto new_config = ultramodern::renderer::get_graphics_config();
|
||||
if (renderer_context->update_config(old_config, new_config)) {
|
||||
old_config = new_config;
|
||||
|
|
@ -323,79 +392,6 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
|
|||
renderer_context->shutdown();
|
||||
}
|
||||
|
||||
extern unsigned int VI_STATUS_REG;
|
||||
extern unsigned int VI_ORIGIN_REG;
|
||||
extern unsigned int VI_WIDTH_REG;
|
||||
extern unsigned int VI_INTR_REG;
|
||||
extern unsigned int VI_V_CURRENT_LINE_REG;
|
||||
extern unsigned int VI_TIMING_REG;
|
||||
extern unsigned int VI_V_SYNC_REG;
|
||||
extern unsigned int VI_H_SYNC_REG;
|
||||
extern unsigned int VI_LEAP_REG;
|
||||
extern unsigned int VI_H_START_REG;
|
||||
extern unsigned int VI_V_START_REG;
|
||||
extern unsigned int VI_V_BURST_REG;
|
||||
extern unsigned int VI_X_SCALE_REG;
|
||||
extern unsigned int VI_Y_SCALE_REG;
|
||||
|
||||
#define VI_STATE_BLACK 0x20
|
||||
#define VI_STATE_REPEATLINE 0x40
|
||||
|
||||
uint32_t hstart = 0;
|
||||
uint32_t vi_origin_offset = 320 * sizeof(uint16_t);
|
||||
static uint16_t vi_state = 0;
|
||||
|
||||
void set_dummy_vi() {
|
||||
VI_STATUS_REG = 0x311E;
|
||||
VI_WIDTH_REG = 0x140;
|
||||
VI_V_SYNC_REG = 0x20D;
|
||||
VI_H_SYNC_REG = 0xC15;
|
||||
VI_LEAP_REG = 0x0C150C15;
|
||||
hstart = 0x006C02EC;
|
||||
VI_X_SCALE_REG = 0x200;
|
||||
VI_V_CURRENT_LINE_REG = 0x0;
|
||||
vi_origin_offset = 0x280;
|
||||
VI_Y_SCALE_REG = 0x400;
|
||||
VI_V_START_REG = 0x2501FF;
|
||||
VI_V_BURST_REG = 0xE0204;
|
||||
VI_INTR_REG = 0x2;
|
||||
}
|
||||
|
||||
extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
|
||||
VI_H_START_REG = hstart;
|
||||
if (vi_state & VI_STATE_BLACK) {
|
||||
VI_H_START_REG = 0;
|
||||
}
|
||||
|
||||
if (vi_state & VI_STATE_REPEATLINE) {
|
||||
VI_Y_SCALE_REG = 0;
|
||||
VI_ORIGIN_REG = osVirtualToPhysical(frameBufPtr);
|
||||
}
|
||||
|
||||
events_context.vi.next_buffer = frameBufPtr;
|
||||
events_context.action_queue.enqueue(SwapBuffersAction{ osVirtualToPhysical(frameBufPtr) + vi_origin_offset });
|
||||
}
|
||||
|
||||
extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) {
|
||||
OSViMode* mode = TO_PTR(OSViMode, mode_);
|
||||
VI_STATUS_REG = mode->comRegs.ctrl;
|
||||
VI_WIDTH_REG = mode->comRegs.width;
|
||||
// burst
|
||||
VI_V_SYNC_REG = mode->comRegs.vSync;
|
||||
VI_H_SYNC_REG = mode->comRegs.hSync;
|
||||
VI_LEAP_REG = mode->comRegs.leap;
|
||||
hstart = mode->comRegs.hStart;
|
||||
VI_X_SCALE_REG = mode->comRegs.xScale;
|
||||
VI_V_CURRENT_LINE_REG = mode->comRegs.vCurrent;
|
||||
|
||||
// TODO swap these every VI to account for fields changing
|
||||
vi_origin_offset = mode->fldRegs[0].origin;
|
||||
VI_Y_SCALE_REG = mode->fldRegs[0].yScale;
|
||||
VI_V_START_REG = mode->fldRegs[0].vStart;
|
||||
VI_V_BURST_REG = mode->fldRegs[0].vBurst;
|
||||
VI_INTR_REG = mode->fldRegs[0].vIntr;
|
||||
}
|
||||
|
||||
#define VI_CTRL_TYPE_16 0x00002
|
||||
#define VI_CTRL_TYPE_32 0x00003
|
||||
#define VI_CTRL_GAMMA_DITHER_ON 0x00004
|
||||
|
|
@ -412,6 +408,54 @@ extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) {
|
|||
#define VI_CTRL_PIXEL_ADV_3 0x03000
|
||||
#define VI_CTRL_DITHER_FILTER_ON 0x10000
|
||||
|
||||
static const OSViMode dummy_mode = []() {
|
||||
OSViMode ret{};
|
||||
|
||||
ret.type = 2;
|
||||
ret.comRegs.ctrl = VI_CTRL_TYPE_16 | VI_CTRL_GAMMA_DITHER_ON | VI_CTRL_GAMMA_ON | VI_CTRL_DIVOT_ON | VI_CTRL_ANTIALIAS_MODE_1 | VI_CTRL_PIXEL_ADV_3;
|
||||
ret.comRegs.width = 0x140;
|
||||
ret.comRegs.burst = 0x03E52239;
|
||||
ret.comRegs.vSync = 0x20D;
|
||||
ret.comRegs.hSync = 0xC15;
|
||||
ret.comRegs.leap = 0x0C150C15;
|
||||
ret.comRegs.hStart = 0x006C02EC;
|
||||
ret.comRegs.xScale = 0x200;
|
||||
ret.comRegs.vCurrent = 0x0;
|
||||
|
||||
for (int field = 0; field < 2; field++) {
|
||||
ret.fldRegs[field].origin = 0x280;
|
||||
ret.fldRegs[field].yScale = 0x400;
|
||||
ret.fldRegs[field].vStart = 0x2501FF;
|
||||
ret.fldRegs[field].vBurst = 0xE0204;
|
||||
ret.fldRegs[field].vIntr = 0x2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}();
|
||||
|
||||
void set_dummy_vi(bool odd) {
|
||||
ViState* next_state = events_context.vi.get_next_state();
|
||||
next_state->mode = &dummy_mode;
|
||||
// Set up a dummy framebuffer.
|
||||
next_state->framebuffer = 0x80700000;
|
||||
if (odd) {
|
||||
next_state->framebuffer += 0x25800;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
events_context.vi.get_next_state()->framebuffer = frameBufPtr;
|
||||
}
|
||||
|
||||
extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) {
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
OSViMode* mode = TO_PTR(OSViMode, mode_);
|
||||
ViState* next_state = events_context.vi.get_next_state();
|
||||
next_state->mode = mode;
|
||||
next_state->control = next_state->mode->comRegs.ctrl;
|
||||
}
|
||||
|
||||
#define OS_VI_GAMMA_ON 0x0001
|
||||
#define OS_VI_GAMMA_OFF 0x0002
|
||||
#define OS_VI_GAMMA_DITHER_ON 0x0004
|
||||
|
|
@ -422,54 +466,63 @@ extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) {
|
|||
#define OS_VI_DITHER_FILTER_OFF 0x0080
|
||||
|
||||
extern "C" void osViSetSpecialFeatures(uint32_t func) {
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
ViState* next_state = events_context.vi.get_next_state();
|
||||
uint32_t* control_out = &next_state->control;
|
||||
if ((func & OS_VI_GAMMA_ON) != 0) {
|
||||
VI_STATUS_REG |= VI_CTRL_GAMMA_ON;
|
||||
*control_out |= VI_CTRL_GAMMA_ON;
|
||||
}
|
||||
|
||||
if ((func & OS_VI_GAMMA_OFF) != 0) {
|
||||
VI_STATUS_REG &= ~VI_CTRL_GAMMA_ON;
|
||||
*control_out &= ~VI_CTRL_GAMMA_ON;
|
||||
}
|
||||
|
||||
if ((func & OS_VI_GAMMA_DITHER_ON) != 0) {
|
||||
VI_STATUS_REG |= VI_CTRL_GAMMA_DITHER_ON;
|
||||
*control_out |= VI_CTRL_GAMMA_DITHER_ON;
|
||||
}
|
||||
|
||||
if ((func & OS_VI_GAMMA_DITHER_OFF) != 0) {
|
||||
VI_STATUS_REG &= ~VI_CTRL_GAMMA_DITHER_ON;
|
||||
*control_out &= ~VI_CTRL_GAMMA_DITHER_ON;
|
||||
}
|
||||
|
||||
if ((func & OS_VI_DIVOT_ON) != 0) {
|
||||
VI_STATUS_REG |= VI_CTRL_DIVOT_ON;
|
||||
*control_out |= VI_CTRL_DIVOT_ON;
|
||||
}
|
||||
|
||||
if ((func & OS_VI_DIVOT_OFF) != 0) {
|
||||
VI_STATUS_REG &= ~VI_CTRL_DIVOT_ON;
|
||||
*control_out &= ~VI_CTRL_DIVOT_ON;
|
||||
}
|
||||
|
||||
if ((func & OS_VI_DITHER_FILTER_ON) != 0) {
|
||||
VI_STATUS_REG |= VI_CTRL_DITHER_FILTER_ON;
|
||||
VI_STATUS_REG &= ~VI_CTRL_ANTIALIAS_MASK;
|
||||
*control_out |= VI_CTRL_DITHER_FILTER_ON;
|
||||
*control_out &= ~VI_CTRL_ANTIALIAS_MASK;
|
||||
}
|
||||
|
||||
if ((func & OS_VI_DITHER_FILTER_OFF) != 0) {
|
||||
VI_STATUS_REG &= ~VI_CTRL_DITHER_FILTER_ON;
|
||||
//VI_STATUS_REG |= __osViNext->modep->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK;
|
||||
*control_out &= ~VI_CTRL_DITHER_FILTER_ON;
|
||||
*control_out |= next_state->mode->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osViBlack(uint8_t active) {
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
ViState* next_state = events_context.vi.get_next_state();
|
||||
uint32_t* state_out = &next_state->state;
|
||||
if (active) {
|
||||
vi_state |= VI_STATE_BLACK;
|
||||
*state_out |= VI_STATE_BLACK;
|
||||
} else {
|
||||
vi_state &= ~VI_STATE_BLACK;
|
||||
*state_out &= ~VI_STATE_BLACK;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osViRepeatLine(uint8_t active) {
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
ViState* next_state = events_context.vi.get_next_state();
|
||||
uint32_t* state_out = &next_state->state;
|
||||
if (active) {
|
||||
vi_state |= VI_STATE_REPEATLINE;
|
||||
*state_out |= VI_STATE_REPEATLINE;
|
||||
} else {
|
||||
vi_state &= ~VI_STATE_REPEATLINE;
|
||||
*state_out &= ~VI_STATE_REPEATLINE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -486,11 +539,11 @@ extern "C" void osViSetYScale(float scale) {
|
|||
}
|
||||
|
||||
extern "C" PTR(void) osViGetNextFramebuffer() {
|
||||
return events_context.vi.next_buffer;
|
||||
return events_context.vi.get_next_state()->framebuffer;
|
||||
}
|
||||
|
||||
extern "C" PTR(void) osViGetCurrentFramebuffer() {
|
||||
return events_context.vi.current_buffer;
|
||||
return events_context.vi.get_cur_state()->framebuffer;
|
||||
}
|
||||
|
||||
void ultramodern::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) {
|
||||
|
|
@ -523,7 +576,7 @@ void ultramodern::init_events(RDRAM_ARG ultramodern::renderer::WindowHandle wind
|
|||
task_thread_ready.wait();
|
||||
|
||||
ultramodern::renderer::SetupResult setup_result = renderer_setup_result.load();
|
||||
if (renderer_setup_result != ultramodern::renderer::SetupResult::Success) {
|
||||
if (setup_result != ultramodern::renderer::SetupResult::Success) {
|
||||
auto show_renderer_error = [](const std::string& msg) {
|
||||
std::string error_msg = "An error has been encountered on startup: " + msg;
|
||||
|
||||
|
|
@ -531,7 +584,7 @@ void ultramodern::init_events(RDRAM_ARG ultramodern::renderer::WindowHandle wind
|
|||
};
|
||||
|
||||
const std::string driver_os_suffix = "\nPlease make sure your GPU drivers and your OS are up to date.";
|
||||
switch (renderer_setup_result) {
|
||||
switch (setup_result) {
|
||||
case ultramodern::renderer::SetupResult::Success:
|
||||
break;
|
||||
case ultramodern::renderer::SetupResult::DynamicLibrariesNotFound:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
// Start time for the program
|
||||
static std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();
|
||||
// Offset of the duration since program start used to calculate the value for osGetTime.
|
||||
static int64_t ostime_offset = 0;
|
||||
// Game speed multiplier (1 means no speedup)
|
||||
constexpr uint32_t speed_multiplier = 1;
|
||||
// N64 CPU counter ticks per millisecond
|
||||
|
|
@ -162,12 +164,20 @@ extern "C" u32 osGetCount() {
|
|||
return (uint32_t)total_count;
|
||||
}
|
||||
|
||||
extern "C" void osSetCount(u32 count) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" OSTime osGetTime() {
|
||||
uint64_t total_count = time_now();
|
||||
uint64_t total_count = time_now() - ostime_offset;
|
||||
|
||||
return total_count;
|
||||
}
|
||||
|
||||
extern "C" void osSetTime(OSTime t) {
|
||||
ostime_offset = time_now() - t;
|
||||
}
|
||||
|
||||
extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) {
|
||||
OSTimer* t = TO_PTR(OSTimer, t_);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue