mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-21 08:55:04 -05:00
Dual OTR MQ and Vanilla Support (#1694)
* Changes OTR Extraction to have specific mq and nonmq paths. Also updates the game to load resources according to whether or not Master Quest or Vanilla is loaded. * Removes unneeded code from the last commit. * Fixes some weird formatting in ZRom.c * Loads oot-mq.otr and patches oot.otr on top, if both are present. If only one or the other are present, it becomes the only and main OTR. * Adds ImGui Logic for whether or an MQ Checkbox. Checkbox checked only specifies whether new saves should be MQ or not. Checkbox is disabled or force-enabled according to which OTRs are loaded. Also as a necessity includes tracking what game versions have been loaded from the OTRs. * Adds MQ settings logic for Randomizer's ImGui menu. * Writes Master Quest dungeons to the spoiler log * Loads MQ Dungeons from spoiler, persists in save, and loads when appropriate. * Adds logic to prevent loading or creating incompatible rando saves. * Fixdes some linux build issues and new rando save issues * Makes appimage create both vanilla and mq otrs If either rom is present, it makes the corresponding OTR. If both are present, it will make both. If one OTR is present but both roms are present, it will create the missing OTR. * Makes it so a randomized save file will not be marked as MQ. * Refactors to load all OTRs from MainPath or a specific list. Also adds the ability to take a std::unordered_set of hashes to validate each OTR's version file against. * Fixes a syntax error * Makes ExtractAssets output Vanilla and MQ OTRs if both roms are present * Fixes asset generation bug. * Partially working fix for dual OTR extract_assets Currently the cmake ExtractAssets target will return with a 1 if you only end up exporting one type of OTR isntead of both. Haven't found a great way to only attempt to copy a file if it exists from within cmake. It does actually correctly copy the OTR that is generated, despite the error from copying the other one. Pushing as is for now but will keep investigating. * Adds oot-mq.otr to the gitignore. * Makes ExtractAssets not fail on only one rom/OTR. * Removes PatchesPath from the constructors requiring OTRFiles vector. * Renames OOT_UNKNOWN to just UNKNOWN to remove OOT specific reference. * Removes randomizing MQ Dungeons and re-disables MQ rando. Doing this so the PR can get merged quicker with just the Dual OTR support and won't need to wait on rando logic to be updated. That will happen in another PR directly after the merge. * Update mac startup script for dual otr * Update soh/macosx/soh-macos.sh * Update soh/macosx/soh-macos.sh * Update soh/macosx/soh-macos.sh * Implements new BinaryReader to fix Linux build issue. BinaryReader itself comes from https://github.com/Moneyl/BinaryTools I added a wrapper to adapt it to the ABI from ZAPD's Binary Reader and add Endianness checking. I also had to copy a handful of other bits and pieces from ZAPD to make it all function as expected. * A few edits to the updatream BinaryReader to compile it on Linux. * Adds the Endianness to the first byte of the version file. * Fixes Jenkins * Addresses some of Kenix's comments * Renames `ReadNullTerminatedString` to `ReadCString` * Refactors Archive::LoadFile into a private method with more arguments. * Removes BitConverter and extends existing endianness.h instead. * Fixes an endianness issue with the version file. Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
This commit is contained in:
parent
350315a5d1
commit
7b08f98b8c
1
.gitignore
vendored
1
.gitignore
vendored
@ -410,6 +410,7 @@ ReleaseObj/*
|
||||
.tags
|
||||
tags
|
||||
oot.otr
|
||||
oot-mq.otr
|
||||
*.sav
|
||||
shipofharkinian.ini
|
||||
shipofharkinian.json
|
||||
|
@ -136,14 +136,13 @@ find_package(Python3 COMPONENTS Interpreter)
|
||||
add_custom_target(
|
||||
ExtractAssets
|
||||
# CMake versions prior to 3.17 do not have the rm command, use remove instead for older versions
|
||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<VERSION_LESS:${CMAKE_VERSION},3.17>,remove,rm> -f oot.otr
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets.py -z "$<TARGET_FILE:ZAPD>"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${CMAKE_SOURCE_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${CMAKE_BINARY_DIR}/soh
|
||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<VERSION_LESS:${CMAKE_VERSION},3.17>,remove,rm> -f oot.otr oot-mq.otr
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets.py -z "$<TARGET_FILE:ZAPD>" --non-interactive
|
||||
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DBINARY_DIR=${CMAKE_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/copy-existing-otrs.cmake
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter
|
||||
COMMENT "Running asset extraction..."
|
||||
DEPENDS ZAPD
|
||||
BYPRODUCTS oot.otr ${CMAKE_SOURCE_DIR}/oot.otr
|
||||
BYPRODUCTS oot.otr ${CMAKE_SOURCE_DIR}/oot.otr oot-mq.otr ${CMAKE_SOURCE_DIR}/oot-mq.otr
|
||||
)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
|
@ -882,8 +882,14 @@ std::string OTRExporter_DisplayList::GetPrefix(ZResource* res)
|
||||
std::string prefix = "";
|
||||
std::string xmlPath = StringHelper::Replace(res->parent->GetXmlFilePath().string(), "\\", "/");
|
||||
|
||||
if (StringHelper::Contains(oName, "_scene") || StringHelper::Contains(oName, "_room"))
|
||||
if (StringHelper::Contains(oName, "_scene") || StringHelper::Contains(oName, "_room")) {
|
||||
prefix = "scenes";
|
||||
if (Globals::Instance->rom->IsMQ()) {
|
||||
prefix += "/mq";
|
||||
} else {
|
||||
prefix += "/nonmq";
|
||||
}
|
||||
}
|
||||
else if (StringHelper::Contains(xmlPath, "objects/"))
|
||||
prefix = "objects";
|
||||
else if (StringHelper::Contains(xmlPath, "textures/"))
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <Utils/Directory.h>
|
||||
#include <Utils/MemoryStream.h>
|
||||
#include <Utils/BinaryWriter.h>
|
||||
#include <bit>
|
||||
|
||||
std::string otrFileName = "oot.otr";
|
||||
std::shared_ptr<Ship::Archive> otrArchive;
|
||||
@ -69,13 +70,20 @@ static void ExporterProgramEnd()
|
||||
std::string romPath = Globals::Instance->baseRomPath.string();
|
||||
std::vector<uint8_t> romData = File::ReadAllBytes(romPath);
|
||||
uint32_t crc = BitConverter::ToUInt32BE(romData, 0x10);
|
||||
uint8_t endianness = (uint8_t)Endianness::Big;
|
||||
|
||||
// Write crc to version file
|
||||
fs::path versionPath("Extract/version");
|
||||
MemoryStream* versionStream = new MemoryStream();
|
||||
BinaryWriter writer(versionStream);
|
||||
writer.SetEndianness(Endianness::Big);
|
||||
writer.Write(endianness);
|
||||
writer.Write(crc);
|
||||
std::ofstream versionFile(versionPath.c_str(), std::ios::out | std::ios::binary);
|
||||
versionFile.write((char*)&crc, sizeof(crc));
|
||||
versionFile.write(versionStream->ToVector().data(), versionStream->GetLength());
|
||||
versionFile.flush();
|
||||
versionFile.close();
|
||||
writer.Close();
|
||||
|
||||
printf("Created version file.\n");
|
||||
|
||||
|
@ -16,7 +16,8 @@ def BuildOTR(xmlPath, rom, zapd_exe=None):
|
||||
|
||||
exec_cmd = [zapd_exe, "ed", "-i", xmlPath, "-b", rom, "-fl", "CFG/filelists",
|
||||
"-o", "placeholder", "-osf", "placeholder", "-gsf", "1",
|
||||
"-rconf", "CFG/Config.xml", "-se", "OTR"]
|
||||
"-rconf", "CFG/Config.xml", "-se", "OTR", "--otrfile",
|
||||
"oot-mq.otr" if Z64Rom.isMqRom(rom) else "oot.otr"]
|
||||
|
||||
print(exec_cmd)
|
||||
exitValue = subprocess.call(exec_cmd)
|
||||
@ -30,16 +31,18 @@ def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-z", "--zapd", help="Path to ZAPD executable", dest="zapd_exe", type=str)
|
||||
parser.add_argument("rom", help="Path to the rom", type=str, nargs="?")
|
||||
parser.add_argument("--non-interactive", help="Runs the script non-interactively for use in build scripts.", dest="non_interactive", action="store_true")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
rom_path = args.rom if args.rom else rom_chooser.chooseROM()
|
||||
rom = Z64Rom(rom_path)
|
||||
rom_paths = [ args.rom ] if args.rom else rom_chooser.chooseROM(args.non_interactive)
|
||||
for rom_path in rom_paths:
|
||||
rom = Z64Rom(rom_path)
|
||||
|
||||
if (os.path.exists("Extract")):
|
||||
shutil.rmtree("Extract")
|
||||
if (os.path.exists("Extract")):
|
||||
shutil.rmtree("Extract")
|
||||
|
||||
BuildOTR("../soh/assets/xml/" + rom.version.xml_ver + "/", rom_path, zapd_exe=args.zapd_exe)
|
||||
BuildOTR("../soh/assets/xml/" + rom.version.xml_ver + "/", rom_path, zapd_exe=args.zapd_exe)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -2,7 +2,7 @@ import os, sys, glob
|
||||
|
||||
from rom_info import Z64Rom
|
||||
|
||||
def chooseROM():
|
||||
def chooseROM(non_interactive=False):
|
||||
roms = []
|
||||
|
||||
for file in glob.glob("*.z64"):
|
||||
@ -14,7 +14,21 @@ def chooseROM():
|
||||
sys.exit(1)
|
||||
|
||||
if (len(roms) == 1):
|
||||
return roms[0]
|
||||
return roms
|
||||
|
||||
if non_interactive:
|
||||
romsToExtract = []
|
||||
foundMq = False
|
||||
foundOot = False
|
||||
for rom in roms:
|
||||
isMq = Z64Rom.isMqRom(rom)
|
||||
if isMq and not foundMq:
|
||||
romsToExtract.append(rom)
|
||||
foundMq = True
|
||||
elif not isMq and not foundOot:
|
||||
romsToExtract.append(rom)
|
||||
foundOot = True
|
||||
return romsToExtract
|
||||
|
||||
print(str(len(roms))+ " roms found, please select one by pressing 1-"+str(len(roms)))
|
||||
|
||||
@ -34,4 +48,4 @@ def chooseROM():
|
||||
|
||||
else: break
|
||||
|
||||
return roms[selection - 1]
|
||||
return [ roms[selection - 1] ]
|
||||
|
@ -73,6 +73,11 @@ class Z64Rom:
|
||||
if self.checksum == Checksums.OOT_UNKNOWN:
|
||||
self.is_valid = False
|
||||
return
|
||||
|
||||
if self.checksum in [Checksums.OOT_NTSC_JP_MQ, Checksums.OOT_NTSC_US_MQ, Checksums.OOT_PAL_GC_MQ_DBG, Checksums.OOT_PAL_MQ]:
|
||||
self.isMq = True
|
||||
else:
|
||||
self.isMq = False
|
||||
|
||||
# get rom version
|
||||
self.version = ROM_INFO_TABLE[self.checksum]
|
||||
@ -86,3 +91,7 @@ class Z64Rom:
|
||||
@staticmethod
|
||||
def isValidRom(rom_path):
|
||||
return Z64Rom(rom_path).is_valid
|
||||
|
||||
@staticmethod
|
||||
def isMqRom(rom_path):
|
||||
return Z64Rom(rom_path).isMq
|
||||
|
@ -328,7 +328,7 @@ std::string ZResource::GetSourceOutputHeader([[maybe_unused]] const std::string&
|
||||
std::string xmlPath = StringHelper::Replace(parent->GetXmlFilePath().string(), "\\", "/");
|
||||
|
||||
if (StringHelper::Contains(outName, "_room_") || StringHelper::Contains(outName, "_scene"))
|
||||
prefix = "scenes";
|
||||
prefix = "scenes/nonmq";
|
||||
else if (StringHelper::Contains(xmlPath, "objects/"))
|
||||
prefix = "objects";
|
||||
else if (StringHelper::Contains(xmlPath, "textures/"))
|
||||
@ -342,8 +342,9 @@ std::string ZResource::GetSourceOutputHeader([[maybe_unused]] const std::string&
|
||||
else if (StringHelper::Contains(xmlPath, "text/"))
|
||||
prefix = "text";
|
||||
|
||||
if (prefix != "")
|
||||
if (prefix != "") {
|
||||
str += StringHelper::Sprintf("#define d%s \"__OTR__%s/%s/%s\"", name.c_str(), prefix.c_str(), outName.c_str(), nameStr.c_str());
|
||||
}
|
||||
else
|
||||
str += StringHelper::Sprintf("#define d%s \"__OTR__%s/%s\"", name.c_str(), outName.c_str(), nameStr.c_str());
|
||||
|
||||
|
@ -64,7 +64,33 @@ namespace fs = std::filesystem;
|
||||
#define OOT_PAL_GC_MQ_DBG 0x917D18F6
|
||||
#define OOT_IQUE_TW 0x3D81FB3E
|
||||
#define OOT_IQUE_CN 0xB1E1E07B
|
||||
#define OOT_UNKNOWN 0xFFFFFFFF
|
||||
#define UNKNOWN 0xFFFFFFFF
|
||||
|
||||
bool ZRom::IsMQ() {
|
||||
int crc = BitConverter::ToInt32BE(romData, 0x10);
|
||||
switch (crc) {
|
||||
case OOT_NTSC_10:
|
||||
case OOT_NTSC_11:
|
||||
case OOT_NTSC_12:
|
||||
case OOT_PAL_10:
|
||||
case OOT_PAL_11:
|
||||
case OOT_NTSC_JP_GC:
|
||||
case OOT_NTSC_JP_GC_CE:
|
||||
case OOT_NTSC_US_GC:
|
||||
case OOT_PAL_GC:
|
||||
case OOT_PAL_GC_DBG1:
|
||||
case OOT_PAL_GC_DBG2:
|
||||
case OOT_IQUE_CN:
|
||||
case OOT_IQUE_TW:
|
||||
default:
|
||||
return false;
|
||||
case OOT_NTSC_JP_MQ:
|
||||
case OOT_NTSC_US_MQ:
|
||||
case OOT_PAL_MQ:
|
||||
case OOT_PAL_GC_MQ_DBG:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ZRom::ZRom(std::string romPath)
|
||||
{
|
||||
|
@ -11,6 +11,7 @@ public:
|
||||
ZRom(std::string romPath);
|
||||
|
||||
std::vector<uint8_t> GetFile(std::string fileName);
|
||||
bool IsMQ();
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> romData;
|
||||
|
11
copy-existing-otrs.cmake
Normal file
11
copy-existing-otrs.cmake
Normal file
@ -0,0 +1,11 @@
|
||||
if(EXISTS ${SOURCE_DIR}/OTRExporter/oot.otr)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${SOURCE_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${BINARY_DIR}/soh/)
|
||||
endif()
|
||||
if(EXISTS ${SOURCE_DIR}/OTRExporter/oot-mq.otr)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy oot-mq.otr ${SOURCE_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy oot-mq.otr ${BINARY_DIR}/soh/)
|
||||
endif()
|
||||
if(NOT EXISTS ${SOURCE_DIR}/oot.otr AND NOT EXISTS ${SOURCE_DIR}/oot-mq.otr)
|
||||
message(FATAL_ERROR, "No OTR files found.")
|
||||
endif()
|
@ -48,7 +48,7 @@ void Ship::AnimationV0::ParseFileBinary(BinaryReader* reader, Resource* res)
|
||||
data.unk_02 = reader->ReadInt16();
|
||||
data.unk_04 = reader->ReadInt16();
|
||||
data.unk_06 = reader->ReadInt16();
|
||||
data.unk_08 = reader->ReadSingle();
|
||||
data.unk_08 = reader->ReadFloat();
|
||||
|
||||
anim->transformDataArr.push_back(data);
|
||||
}
|
||||
|
@ -1,27 +1,38 @@
|
||||
#include "Archive.h"
|
||||
#include "Resource.h"
|
||||
#include "File.h"
|
||||
#include "Window.h"
|
||||
#include "ResourceMgr.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "Utils/StringHelper.h"
|
||||
#include "Lib/StrHash64.h"
|
||||
#include <filesystem>
|
||||
#include "Lib/BinaryTools/BinaryTools/BinaryReader.h"
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include "SwitchImpl.h"
|
||||
#endif
|
||||
|
||||
namespace Ship {
|
||||
Archive::Archive(const std::string& MainPath, bool enableWriting) : Archive(MainPath, "", enableWriting)
|
||||
Archive::Archive(const std::string& MainPath, bool enableWriting) : Archive(MainPath, "", std::unordered_set<uint32_t>(), enableWriting)
|
||||
{
|
||||
mainMPQ = nullptr;
|
||||
}
|
||||
|
||||
Archive::Archive(const std::string& MainPath, const std::string& PatchesPath, bool enableWriting, bool genCRCMap) : MainPath(MainPath), PatchesPath(PatchesPath) {
|
||||
Archive::Archive(const std::string& MainPath, const std::string& PatchesPath, const std::unordered_set<uint32_t>& ValidHashes, bool enableWriting, bool genCRCMap)
|
||||
: MainPath(MainPath), PatchesPath(PatchesPath), OTRFiles({}), ValidHashes(ValidHashes) {
|
||||
mainMPQ = nullptr;
|
||||
Load(enableWriting, genCRCMap);
|
||||
}
|
||||
|
||||
Archive::~Archive() {
|
||||
Archive::Archive(const std::vector<std::string> &OTRFiles, const std::unordered_set<uint32_t> &ValidHashes, bool enableWriting, bool genCRCMap)
|
||||
: OTRFiles(OTRFiles), ValidHashes(ValidHashes)
|
||||
{
|
||||
mainMPQ = nullptr;
|
||||
Load(enableWriting, genCRCMap);
|
||||
}
|
||||
|
||||
Archive::~Archive() {
|
||||
Unload();
|
||||
}
|
||||
|
||||
@ -55,48 +66,63 @@ namespace Ship {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<File> Archive::LoadFileFromHandle(const std::string& filePath, bool includeParent, std::shared_ptr<File> FileToLoad, HANDLE mpqHandle)
|
||||
{
|
||||
HANDLE fileHandle = NULL;
|
||||
|
||||
if (FileToLoad == nullptr)
|
||||
{
|
||||
FileToLoad = std::make_shared<File>();
|
||||
FileToLoad->path = filePath;
|
||||
}
|
||||
|
||||
if (mpqHandle == nullptr)
|
||||
{
|
||||
mpqHandle = mainMPQ;
|
||||
}
|
||||
|
||||
bool attempt = SFileOpenFileEx(mpqHandle, filePath.c_str(), 0, &fileHandle);
|
||||
|
||||
if (!attempt)
|
||||
{
|
||||
SPDLOG_ERROR("({}) Failed to open file {} from mpq archive {}.", GetLastError(), filePath.c_str(), MainPath.c_str());
|
||||
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
|
||||
FileToLoad->bHasLoadError = true;
|
||||
return FileToLoad;
|
||||
}
|
||||
|
||||
DWORD dwFileSize = SFileGetFileSize(fileHandle, 0);
|
||||
std::shared_ptr<char[]> fileData(new char[dwFileSize]);
|
||||
DWORD dwBytes;
|
||||
|
||||
if (!SFileReadFile(fileHandle, fileData.get(), dwFileSize, &dwBytes, NULL))
|
||||
{
|
||||
SPDLOG_ERROR("({}) Failed to read file {} from mpq archive {}", GetLastError(), filePath.c_str(), MainPath.c_str());
|
||||
if (!SFileCloseFile(fileHandle))
|
||||
{
|
||||
SPDLOG_ERROR("({}) Failed to close file {} from mpq after read failure in archive {}", GetLastError(), filePath.c_str(), MainPath.c_str());
|
||||
}
|
||||
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
|
||||
FileToLoad->bHasLoadError = true;
|
||||
return FileToLoad;
|
||||
}
|
||||
|
||||
if (!SFileCloseFile(fileHandle))
|
||||
{
|
||||
SPDLOG_ERROR("({}) Failed to close file {} from mpq archive {}", GetLastError(), filePath.c_str(), MainPath.c_str());
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
|
||||
FileToLoad->parent = includeParent ? shared_from_this() : nullptr;
|
||||
FileToLoad->buffer = fileData;
|
||||
FileToLoad->dwBufferSize = dwFileSize;
|
||||
FileToLoad->bIsLoaded = true;
|
||||
|
||||
return FileToLoad;
|
||||
}
|
||||
|
||||
std::shared_ptr<File> Archive::LoadFile(const std::string& filePath, bool includeParent, std::shared_ptr<File> FileToLoad) {
|
||||
HANDLE fileHandle = NULL;
|
||||
|
||||
if (FileToLoad == nullptr) {
|
||||
FileToLoad = std::make_shared<File>();
|
||||
FileToLoad->path = filePath;
|
||||
}
|
||||
|
||||
bool attempt = SFileOpenFileEx(mainMPQ, filePath.c_str(), 0, &fileHandle);
|
||||
|
||||
if (!attempt) {
|
||||
SPDLOG_ERROR("({}) Failed to open file {} from mpq archive {}.", GetLastError(), filePath.c_str(), MainPath.c_str());
|
||||
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
|
||||
FileToLoad->bHasLoadError = true;
|
||||
return FileToLoad;
|
||||
}
|
||||
|
||||
DWORD dwFileSize = SFileGetFileSize(fileHandle, 0);
|
||||
std::shared_ptr<char[]> fileData(new char[dwFileSize]);
|
||||
DWORD dwBytes;
|
||||
|
||||
if (!SFileReadFile(fileHandle, fileData.get(), dwFileSize, &dwBytes, NULL)) {
|
||||
SPDLOG_ERROR("({}) Failed to read file {} from mpq archive {}", GetLastError(), filePath.c_str(), MainPath.c_str());
|
||||
if (!SFileCloseFile(fileHandle)) {
|
||||
SPDLOG_ERROR("({}) Failed to close file {} from mpq after read failure in archive {}", GetLastError(), filePath.c_str(), MainPath.c_str());
|
||||
}
|
||||
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
|
||||
FileToLoad->bHasLoadError = true;
|
||||
return FileToLoad;
|
||||
}
|
||||
|
||||
if (!SFileCloseFile(fileHandle)) {
|
||||
SPDLOG_ERROR("({}) Failed to close file {} from mpq archive {}", GetLastError(), filePath.c_str(), MainPath.c_str());
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
|
||||
FileToLoad->parent = includeParent ? shared_from_this() : nullptr;
|
||||
FileToLoad->buffer = fileData;
|
||||
FileToLoad->dwBufferSize = dwFileSize;
|
||||
FileToLoad->bIsLoaded = true;
|
||||
|
||||
return FileToLoad;
|
||||
return LoadFileFromHandle(filePath, includeParent, FileToLoad, nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<File> Archive::LoadPatchFile(const std::string& filePath, bool includeParent, std::shared_ptr<File> FileToLoad) {
|
||||
@ -320,53 +346,129 @@ namespace Ship {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Archive::GenerateCRCMap() {
|
||||
auto listFile = LoadFile("(listfile)", false);
|
||||
|
||||
std::vector<std::string> lines = StringHelper::Split(std::string(listFile->buffer.get(), listFile->dwBufferSize), "\n");
|
||||
|
||||
for (size_t i = 0; i < lines.size(); i++)
|
||||
{
|
||||
std::string line = StringHelper::Replace(StringHelper::Strip(lines[i], "\r"), "/", "\\");
|
||||
std::string line2 = StringHelper::Replace(line, "\\", "/");
|
||||
|
||||
uint64_t hash = CRC64(StringHelper::Replace(line, "/", "\\").c_str());
|
||||
uint64_t hash2 = CRC64(StringHelper::Replace(line, "\\", "/").c_str());
|
||||
hashes[hash] = line;
|
||||
hashes[hash2] = line2;
|
||||
}
|
||||
}
|
||||
|
||||
bool Archive::PushGameVersion(HANDLE mpqHandle) {
|
||||
auto t = LoadFileFromHandle("version", false, nullptr, mpqHandle);
|
||||
if (!t->bHasLoadError)
|
||||
{
|
||||
BinaryReader *reader = new BinaryReader(t->buffer.get(), t->dwBufferSize);
|
||||
Ship::Endianness endianness = (Ship::Endianness)reader->ReadUByte();
|
||||
reader->SetEndianness(endianness);
|
||||
uint32_t version = reader->ReadUInt32();
|
||||
if (ValidHashes.empty() || ValidHashes.contains(version)) {
|
||||
gameVersions.push_back(version);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Archive::LoadMainMPQ(bool enableWriting, bool genCRCMap) {
|
||||
HANDLE mpqHandle = NULL;
|
||||
HANDLE mpqHandle = NULL;
|
||||
if (OTRFiles.empty()) {
|
||||
if (MainPath.length() > 0) {
|
||||
if (std::filesystem::is_directory(MainPath)) {
|
||||
for (const auto &p : std::filesystem::recursive_directory_iterator(MainPath)) {
|
||||
if (StringHelper::IEquals(p.path().extension().string(), ".otr")) {
|
||||
SPDLOG_ERROR("Reading {} mpq", p.path().string().c_str());
|
||||
OTRFiles.push_back(p.path().string());
|
||||
}
|
||||
}
|
||||
} else if (std::filesystem::is_regular_file(MainPath)) {
|
||||
OTRFiles.push_back(MainPath);
|
||||
} else {
|
||||
SPDLOG_ERROR("The directory {} does not exist", MainPath.c_str());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
SPDLOG_ERROR("No OTR file list or Main Path provided.");
|
||||
return false;
|
||||
}
|
||||
if (OTRFiles.empty()) {
|
||||
SPDLOG_ERROR("No OTR files present in {}", MainPath.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool baseLoaded = false;
|
||||
int i = 0;
|
||||
while (!baseLoaded && i < OTRFiles.size()) {
|
||||
#ifdef _WIN32
|
||||
std::wstring wfullPath = std::filesystem::absolute(MainPath).wstring();
|
||||
std::wstring wfullPath = std::filesystem::absolute(OTRFiles[i]).wstring();
|
||||
#endif
|
||||
#if defined(__SWITCH__)
|
||||
std::string fullPath = MainPath;
|
||||
#else
|
||||
std::string fullPath = std::filesystem::absolute(MainPath).string();
|
||||
std::string fullPath = OTRFiles[0];
|
||||
#else
|
||||
std::string fullPath = std::filesystem::absolute(OTRFiles[i]).string();
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!SFileOpenArchive(wfullPath.c_str(), 0, enableWriting ? 0 : MPQ_OPEN_READ_ONLY, &mpqHandle)) {
|
||||
if (SFileOpenArchive(wfullPath.c_str(), 0, enableWriting ? 0 : MPQ_OPEN_READ_ONLY, &mpqHandle))
|
||||
{
|
||||
#else
|
||||
if (!SFileOpenArchive(fullPath.c_str(), 0, enableWriting ? 0 : MPQ_OPEN_READ_ONLY, &mpqHandle)) {
|
||||
if (SFileOpenArchive(fullPath.c_str(), 0, enableWriting ? 0 : MPQ_OPEN_READ_ONLY, &mpqHandle))
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
Switch::ThrowMissingOTR(fullPath);
|
||||
#endif
|
||||
SPDLOG_ERROR("({}) Failed to open main mpq file {}.", GetLastError(), fullPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
mpqHandles[fullPath] = mpqHandle;
|
||||
mainMPQ = mpqHandle;
|
||||
|
||||
if (genCRCMap) {
|
||||
auto listFile = LoadFile("(listfile)", false);
|
||||
|
||||
std::vector<std::string> lines = StringHelper::Split(std::string(listFile->buffer.get(), listFile->dwBufferSize), "\n");
|
||||
|
||||
for (size_t i = 0; i < lines.size(); i++) {
|
||||
std::string line = StringHelper::Replace(StringHelper::Strip(lines[i], "\r"), "/", "\\");
|
||||
std::string line2 = StringHelper::Replace(line, "\\", "/");
|
||||
|
||||
uint64_t hash = CRC64(StringHelper::Replace(line, "/", "\\").c_str());
|
||||
uint64_t hash2 = CRC64(StringHelper::Replace(line, "\\", "/").c_str());
|
||||
hashes[hash] = line;
|
||||
hashes[hash2] = line2;
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INFO("Opened mpq file {}.", fullPath.c_str());
|
||||
mainMPQ = mpqHandle;
|
||||
if (!PushGameVersion()) {
|
||||
SPDLOG_WARN("Attempted to load invalid OTR file {}", OTRFiles[i].c_str());
|
||||
SFileCloseArchive(mpqHandle);
|
||||
mainMPQ = nullptr;
|
||||
} else {
|
||||
mpqHandles[fullPath] = mpqHandle;
|
||||
if (genCRCMap)
|
||||
{
|
||||
GenerateCRCMap();
|
||||
}
|
||||
baseLoaded = true;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// If we exited the above loop without setting baseLoaded to true, then we've
|
||||
// attemtped to load all the OTRs available to us.
|
||||
if (!baseLoaded) {
|
||||
SPDLOG_ERROR("No valid OTR file was provided.");
|
||||
return false;
|
||||
}
|
||||
for (int j = i; j < OTRFiles.size(); j++) {
|
||||
#ifdef _WIN32
|
||||
std::wstring wfullPath = std::filesystem::absolute(OTRFiles[i]).wstring();
|
||||
#endif
|
||||
#if defined(__SWITCH__)
|
||||
std::string fullPath = OTRFiles[i];
|
||||
#else
|
||||
std::string fullPath = std::filesystem::absolute(OTRFiles[i]).string();
|
||||
#endif
|
||||
if (LoadPatchMPQ(fullPath, true))
|
||||
{
|
||||
SPDLOG_INFO("({}) Patched in mpq file.", fullPath.c_str());
|
||||
}
|
||||
if (genCRCMap)
|
||||
{
|
||||
GenerateCRCMap();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Archive::LoadPatchMPQ(const std::string& path) {
|
||||
bool Archive::LoadPatchMPQ(const std::string& path, bool validateVersion) {
|
||||
HANDLE patchHandle = NULL;
|
||||
#if defined(__SWITCH__)
|
||||
std::string fullPath = path;
|
||||
@ -386,7 +488,16 @@ namespace Ship {
|
||||
#endif
|
||||
SPDLOG_ERROR("({}) Failed to open patch mpq file {} while applying to {}.", GetLastError(), path.c_str(), MainPath.c_str());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// We don't always want to validate the "version" file, only when we're loading standalone OTRs as patches
|
||||
// i.e. Ocarina of Time along with Master Quest.
|
||||
if (validateVersion) {
|
||||
if (!PushGameVersion(patchHandle)) {
|
||||
SPDLOG_WARN("({}) Invalid MQP file.", path.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (!SFileOpenPatchArchive(mainMPQ, wPath.c_str(), "", 0)) {
|
||||
#else
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include "Resource.h"
|
||||
#include "StormLib.h"
|
||||
|
||||
@ -21,8 +22,9 @@ namespace Ship
|
||||
{
|
||||
public:
|
||||
Archive(const std::string& MainPath, bool enableWriting);
|
||||
Archive(const std::string& MainPath, const std::string& PatchesPath, bool enableWriting, bool genCRCMap = true);
|
||||
~Archive();
|
||||
Archive(const std::string &MainPath, const std::string &PatchesPath, const std::unordered_set<uint32_t> &ValidHashes, bool enableWriting, bool genCRCMap = true);
|
||||
Archive(const std::vector<std::string> &OTRFiles, const std::unordered_set<uint32_t> &ValidHashes, bool enableWriting, bool genCRCMap = true);
|
||||
~Archive();
|
||||
|
||||
bool IsMainMPQValid();
|
||||
|
||||
@ -37,19 +39,25 @@ namespace Ship
|
||||
std::vector<SFILE_FIND_DATA> ListFiles(const std::string& searchMask) const;
|
||||
bool HasFile(const std::string& searchMask) const;
|
||||
const std::string* HashToString(uint64_t hash) const;
|
||||
std::vector<uint32_t> gameVersions;
|
||||
protected:
|
||||
bool Load(bool enableWriting, bool genCRCMap);
|
||||
bool Unload();
|
||||
private:
|
||||
std::string MainPath;
|
||||
std::string PatchesPath;
|
||||
std::map<std::string, HANDLE> mpqHandles;
|
||||
std::vector<std::string> OTRFiles;
|
||||
std::unordered_set<uint32_t> ValidHashes;
|
||||
std::map<std::string, HANDLE> mpqHandles;
|
||||
std::vector<std::string> addedFiles;
|
||||
std::unordered_map<uint64_t, std::string> hashes;
|
||||
HANDLE mainMPQ;
|
||||
|
||||
bool LoadMainMPQ(bool enableWriting, bool genCRCMap);
|
||||
bool LoadPatchMPQs();
|
||||
bool LoadPatchMPQ(const std::string& path);
|
||||
};
|
||||
bool LoadPatchMPQ(const std::string& path, bool validateVersion = false);
|
||||
void GenerateCRCMap();
|
||||
bool PushGameVersion(HANDLE mpqHandle = nullptr);
|
||||
std::shared_ptr<File> LoadFileFromHandle(const std::string &filePath, bool includeParent = true, std::shared_ptr<File> FileToLoad = nullptr, HANDLE mpqHandle = nullptr);
|
||||
};
|
||||
}
|
@ -31,10 +31,10 @@ namespace Ship
|
||||
|
||||
ResourceFile::ParseFileBinary(reader, res);
|
||||
|
||||
entry->codec = reader->ReadByte();
|
||||
entry->medium = reader->ReadByte();
|
||||
entry->unk_bit26 = reader->ReadByte();
|
||||
entry->unk_bit25 = reader->ReadByte();
|
||||
entry->codec = reader->ReadInt8();
|
||||
entry->medium = reader->ReadInt8();
|
||||
entry->unk_bit26 = reader->ReadInt8();
|
||||
entry->unk_bit25 = reader->ReadInt8();
|
||||
|
||||
uint32_t dataSize = reader->ReadInt32();
|
||||
|
||||
@ -66,8 +66,8 @@ namespace Ship
|
||||
ResourceFile::ParseFileBinary(reader, res);
|
||||
|
||||
soundFont->id = reader->ReadInt32();
|
||||
soundFont->medium = reader->ReadByte();
|
||||
soundFont->cachePolicy = reader->ReadByte();
|
||||
soundFont->medium = reader->ReadInt8();
|
||||
soundFont->cachePolicy = reader->ReadInt8();
|
||||
soundFont->data1 = reader->ReadInt16();
|
||||
soundFont->data2 = reader->ReadInt16();
|
||||
soundFont->data3 = reader->ReadInt16();
|
||||
@ -85,9 +85,9 @@ namespace Ship
|
||||
|
||||
drum.env = ReadEnvelopeData(reader);
|
||||
|
||||
bool hasSample = reader->ReadByte();
|
||||
bool hasSample = reader->ReadInt8();
|
||||
drum.sampleFileName = reader->ReadString();
|
||||
drum.tuning = reader->ReadSingle();
|
||||
drum.tuning = reader->ReadFloat();
|
||||
|
||||
soundFont->drums.push_back(drum);
|
||||
}
|
||||
@ -105,38 +105,38 @@ namespace Ship
|
||||
entry.env = ReadEnvelopeData(reader);
|
||||
|
||||
{
|
||||
bool hasSFEntry = reader->ReadByte();
|
||||
bool hasSFEntry = reader->ReadInt8();
|
||||
|
||||
if (hasSFEntry)
|
||||
{
|
||||
entry.lowNotesSound = new SoundFontEntry();
|
||||
bool hasSampleRef = reader->ReadByte();
|
||||
bool hasSampleRef = reader->ReadInt8();
|
||||
entry.lowNotesSound->sampleFileName = reader->ReadString();
|
||||
entry.lowNotesSound->tuning = reader->ReadSingle();
|
||||
entry.lowNotesSound->tuning = reader->ReadFloat();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
bool hasSFEntry = reader->ReadByte();
|
||||
bool hasSFEntry = reader->ReadInt8();
|
||||
|
||||
if (hasSFEntry)
|
||||
{
|
||||
entry.normalNotesSound = new SoundFontEntry();
|
||||
bool hasSampleRef = reader->ReadByte();
|
||||
bool hasSampleRef = reader->ReadInt8();
|
||||
entry.normalNotesSound->sampleFileName = reader->ReadString();
|
||||
entry.normalNotesSound->tuning = reader->ReadSingle();
|
||||
entry.normalNotesSound->tuning = reader->ReadFloat();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
bool hasSFEntry = reader->ReadByte();
|
||||
bool hasSFEntry = reader->ReadInt8();
|
||||
|
||||
if (hasSFEntry)
|
||||
{
|
||||
entry.highNotesSound = new SoundFontEntry();
|
||||
bool hasSampleRef = reader->ReadByte();
|
||||
bool hasSampleRef = reader->ReadInt8();
|
||||
entry.highNotesSound->sampleFileName = reader->ReadString();
|
||||
entry.highNotesSound->tuning = reader->ReadSingle();
|
||||
entry.highNotesSound->tuning = reader->ReadFloat();
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,13 +147,13 @@ namespace Ship
|
||||
{
|
||||
SoundFontEntry* entry = new SoundFontEntry();
|
||||
|
||||
bool hasSFEntry = reader->ReadByte();
|
||||
bool hasSFEntry = reader->ReadInt8();
|
||||
|
||||
if (hasSFEntry)
|
||||
{
|
||||
bool hasSampleRef = reader->ReadByte();
|
||||
bool hasSampleRef = reader->ReadInt8();
|
||||
entry->sampleFileName = reader->ReadString();
|
||||
entry->tuning = reader->ReadSingle();
|
||||
entry->tuning = reader->ReadFloat();
|
||||
}
|
||||
|
||||
soundFont->soundEffects.push_back(entry);
|
||||
|
216
libultraship/libultraship/BinaryReader.cpp
Normal file
216
libultraship/libultraship/BinaryReader.cpp
Normal file
@ -0,0 +1,216 @@
|
||||
#include "BinaryReader.h"
|
||||
#include <cmath>
|
||||
#include "Lib/BinaryTools/BinaryTools/BinaryReader.h"
|
||||
|
||||
Ship::BinaryReader::BinaryReader(char* buffer, uint32_t size)
|
||||
{
|
||||
this->buffer = std::vector<char>(buffer, buffer + size);
|
||||
this->reader = std::make_shared<::BinaryReader>(this->buffer.data(), size);
|
||||
}
|
||||
|
||||
void Ship::BinaryReader::SetEndianness(Endianness endianness)
|
||||
{
|
||||
this->endianness = endianness;
|
||||
}
|
||||
|
||||
Ship::Endianness Ship::BinaryReader::GetEndianness() const
|
||||
{
|
||||
return endianness;
|
||||
}
|
||||
|
||||
void Ship::BinaryReader::Seek(int32_t offset, SeekOffsetType seekType) {
|
||||
switch(seekType) {
|
||||
case SeekOffsetType::Current:
|
||||
if (offset < 0) {
|
||||
reader->SeekReverse(-1 * offset);
|
||||
break;
|
||||
}
|
||||
reader->SeekCur(offset);
|
||||
break;
|
||||
case SeekOffsetType::Start:
|
||||
reader->SeekBeg(offset);
|
||||
break;
|
||||
case SeekOffsetType::End:
|
||||
reader->SeekReverse(offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Ship::BinaryReader::GetBaseAddress() {
|
||||
return reader->Position();
|
||||
}
|
||||
|
||||
void Ship::BinaryReader::Read(int32_t length)
|
||||
{
|
||||
reader->ReadFixedLengthString(length);
|
||||
}
|
||||
|
||||
void Ship::BinaryReader::Read(char *buffer, int32_t length)
|
||||
{
|
||||
reader->ReadToMemory(buffer, length);
|
||||
}
|
||||
|
||||
char Ship::BinaryReader::ReadChar()
|
||||
{
|
||||
return reader->ReadChar();
|
||||
}
|
||||
|
||||
int8_t Ship::BinaryReader::ReadInt8()
|
||||
{
|
||||
return reader->ReadInt8();
|
||||
}
|
||||
|
||||
int16_t Ship::BinaryReader::ReadInt16()
|
||||
{
|
||||
int16_t result = 0;
|
||||
this->Read((char*)&result, sizeof(int16_t));
|
||||
if (endianness != Endianness::Native)
|
||||
result = BSWAP16(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t Ship::BinaryReader::ReadInt32()
|
||||
{
|
||||
int32_t result = 0;
|
||||
|
||||
this->Read((char *)&result, sizeof(int32_t));
|
||||
|
||||
if (endianness != Endianness::Native)
|
||||
result = BSWAP32(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t Ship::BinaryReader::ReadUByte()
|
||||
{
|
||||
return reader->ReadUint8();
|
||||
}
|
||||
|
||||
uint16_t Ship::BinaryReader::ReadUInt16()
|
||||
{
|
||||
uint16_t result = 0;
|
||||
|
||||
this->Read((char *)&result, sizeof(uint16_t));
|
||||
|
||||
if (endianness != Endianness::Native)
|
||||
result = BSWAP16(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Ship::BinaryReader::ReadUInt32()
|
||||
{
|
||||
uint32_t result = 0;
|
||||
|
||||
this->Read((char *)&result, sizeof(uint32_t));
|
||||
|
||||
if (endianness != Endianness::Native)
|
||||
result = BSWAP32(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t Ship::BinaryReader::ReadUInt64()
|
||||
{
|
||||
uint64_t result = 0;
|
||||
|
||||
this->Read((char *)&result, sizeof(uint64_t));
|
||||
|
||||
if (endianness != Endianness::Native)
|
||||
result = BSWAP64(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float Ship::BinaryReader::ReadFloat()
|
||||
{
|
||||
float result = NAN;
|
||||
|
||||
this->Read((char *)&result, sizeof(float));
|
||||
|
||||
if (endianness != Endianness::Native)
|
||||
{
|
||||
float tmp;
|
||||
char *dst = (char *)&tmp;
|
||||
char *src = (char *)&result;
|
||||
dst[3] = src[0];
|
||||
dst[2] = src[1];
|
||||
dst[1] = src[2];
|
||||
dst[0] = src[3];
|
||||
result = tmp;
|
||||
}
|
||||
|
||||
if (std::isnan(result))
|
||||
throw std::runtime_error("BinaryReader::ReadSingle(): Error reading stream");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double Ship::BinaryReader::ReadDouble()
|
||||
{
|
||||
double result = NAN;
|
||||
|
||||
this->Read((char *)&result, sizeof(double));
|
||||
|
||||
if (endianness != Endianness::Native)
|
||||
{
|
||||
double tmp;
|
||||
char *dst = (char *)&tmp;
|
||||
char *src = (char *)&result;
|
||||
dst[7] = src[0];
|
||||
dst[6] = src[1];
|
||||
dst[5] = src[2];
|
||||
dst[4] = src[3];
|
||||
dst[3] = src[4];
|
||||
dst[2] = src[5];
|
||||
dst[1] = src[6];
|
||||
dst[0] = src[7];
|
||||
result = tmp;
|
||||
}
|
||||
|
||||
if (std::isnan(result))
|
||||
throw std::runtime_error("BinaryReader::ReadDouble(): Error reading stream");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec3f Ship::BinaryReader::ReadVec3f()
|
||||
{
|
||||
return Vec3f();
|
||||
}
|
||||
|
||||
Vec3s Ship::BinaryReader::ReadVec3s()
|
||||
{
|
||||
return Vec3s(0, 0, 0);
|
||||
}
|
||||
|
||||
Vec3s Ship::BinaryReader::ReadVec3b()
|
||||
{
|
||||
return Vec3s(0, 0, 0);
|
||||
}
|
||||
|
||||
Vec2f Ship::BinaryReader::ReadVec2f()
|
||||
{
|
||||
return Vec2f();
|
||||
}
|
||||
|
||||
Color3b Ship::BinaryReader::ReadColor3b()
|
||||
{
|
||||
return Color3b();
|
||||
}
|
||||
|
||||
std::string Ship::BinaryReader::ReadString()
|
||||
{
|
||||
std::string res;
|
||||
int numChars = reader->ReadInt32();
|
||||
for (int i = 0; i < numChars; i++) {
|
||||
res += reader->ReadChar();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string Ship::BinaryReader::ReadCString()
|
||||
{
|
||||
return reader->ReadNullTerminatedString();
|
||||
}
|
61
libultraship/libultraship/BinaryReader.h
Normal file
61
libultraship/libultraship/BinaryReader.h
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "endianness.h"
|
||||
#include "Vec2f.h"
|
||||
#include "Vec3f.h"
|
||||
#include "Vec3s.h"
|
||||
#include "Color3b.h"
|
||||
|
||||
class BinaryReader;
|
||||
|
||||
namespace Ship {
|
||||
|
||||
enum class SeekOffsetType
|
||||
{
|
||||
Start,
|
||||
Current,
|
||||
End
|
||||
};
|
||||
|
||||
class BinaryReader
|
||||
{
|
||||
public:
|
||||
BinaryReader(char* buffer, uint32_t size);
|
||||
|
||||
void SetEndianness(Endianness endianness);
|
||||
Endianness GetEndianness() const;
|
||||
|
||||
void Seek(int32_t offset, SeekOffsetType seekType);
|
||||
uint32_t GetBaseAddress();
|
||||
|
||||
void Read(int32_t length);
|
||||
void Read(char *buffer, int32_t length);
|
||||
char ReadChar();
|
||||
int8_t ReadInt8();
|
||||
int16_t ReadInt16();
|
||||
int32_t ReadInt32();
|
||||
uint8_t ReadUByte();
|
||||
uint16_t ReadUInt16();
|
||||
uint32_t ReadUInt32();
|
||||
uint64_t ReadUInt64();
|
||||
float ReadFloat();
|
||||
double ReadDouble();
|
||||
Vec3f ReadVec3f();
|
||||
Vec3s ReadVec3s();
|
||||
Vec3s ReadVec3b();
|
||||
Vec2f ReadVec2f();
|
||||
Color3b ReadColor3b();
|
||||
std::string ReadString();
|
||||
std::string ReadCString();
|
||||
|
||||
protected:
|
||||
Endianness endianness = Endianness::Native;
|
||||
|
||||
private:
|
||||
std::vector<char> buffer;
|
||||
std::shared_ptr<::BinaryReader> reader;
|
||||
};
|
||||
}
|
@ -77,6 +77,21 @@ endif ()
|
||||
|
||||
source_group("Source Files\\Audio" FILES ${Source_Files__Audio} ${Source_Files__Audio__extra})
|
||||
|
||||
set (Source_Files__BinaryTools
|
||||
"BinaryReader.cpp"
|
||||
"BinaryReader.h"
|
||||
"Lib/BinaryTools/BinaryTools/Binary.h"
|
||||
"Lib/BinaryTools/BinaryTools/Binary.cpp"
|
||||
"Lib/BinaryTools/BinaryTools/BinaryReader.cpp"
|
||||
"Lib/BinaryTools/BinaryTools/BinaryReader.h"
|
||||
"Lib/BinaryTools/BinaryTools/BinaryWriter.cpp"
|
||||
"Lib/BinaryTools/BinaryTools/BinaryWriter.h"
|
||||
"Lib/BinaryTools/BinaryTools/MemoryBuffer.h"
|
||||
"Lib/BinaryTools/BinaryTools/Span.h"
|
||||
)
|
||||
|
||||
source_group("Source Files\\BinaryTools" FILES ${Source_Files__BinaryTools})
|
||||
|
||||
set(Source_Files__Controller
|
||||
"ControlDeck.cpp"
|
||||
"ControlDeck.h"
|
||||
@ -376,6 +391,7 @@ set(ALL_FILES
|
||||
${Header_Files__Resources__Files}
|
||||
${Source_Files__Audio}
|
||||
${Source_Files__Audio__extra}
|
||||
${Source_Files__BinaryTools}
|
||||
${Source_Files__Controller}
|
||||
${Source_Files__Controller__Attachment}
|
||||
${Source_Files__CustomImpl}
|
||||
|
@ -55,7 +55,7 @@ static const char* GetGameVersionString() {
|
||||
return "IQUE_TW";
|
||||
case OOT_IQUE_CN:
|
||||
return "IQUE_CN";
|
||||
case OOT_UNKNOWN:
|
||||
case UNKNOWN:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ enum class CutsceneCommands
|
||||
Error = 0xFEAF,
|
||||
};
|
||||
|
||||
static inline uint32_t read_CMD_BBBB(BinaryReader* reader)
|
||||
static inline uint32_t read_CMD_BBBB(Ship::BinaryReader* reader)
|
||||
{
|
||||
uint32_t v;
|
||||
reader->Read((char*)&v, sizeof(uint32_t));
|
||||
@ -45,13 +45,13 @@ static inline uint32_t read_CMD_BBBB(BinaryReader* reader)
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline uint32_t read_CMD_BBH(BinaryReader* reader)
|
||||
static inline uint32_t read_CMD_BBH(Ship::BinaryReader* reader)
|
||||
{
|
||||
uint32_t v;
|
||||
reader->Read((char*)&v, sizeof(uint32_t));
|
||||
|
||||
// swap the half word to match endianness
|
||||
if (reader->GetEndianness() != Endianness::Native)
|
||||
if (reader->GetEndianness() != Ship::Endianness::Native)
|
||||
{
|
||||
uint8_t* b = (uint8_t*)&v;
|
||||
uint8_t tmp = b[2];
|
||||
@ -62,13 +62,13 @@ static inline uint32_t read_CMD_BBH(BinaryReader* reader)
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline uint32_t read_CMD_HBB(BinaryReader* reader)
|
||||
static inline uint32_t read_CMD_HBB(Ship::BinaryReader* reader)
|
||||
{
|
||||
uint32_t v;
|
||||
reader->Read((char*)&v, sizeof(uint32_t));
|
||||
|
||||
// swap the half word to match endianness
|
||||
if (reader->GetEndianness() != Endianness::Native)
|
||||
if (reader->GetEndianness() != Ship::Endianness::Native)
|
||||
{
|
||||
uint8_t* b = (uint8_t*)&v;
|
||||
uint8_t tmp = b[0];
|
||||
@ -79,13 +79,13 @@ static inline uint32_t read_CMD_HBB(BinaryReader* reader)
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline uint32_t read_CMD_HH(BinaryReader* reader)
|
||||
static inline uint32_t read_CMD_HH(Ship::BinaryReader* reader)
|
||||
{
|
||||
uint32_t v;
|
||||
reader->Read((char*)&v, sizeof(uint32_t));
|
||||
|
||||
// swap the half words to match endianness
|
||||
if (reader->GetEndianness() != Endianness::Native)
|
||||
if (reader->GetEndianness() != Ship::Endianness::Native)
|
||||
{
|
||||
uint8_t* b = (uint8_t*)&v;
|
||||
uint8_t tmp = b[0];
|
||||
@ -99,7 +99,7 @@ static inline uint32_t read_CMD_HH(BinaryReader* reader)
|
||||
return v;
|
||||
}
|
||||
|
||||
void Ship::CutsceneV0::ParseFileBinary(BinaryReader* reader, Resource* res)
|
||||
void Ship::CutsceneV0::ParseFileBinary(Ship::BinaryReader* reader, Resource* res)
|
||||
{
|
||||
Cutscene* cs = (Cutscene*)res;
|
||||
|
||||
|
@ -10,7 +10,7 @@ namespace Ship
|
||||
ResourceFile::ParseFileBinary(reader, res);
|
||||
|
||||
while (reader->GetBaseAddress() % 8 != 0)
|
||||
reader->ReadByte();
|
||||
reader->ReadInt8();
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -22,13 +22,12 @@ namespace Ship
|
||||
{
|
||||
Resource* ResourceLoader::LoadResource(std::shared_ptr<File> FileToLoad)
|
||||
{
|
||||
auto memStream = std::make_shared<MemoryStream>(FileToLoad->buffer.get(), FileToLoad->dwBufferSize);
|
||||
auto reader = std::make_shared<BinaryReader>(memStream);
|
||||
auto reader = std::make_shared<BinaryReader>(FileToLoad->buffer.get(), FileToLoad->dwBufferSize);
|
||||
|
||||
Endianness endianness = (Endianness)reader->ReadByte();
|
||||
Endianness endianness = (Endianness)reader->ReadInt8();
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
reader->ReadByte();
|
||||
reader->ReadInt8();
|
||||
|
||||
reader->SetEndianness(endianness);
|
||||
|
||||
|
@ -20,6 +20,6 @@
|
||||
#define OOT_PAL_GC_MQ_DBG 0x917D18F6
|
||||
#define OOT_IQUE_TW 0x3D81FB3E
|
||||
#define OOT_IQUE_CN 0xB1E1E07B
|
||||
#define OOT_UNKNOWN 0xFFFFFFFF
|
||||
#define UNKNOWN 0xFFFFFFFF
|
||||
|
||||
#endif
|
||||
|
276
libultraship/libultraship/Lib/BinaryTools/.gitignore
vendored
Normal file
276
libultraship/libultraship/Lib/BinaryTools/.gitignore
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
#*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
CodeGraphData/
|
||||
|
||||
# Documentation build files and tool test output
|
||||
\.vscode/settings\.json
|
||||
Docs/_build/
|
||||
\.vscode/
|
||||
Tools/Player\.rst
|
||||
Tools/TestFile\.h
|
||||
Proxy DLL Loader/Appveyor/
|
||||
|
||||
RSL/Appveyor/
|
||||
|
||||
Appveyor/
|
||||
|
||||
TODO
|
||||
|
||||
BinaryTools/TestBin2.bin
|
31
libultraship/libultraship/Lib/BinaryTools/BinaryTools.sln
Normal file
31
libultraship/libultraship/Lib/BinaryTools/BinaryTools.sln
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29509.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BinaryTools", "BinaryTools\BinaryTools.vcxproj", "{DC3D45C9-4E30-4E00-9E95-76FCF3754849}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DC3D45C9-4E30-4E00-9E95-76FCF3754849}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{DC3D45C9-4E30-4E00-9E95-76FCF3754849}.Debug|x64.Build.0 = Debug|x64
|
||||
{DC3D45C9-4E30-4E00-9E95-76FCF3754849}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{DC3D45C9-4E30-4E00-9E95-76FCF3754849}.Debug|x86.Build.0 = Debug|Win32
|
||||
{DC3D45C9-4E30-4E00-9E95-76FCF3754849}.Release|x64.ActiveCfg = Release|x64
|
||||
{DC3D45C9-4E30-4E00-9E95-76FCF3754849}.Release|x64.Build.0 = Release|x64
|
||||
{DC3D45C9-4E30-4E00-9E95-76FCF3754849}.Release|x86.ActiveCfg = Release|Win32
|
||||
{DC3D45C9-4E30-4E00-9E95-76FCF3754849}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A5347320-02AF-4DBE-B357-2B183ABC44F7}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -0,0 +1,23 @@
|
||||
#include "Binary.h"
|
||||
#include <fstream>
|
||||
|
||||
Span<char> ReadAllBytes(const std::string& filePath)
|
||||
{
|
||||
std::ifstream file(filePath, std::ios::ate | std::ios::binary);
|
||||
|
||||
if (!file.is_open())
|
||||
{
|
||||
throw std::runtime_error("Failed to open file!"); //Todo: Note file name/path in error. Maybe better to just return an optional
|
||||
}
|
||||
|
||||
size_t fileSize = (size_t)file.tellg();
|
||||
char* buffer = new char[fileSize];
|
||||
|
||||
file.seekg(0);
|
||||
file.read(buffer, fileSize);
|
||||
file.close();
|
||||
|
||||
|
||||
|
||||
return Span<char>(buffer, fileSize);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "Span.h"
|
||||
|
||||
Span<char> ReadAllBytes(const std::string& filePath);
|
@ -0,0 +1,266 @@
|
||||
#include "BinaryReader.h"
|
||||
#include "MemoryBuffer.h"
|
||||
|
||||
BinaryReader::BinaryReader(std::string_view inputPath)
|
||||
{
|
||||
stream_ = new std::ifstream(std::string(inputPath), std::ifstream::in | std::ifstream::binary);
|
||||
}
|
||||
|
||||
BinaryReader::BinaryReader(char* buffer, uint32_t sizeInBytes)
|
||||
{
|
||||
buffer_ = new basic_memstreambuf(buffer, sizeInBytes);
|
||||
stream_ = new std::istream(buffer_);
|
||||
}
|
||||
|
||||
BinaryReader::BinaryReader(std::span<uint8_t> buffer)
|
||||
{
|
||||
buffer_ = new basic_memstreambuf((char*)buffer.data(), buffer.size_bytes());
|
||||
stream_ = new std::istream(buffer_);
|
||||
}
|
||||
|
||||
BinaryReader::~BinaryReader()
|
||||
{
|
||||
delete stream_;
|
||||
if (buffer_)
|
||||
delete buffer_;
|
||||
}
|
||||
|
||||
uint8_t BinaryReader::ReadUint8()
|
||||
{
|
||||
uint8_t output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 1);
|
||||
return output;
|
||||
}
|
||||
|
||||
uint16_t BinaryReader::ReadUint16()
|
||||
{
|
||||
uint16_t output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 2);
|
||||
return output;
|
||||
}
|
||||
|
||||
uint32_t BinaryReader::ReadUint32()
|
||||
{
|
||||
//Todo: See if using static or class var speeds these up
|
||||
uint32_t output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 4);
|
||||
return output;
|
||||
}
|
||||
|
||||
uint64_t BinaryReader::ReadUint64()
|
||||
{
|
||||
uint64_t output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 8);
|
||||
return output;
|
||||
}
|
||||
|
||||
int8_t BinaryReader::ReadInt8()
|
||||
{
|
||||
int8_t output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 1);
|
||||
return output;
|
||||
}
|
||||
|
||||
int16_t BinaryReader::ReadInt16()
|
||||
{
|
||||
int16_t output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 2);
|
||||
return output;
|
||||
}
|
||||
|
||||
int32_t BinaryReader::ReadInt32()
|
||||
{
|
||||
int32_t output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 4);
|
||||
return output;
|
||||
}
|
||||
|
||||
int64_t BinaryReader::ReadInt64()
|
||||
{
|
||||
int64_t output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 8);
|
||||
return output;
|
||||
}
|
||||
|
||||
char BinaryReader::ReadChar()
|
||||
{
|
||||
char output;
|
||||
stream_->read(&output, 1);
|
||||
return output;
|
||||
}
|
||||
|
||||
wchar_t BinaryReader::ReadCharWide()
|
||||
{
|
||||
wchar_t output;
|
||||
stream_->read((char*)&output, 2);
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string BinaryReader::ReadNullTerminatedString()
|
||||
{
|
||||
std::string output;
|
||||
char charBuffer = 0;
|
||||
while(PeekChar() != '\0')
|
||||
{
|
||||
stream_->read(&charBuffer, 1);
|
||||
output.push_back(charBuffer);
|
||||
}
|
||||
Skip(1); //Move past null terminator
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string BinaryReader::ReadFixedLengthString(size_t length)
|
||||
{
|
||||
std::string output;
|
||||
output.reserve(length);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
char charBuffer;
|
||||
stream_->read(&charBuffer, 1);
|
||||
output.push_back(charBuffer);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::wstring BinaryReader::ReadNullTerminatedStringWide()
|
||||
{
|
||||
std::wstring output;
|
||||
wchar_t charBuffer = 0;
|
||||
while (PeekCharWide() != '\0')
|
||||
{
|
||||
stream_->read((char*)&charBuffer, 2);
|
||||
output.push_back(charBuffer);
|
||||
}
|
||||
Skip(2); //Move past null terminator
|
||||
return output;
|
||||
}
|
||||
|
||||
std::wstring BinaryReader::ReadFixedLengthStringWide(size_t length)
|
||||
{
|
||||
std::wstring output;
|
||||
output.reserve(length);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
wchar_t charBuffer;
|
||||
stream_->read((char*)&charBuffer, 2);
|
||||
output.push_back(charBuffer);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<std::string> BinaryReader::ReadSizedStringList(size_t listSize)
|
||||
{
|
||||
std::vector<std::string> stringList = { };
|
||||
if (listSize == 0)
|
||||
return stringList;
|
||||
|
||||
size_t startPos = Position();
|
||||
while (Position() - startPos < listSize)
|
||||
{
|
||||
stringList.push_back(ReadNullTerminatedString());
|
||||
while (Position() - startPos < listSize)
|
||||
{
|
||||
//TODO: See if Align(4) would accomplish the same. This is really for RfgTools++ since many RFG formats have sized string lists
|
||||
//Sometimes names have extra null bytes after them for some reason. Simple way to handle this
|
||||
if (PeekChar() == '\0')
|
||||
Skip(1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return stringList;
|
||||
}
|
||||
|
||||
char BinaryReader::PeekChar()
|
||||
{
|
||||
char output = ReadChar();
|
||||
SeekReverse(1);
|
||||
return output;
|
||||
}
|
||||
|
||||
uint32_t BinaryReader::PeekUint32()
|
||||
{
|
||||
uint32_t output = ReadUint32();
|
||||
SeekReverse(4);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
wchar_t BinaryReader::PeekCharWide()
|
||||
{
|
||||
wchar_t output = ReadCharWide();
|
||||
SeekReverse(2);
|
||||
return output;
|
||||
}
|
||||
|
||||
float BinaryReader::ReadFloat()
|
||||
{
|
||||
float output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 4);
|
||||
return output;
|
||||
}
|
||||
|
||||
double BinaryReader::ReadDouble()
|
||||
{
|
||||
double output;
|
||||
stream_->read(reinterpret_cast<char*>(&output), 8);
|
||||
return output;
|
||||
}
|
||||
|
||||
void BinaryReader::ReadToMemory(void* destination, size_t size)
|
||||
{
|
||||
stream_->read(static_cast<char*>(destination), size);
|
||||
}
|
||||
|
||||
void BinaryReader::SeekBeg(size_t absoluteOffset)
|
||||
{
|
||||
stream_->seekg(absoluteOffset, std::ifstream::beg);
|
||||
}
|
||||
|
||||
void BinaryReader::SeekCur(size_t relativeOffset)
|
||||
{
|
||||
stream_->seekg(relativeOffset, std::ifstream::cur);
|
||||
}
|
||||
|
||||
void BinaryReader::SeekReverse(size_t relativeOffset)
|
||||
{
|
||||
const size_t delta = std::min(Position(), relativeOffset); //Don't allow seeking before the beginning of the stream
|
||||
const size_t targetOffset = Position() - delta;
|
||||
SeekBeg(targetOffset);
|
||||
}
|
||||
|
||||
void BinaryReader::Skip(size_t bytesToSkip)
|
||||
{
|
||||
stream_->seekg(bytesToSkip, std::ifstream::cur);
|
||||
}
|
||||
|
||||
size_t BinaryReader::Align(size_t alignmentValue)
|
||||
{
|
||||
//Todo: Test that this math is working as expected. Had bug here in C# version
|
||||
const size_t remainder = stream_->tellg() % alignmentValue;
|
||||
size_t paddingSize = remainder > 0 ? alignmentValue - remainder : 0;
|
||||
Skip(paddingSize);
|
||||
return paddingSize;
|
||||
}
|
||||
|
||||
size_t BinaryReader::Position() const
|
||||
{
|
||||
return stream_->tellg();
|
||||
}
|
||||
|
||||
size_t BinaryReader::Length()
|
||||
{
|
||||
//Save current position
|
||||
size_t realPosition = Position();
|
||||
|
||||
//Seek to end of file and get position (the length)
|
||||
stream_->seekg(0, std::ios::end);
|
||||
size_t endPosition = Position();
|
||||
|
||||
//Seek back to real pos and return length
|
||||
if(realPosition != endPosition)
|
||||
SeekBeg(realPosition);
|
||||
|
||||
return endPosition;
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "MemoryBuffer.h"
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <istream>
|
||||
|
||||
struct MemoryBuffer;
|
||||
|
||||
|
||||
//Class that can read binary data either from a file or from a fixed size buffer
|
||||
//depending on the constructor used.
|
||||
class BinaryReader
|
||||
{
|
||||
public:
|
||||
//Reads binary data from file at path
|
||||
BinaryReader(std::string_view inputPath);
|
||||
//Reads binary data from fixed size memory buffer
|
||||
BinaryReader(char* buffer, uint32_t sizeInBytes);
|
||||
//Reads binary data from fixed size memory buffer
|
||||
BinaryReader(std::span<uint8_t> buffer);
|
||||
~BinaryReader();
|
||||
|
||||
[[nodiscard]] uint8_t ReadUint8();
|
||||
[[nodiscard]] uint16_t ReadUint16();
|
||||
[[nodiscard]] uint32_t ReadUint32();
|
||||
[[nodiscard]] uint64_t ReadUint64();
|
||||
|
||||
[[nodiscard]] int8_t ReadInt8();
|
||||
[[nodiscard]] int16_t ReadInt16();
|
||||
[[nodiscard]] int32_t ReadInt32();
|
||||
[[nodiscard]] int64_t ReadInt64();
|
||||
|
||||
[[nodiscard]] char ReadChar();
|
||||
[[nodiscard]] wchar_t ReadCharWide();
|
||||
[[nodiscard]] std::string ReadNullTerminatedString();
|
||||
[[nodiscard]] std::string ReadFixedLengthString(size_t length);
|
||||
[[nodiscard]] std::wstring ReadNullTerminatedStringWide();
|
||||
[[nodiscard]] std::wstring ReadFixedLengthStringWide(size_t length);
|
||||
[[nodiscard]] std::vector<std::string> ReadSizedStringList(size_t listSize);
|
||||
[[nodiscard]] char PeekChar();
|
||||
[[nodiscard]] uint32_t PeekUint32();
|
||||
[[nodiscard]] wchar_t PeekCharWide();
|
||||
|
||||
[[nodiscard]] float ReadFloat();
|
||||
[[nodiscard]] double ReadDouble();
|
||||
|
||||
void ReadToMemory(void* destination, size_t size);
|
||||
|
||||
void SeekBeg(size_t absoluteOffset);
|
||||
void SeekCur(size_t relativeOffset);
|
||||
void SeekReverse(size_t relativeOffset); //Move backwards from the current stream position
|
||||
void Skip(size_t bytesToSkip);
|
||||
size_t Align(size_t alignmentValue = 2048);
|
||||
|
||||
size_t Position() const;
|
||||
size_t Length();
|
||||
|
||||
private:
|
||||
std::istream* stream_ = nullptr;
|
||||
basic_memstreambuf* buffer_ = nullptr;
|
||||
};
|
||||
|
@ -0,0 +1,162 @@
|
||||
#include "BinaryWriter.h"
|
||||
#include "BinaryReader.h"
|
||||
#include "Binary.h"
|
||||
#include <array>
|
||||
|
||||
struct TestPod
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
uint32_t cash;
|
||||
int32_t score;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("**** Test 1 - Write + Read a few values ****\n");
|
||||
//Test writing a few values and reading them back
|
||||
{
|
||||
{
|
||||
printf("Writing some values to file... ");
|
||||
BinaryWriter writer("./TestBin1.bin");
|
||||
writer.WriteFloat(1232.3f);
|
||||
writer.WriteFloat(300.7f);
|
||||
writer.WriteFloat(1680.0f);
|
||||
writer.WriteUint32(8000);
|
||||
writer.WriteInt32(-2003443);
|
||||
printf("Done!\n");
|
||||
}
|
||||
{
|
||||
printf("Reading those values back...\n");
|
||||
BinaryReader reader("./TestBin1.bin");
|
||||
printf("Float: %f\n", reader.ReadFloat());
|
||||
printf("Float: %f\n", reader.ReadFloat());
|
||||
printf("Float: %f\n", reader.ReadFloat());
|
||||
printf("Uint32: %d\n", reader.ReadUint32());
|
||||
printf("Int32: %d\n", reader.ReadInt32());
|
||||
printf("Done!\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
printf("\n\n**** Test 2 - Write + Read a POD struct directly to/from memory ****\n");
|
||||
//Test writing a struct from memory, reading it back and casting the data onto it
|
||||
{
|
||||
//Write data
|
||||
{
|
||||
TestPod writeData = {};
|
||||
writeData.x = 1234.44f;
|
||||
writeData.y = 1734.44f;
|
||||
writeData.z = 22334.44f;
|
||||
writeData.cash = 1003;
|
||||
writeData.score = -64230;
|
||||
printf("sizeof(TestPod) = %zd\n", sizeof(TestPod));
|
||||
|
||||
printf("Writing POD struct from memory... ");
|
||||
BinaryWriter writer("./TestBin2.bin");
|
||||
writer.WriteFromMemory(&writeData, sizeof(TestPod));
|
||||
printf("Done!\n\n");
|
||||
}
|
||||
//Read it back
|
||||
{
|
||||
TestPod readData = {};
|
||||
readData.x = 0.00000000f;
|
||||
readData.y = 0.00000000f;
|
||||
readData.z = 0.00000000f;
|
||||
readData.cash = 0;
|
||||
readData.score = 0;
|
||||
|
||||
printf("Reading back data directly into POD struct location in memory... ");
|
||||
BinaryReader reader("./TestBin2.bin");
|
||||
reader.ReadToMemory(&readData, sizeof(TestPod));
|
||||
printf("Done!\n");
|
||||
printf("Printing values...\n");
|
||||
printf("Float: %f\n", readData.x);
|
||||
printf("Float: %f\n", readData.y);
|
||||
printf("Float: %f\n", readData.z);
|
||||
printf("Uint32: %d\n", readData.cash);
|
||||
printf("Int32: %d\n", readData.score);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\n**** Test 3 - Read a POD struct directly to/from memory from handmade binary file ****\n");
|
||||
//Test reading data from handmade binary file straight into POD struct memory location
|
||||
{
|
||||
{
|
||||
TestPod readData = {};
|
||||
readData.x = 0.00000000f;
|
||||
readData.y = 0.00000000f;
|
||||
readData.z = 0.00000000f;
|
||||
readData.cash = 0;
|
||||
readData.score = 0;
|
||||
|
||||
printf("Reading data directly into POD struct location in memory... ");
|
||||
BinaryReader reader("./TestBin3.bin");
|
||||
reader.ReadToMemory(&readData, sizeof(TestPod));
|
||||
printf("Done!\n");
|
||||
printf("Printing values...\n");
|
||||
printf("Float: %f\n", readData.x);
|
||||
printf("Float: %f\n", readData.y);
|
||||
printf("Float: %f\n", readData.z);
|
||||
printf("Uint32: %d\n", readData.cash);
|
||||
printf("Int32: %d\n", readData.score);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\n**** Test 4 - Read a POD struct from a file to memory and read data from that memory area with BinaryReader ****\n");
|
||||
//Test reading data from handmade binary file straight into POD struct memory location
|
||||
{
|
||||
{
|
||||
TestPod readData = {};
|
||||
readData.x = 0.00000000f;
|
||||
readData.y = 0.00000000f;
|
||||
readData.z = 0.00000000f;
|
||||
readData.cash = 0;
|
||||
readData.score = 0;
|
||||
|
||||
printf("Reading data directly into memory... ");
|
||||
printf("Done!\n");
|
||||
auto span = ReadAllBytes("./TestBin3.bin");
|
||||
printf("Reading values of memory buffer with BinaryReader... ");
|
||||
printf("Done!\n");
|
||||
|
||||
BinaryReader reader(span.Data(), (uint32_t)span.Size());
|
||||
printf("Printing values...\n");
|
||||
printf("Float: %f\n", reader.ReadFloat());
|
||||
printf("Float: %f\n", reader.ReadFloat());
|
||||
printf("Float: %f\n", reader.ReadFloat());
|
||||
printf("Uint32: %d\n", reader.ReadUint32());
|
||||
printf("Int32: %d\n", reader.ReadInt32());
|
||||
delete span.Data();
|
||||
}
|
||||
}
|
||||
|
||||
struct test
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
std::array<test, 3> testArray;
|
||||
testArray[0] = test{ 2, 3 };
|
||||
testArray[1] = test{ 4, 5 };
|
||||
testArray[2] = test{ 6, 7 };
|
||||
//Intentionally specifying size of 2 here to see if end() actually points to the end of last element of the span
|
||||
Span<test> testSpan(testArray.data(), 2);
|
||||
|
||||
test* begin = testSpan.begin();
|
||||
test* end = testSpan.end();
|
||||
auto& front = testSpan.front();
|
||||
auto& back = testSpan.back();
|
||||
|
||||
auto& zero = testSpan[0];
|
||||
auto& one = testSpan[1];
|
||||
auto& two = testSpan[2];
|
||||
|
||||
printf("Testing use of range based for loops with Span<T>...\n");
|
||||
for (auto& val : testSpan)
|
||||
printf("value: {a: %d, b: %d}\n", val.a, val.b);
|
||||
|
||||
auto a = 2;
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{DC3D45C9-4E30-4E00-9E95-76FCF3754849}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>BinaryTools</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<EnableModules>true</EnableModules>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<EnableModules>true</EnableModules>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<EnableModules>true</EnableModules>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<EnableModules>true</EnableModules>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Binary.cpp" />
|
||||
<ClCompile Include="BinaryReader.cpp" />
|
||||
<ClCompile Include="BinaryTools.cpp" />
|
||||
<ClCompile Include="BinaryWriter.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Binary.h" />
|
||||
<ClInclude Include="BinaryReader.h" />
|
||||
<ClInclude Include="BinaryWriter.h" />
|
||||
<ClInclude Include="MemoryBuffer.h" />
|
||||
<ClInclude Include="Span.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="src">
|
||||
<UniqueIdentifier>{54101738-c0ef-40a7-be61-cf8ae2cbcae3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="src\BinaryReader">
|
||||
<UniqueIdentifier>{5568f67e-fdd2-4c16-bb85-bf72217b642c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="src\BinaryWriter">
|
||||
<UniqueIdentifier>{03c417c9-055a-45f5-83d9-42357834c20e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="src\helpers">
|
||||
<UniqueIdentifier>{063757fd-06fb-4f35-8362-005b4a8261b9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BinaryTools.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BinaryReader.cpp">
|
||||
<Filter>src\BinaryReader</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BinaryWriter.cpp">
|
||||
<Filter>src\BinaryWriter</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Binary.cpp">
|
||||
<Filter>src\helpers</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BinaryReader.h">
|
||||
<Filter>src\BinaryReader</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BinaryWriter.h">
|
||||
<Filter>src\BinaryWriter</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Binary.h">
|
||||
<Filter>src\helpers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MemoryBuffer.h">
|
||||
<Filter>src\helpers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Span.h">
|
||||
<Filter>src\helpers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -0,0 +1,183 @@
|
||||
#include "BinaryWriter.h"
|
||||
#include "MemoryBuffer.h"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
BinaryWriter::BinaryWriter(std::string_view inputPath, bool truncate)
|
||||
{
|
||||
//Can't simply exclude the truncate flag when !truncate. More details here: https://stackoverflow.com/a/57070159
|
||||
int flags = 0;
|
||||
if (truncate)
|
||||
flags = std::ofstream::out | std::ofstream::binary | std::ofstream::trunc; //Clears existing contents of the file
|
||||
else
|
||||
flags = std::ofstream::in | std::ofstream::out | std::ofstream::binary;
|
||||
|
||||
//If not truncating and the file doesn't exist, then opening will fail. So we create the file first if it doesn't exist
|
||||
if (!truncate && !std::filesystem::exists(inputPath))
|
||||
{
|
||||
std::fstream f;
|
||||
f.open(std::string(inputPath), std::fstream::out);
|
||||
f.close();
|
||||
}
|
||||
|
||||
stream_ = new std::ofstream(std::string(inputPath), (std::ios_base::openmode)flags);
|
||||
}
|
||||
|
||||
BinaryWriter::BinaryWriter(char* buffer, uint32_t sizeInBytes)
|
||||
{
|
||||
buffer_ = new MemoryBuffer(buffer, sizeInBytes);
|
||||
stream_ = new std::ostream(buffer_);
|
||||
}
|
||||
|
||||
BinaryWriter::~BinaryWriter()
|
||||
{
|
||||
delete stream_;
|
||||
if (buffer_)
|
||||
delete[] buffer_;
|
||||
}
|
||||
|
||||
void BinaryWriter::Flush()
|
||||
{
|
||||
stream_->flush();
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteUint8(uint8_t value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 1);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteUint16(uint16_t value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 2);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteUint32(uint32_t value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 4);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteUint64(uint64_t value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 8);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteInt8(int8_t value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 1);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteInt16(int16_t value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 2);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteInt32(int32_t value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 4);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteInt64(int64_t value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 8);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteChar(char value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 1);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteNullTerminatedString(const std::string& value)
|
||||
{
|
||||
stream_->write(value.data(), value.size());
|
||||
WriteChar('\0');
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteFixedLengthString(const std::string& value)
|
||||
{
|
||||
stream_->write(value.data(), value.size());
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteFloat(float value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 4);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteDouble(double value)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(&value), 8);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteFromMemory(const void* data, size_t size)
|
||||
{
|
||||
stream_->write(reinterpret_cast<const char*>(data), size);
|
||||
}
|
||||
|
||||
void BinaryWriter::SeekBeg(size_t absoluteOffset)
|
||||
{
|
||||
stream_->seekp(absoluteOffset, std::ifstream::beg);
|
||||
}
|
||||
|
||||
void BinaryWriter::SeekCur(size_t relativeOffset)
|
||||
{
|
||||
stream_->seekp(relativeOffset, std::ifstream::cur);
|
||||
}
|
||||
|
||||
void BinaryWriter::Skip(size_t bytesToSkip)
|
||||
{
|
||||
size_t position = Position();
|
||||
size_t length = Length();
|
||||
|
||||
//If we're skipped past the end of the stream then skip what's available and write null bytes for the rest
|
||||
if (position + bytesToSkip > length)
|
||||
{
|
||||
size_t bytesAvailable = length - position;
|
||||
size_t bytesNeeded = bytesToSkip - bytesAvailable;
|
||||
|
||||
stream_->seekp(bytesAvailable, std::ifstream::cur);
|
||||
WriteNullBytes(bytesNeeded);
|
||||
}
|
||||
else
|
||||
stream_->seekp(bytesToSkip, std::ifstream::cur);
|
||||
}
|
||||
|
||||
void BinaryWriter::WriteNullBytes(size_t bytesToWrite)
|
||||
{
|
||||
//Todo: See if quicker to allocate array of zeros and use WriteFromMemory
|
||||
for (size_t i = 0; i < bytesToWrite; i++)
|
||||
WriteUint8(0);
|
||||
}
|
||||
|
||||
size_t BinaryWriter::CalcAlign(size_t position, size_t alignmentValue)
|
||||
{
|
||||
const size_t remainder = position % alignmentValue;
|
||||
size_t paddingSize = remainder > 0 ? alignmentValue - remainder : 0;
|
||||
return paddingSize;
|
||||
}
|
||||
|
||||
size_t BinaryWriter::Align(size_t alignmentValue)
|
||||
{
|
||||
const size_t paddingSize = CalcAlign(stream_->tellp(), alignmentValue);
|
||||
Skip(paddingSize);
|
||||
return paddingSize;
|
||||
}
|
||||
|
||||
size_t BinaryWriter::Position() const
|
||||
{
|
||||
return stream_->tellp();
|
||||
}
|
||||
|
||||
size_t BinaryWriter::Length()
|
||||
{
|
||||
//Save current position
|
||||
size_t realPosition = Position();
|
||||
|
||||
//Seek to end of file and get position (the length)
|
||||
stream_->seekp(0, std::ios::end);
|
||||
size_t endPosition = Position();
|
||||
|
||||
//Seek back to real pos and return length
|
||||
if (realPosition != endPosition)
|
||||
SeekBeg(realPosition);
|
||||
|
||||
return endPosition;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <span>
|
||||
|
||||
struct MemoryBuffer;
|
||||
|
||||
//Class that can write binary data either from a file or from a fixed size buffer
|
||||
//depending on the constructor used.
|
||||
class BinaryWriter
|
||||
{
|
||||
public:
|
||||
//Writes binary data from file at path. If truncate == true any existing file contents will be cleared
|
||||
BinaryWriter(std::string_view inputPath, bool truncate = true);
|
||||
//Writes binary data from fixed size memory buffer
|
||||
BinaryWriter(char* buffer, uint32_t sizeInBytes);
|
||||
~BinaryWriter();
|
||||
|
||||
void Flush();
|
||||
|
||||
void WriteUint8(uint8_t value);
|
||||
void WriteUint16(uint16_t value);
|
||||
void WriteUint32(uint32_t value);
|
||||
void WriteUint64(uint64_t value);
|
||||
|
||||
void WriteInt8(int8_t value);
|
||||
void WriteInt16(int16_t value);
|
||||
void WriteInt32(int32_t value);
|
||||
void WriteInt64(int64_t value);
|
||||
|
||||
void WriteChar(char value);
|
||||
//Write string to output with null terminator
|
||||
void WriteNullTerminatedString(const std::string& value);
|
||||
//Write string to output without null terminator
|
||||
void WriteFixedLengthString(const std::string& value);
|
||||
|
||||
void WriteFloat(float value);
|
||||
void WriteDouble(double value);
|
||||
|
||||
void WriteFromMemory(const void* data, size_t size);
|
||||
|
||||
template<typename T>
|
||||
void Write(const T& data)
|
||||
{
|
||||
//Don't allow T to be a pointer to avoid accidentally writing the value of a pointer instead of what it points to.
|
||||
static_assert(!std::is_pointer<T>(), "BinaryWriter::Write<T> requires T to be a non pointer type.");
|
||||
WriteFromMemory(&data, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void WriteSpan(std::span<T> data)
|
||||
{
|
||||
WriteFromMemory(data.data(), data.size_bytes());
|
||||
}
|
||||
|
||||
void SeekBeg(size_t absoluteOffset);
|
||||
void SeekCur(size_t relativeOffset);
|
||||
void Skip(size_t bytesToSkip);
|
||||
void WriteNullBytes(size_t bytesToWrite);
|
||||
//Static method for calculating alignment pad from pos and alignment. Does not change position since static
|
||||
static size_t CalcAlign(size_t position, size_t alignmentValue = 2048);
|
||||
//Aligns stream to alignment value. Returns padding byte count
|
||||
size_t Align(size_t alignmentValue = 2048);
|
||||
|
||||
size_t Position() const;
|
||||
size_t Length();
|
||||
|
||||
private:
|
||||
std::ostream* stream_ = nullptr;
|
||||
MemoryBuffer* buffer_ = nullptr;
|
||||
};
|
||||
|
@ -0,0 +1,250 @@
|
||||
#pragma once
|
||||
//Somehow the windows min and max macros are being leaked into here regardless of where I define NOMINMAX. This works as a fix for the moment
|
||||
#undef min
|
||||
#undef max
|
||||
#include <streambuf>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
|
||||
//Simple wrapper around std::streambuf representing a memory buffer.
|
||||
//Used by BinaryReader and BinaryWriter
|
||||
struct MemoryBuffer : std::streambuf
|
||||
{
|
||||
MemoryBuffer(char* begin, char* end)
|
||||
{
|
||||
this->setg(begin, begin, end);
|
||||
}
|
||||
MemoryBuffer(char* begin, uint32_t sizeInBytes)
|
||||
{
|
||||
this->setg(begin, begin, begin + sizeInBytes);
|
||||
}
|
||||
};
|
||||
|
||||
//Used by BinaryReader for reading from in memory buffers.
|
||||
//Source: https://gist.github.com/polyvertex/ce86fddfa28edcfb19a77f9024a5461c
|
||||
|
||||
// A memory stream buffer class compliant with `std::basic_streambuf`.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// std::vector<char> data;
|
||||
// // ... fill-in *data* here ...
|
||||
// xx::basic_memstreambuf<data.value_type> sbuf(&data[0], data.size());
|
||||
// std::istream in(&sbuf);
|
||||
// // ... read data from *in* ...
|
||||
//
|
||||
// Useful references:
|
||||
// * Deriving from std::streambuf
|
||||
// https://artofcode.wordpress.com/2010/12/12/deriving-from-stdstreambuf/
|
||||
// * A beginner's guide to writing a custom stream buffer (std::streambuf)
|
||||
// http://www.voidcn.com/article/p-vjnlygmc-gy.html
|
||||
// * membuf.cpp
|
||||
// https://gist.github.com/mlfarrell/28ea0e7b10756042956b579781ac0dd8
|
||||
// * Memory streambuf and stream
|
||||
// https://codereview.stackexchange.com/questions/138479/memory-streambuf-and-stream
|
||||
|
||||
class basic_memstreambuf : public std::streambuf
|
||||
{
|
||||
public:
|
||||
using BaseT = std::streambuf;
|
||||
|
||||
using BaseT::int_type;
|
||||
using BaseT::traits_type;
|
||||
|
||||
public:
|
||||
basic_memstreambuf()
|
||||
: BaseT()
|
||||
{ }
|
||||
|
||||
explicit basic_memstreambuf(const basic_memstreambuf& rhs)
|
||||
: BaseT(rhs)
|
||||
{ }
|
||||
|
||||
// non-standard
|
||||
explicit basic_memstreambuf(const std::basic_string<char_type>& s)
|
||||
: BaseT()
|
||||
{
|
||||
//assert(!s.empty());
|
||||
setg(
|
||||
const_cast<char_type*>(&s.front()),
|
||||
const_cast<char_type*>(&s.front()),
|
||||
const_cast<char_type*>(&s.back()));
|
||||
}
|
||||
|
||||
// non-standard
|
||||
basic_memstreambuf(const char_type* s, std::streamsize n) : BaseT()
|
||||
{
|
||||
// assert(s);
|
||||
//assert(n > 0);
|
||||
setg(
|
||||
const_cast<char_type*>(s),
|
||||
const_cast<char_type*>(s),
|
||||
const_cast<char_type*>(s + n));
|
||||
}
|
||||
|
||||
// non-standard
|
||||
basic_memstreambuf(const char_type* begin, const char_type* end)
|
||||
: BaseT()
|
||||
{
|
||||
//assert(begin);
|
||||
//assert(end);
|
||||
//assert(begin < end);
|
||||
|
||||
// check size
|
||||
const std::uintmax_t count = end - begin;
|
||||
const std::uintmax_t maxValue = static_cast<std::uintmax_t>(std::numeric_limits<std::streamsize>::max());
|
||||
if (count > maxValue)
|
||||
{
|
||||
throw std::invalid_argument("basic_memstreambuf too big");
|
||||
}
|
||||
|
||||
setg(
|
||||
const_cast<char_type*>(begin),
|
||||
const_cast<char_type*>(begin),
|
||||
const_cast<char_type*>(end));
|
||||
}
|
||||
|
||||
basic_memstreambuf& operator=(const basic_memstreambuf&) = delete;
|
||||
|
||||
|
||||
protected:
|
||||
virtual std::streamsize showmanyc() override
|
||||
{
|
||||
const auto* ptr = gptr();
|
||||
const auto* end = egptr();
|
||||
|
||||
//assert(ptr <= end);
|
||||
|
||||
return (ptr <= end) ? (end - ptr) : 0;
|
||||
}
|
||||
|
||||
virtual int_type underflow() override
|
||||
{
|
||||
const auto* ptr = gptr();
|
||||
|
||||
if (ptr >= egptr())
|
||||
return traits_type::eof();
|
||||
|
||||
return traits_type::to_int_type(*ptr);
|
||||
}
|
||||
|
||||
virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
|
||||
{
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
const char* ptr = gptr();
|
||||
const std::streamsize to_read = std::min(
|
||||
count,
|
||||
static_cast<std::streamsize>(egptr() - ptr));
|
||||
|
||||
if (to_read == 0)
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(s, ptr, to_read);
|
||||
gbump((int)to_read);
|
||||
return to_read;
|
||||
}
|
||||
}
|
||||
|
||||
virtual pos_type seekoff(
|
||||
off_type off,
|
||||
std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode which = std::ios_base::in) override
|
||||
{
|
||||
if (which != std::ios_base::in)
|
||||
{
|
||||
//assert(0);
|
||||
throw std::invalid_argument("basic_memstreambuf::seekoff[which]");
|
||||
}
|
||||
|
||||
if (dir == std::ios_base::beg)
|
||||
{
|
||||
if (off >= 0 && off < egptr() - eback())
|
||||
{
|
||||
setg(eback(), eback() + off, egptr());
|
||||
}
|
||||
else
|
||||
{
|
||||
//assert(0);
|
||||
throw std::out_of_range("basic_memstreambuf::seekoff[beg]");
|
||||
}
|
||||
}
|
||||
else if (dir == std::ios_base::cur)
|
||||
{
|
||||
if ((off >= 0 && off <= egptr() - gptr()) ||
|
||||
(off < 0 && std::abs(off) < gptr() - eback()))
|
||||
{
|
||||
gbump((int)off);
|
||||
}
|
||||
else
|
||||
{
|
||||
//assert(0);
|
||||
throw std::out_of_range("basic_memstreambuf::seekoff[cur]");
|
||||
}
|
||||
}
|
||||
else if (dir == std::ios_base::end)
|
||||
{
|
||||
if (off <= 0 && std::abs(off) < egptr() - eback())
|
||||
{
|
||||
setg(eback(), egptr() + (int)off, egptr());
|
||||
}
|
||||
else
|
||||
{
|
||||
//assert(0);
|
||||
throw std::out_of_range("basic_memstreambuf::seekoff[end]");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//assert(0);
|
||||
throw std::invalid_argument("basic_memstreambuf::seekoff[dir]");
|
||||
}
|
||||
|
||||
return gptr() - eback();
|
||||
}
|
||||
|
||||
virtual pos_type seekpos(
|
||||
pos_type pos,
|
||||
std::ios_base::openmode which = std::ios_base::in) override
|
||||
{
|
||||
if (which != std::ios_base::in)
|
||||
{
|
||||
//assert(0);
|
||||
throw std::invalid_argument("basic_memstreambuf::seekpos[which]");
|
||||
}
|
||||
|
||||
if (pos < egptr() - eback())
|
||||
{
|
||||
setg(eback(), eback() + pos, egptr());
|
||||
}
|
||||
else
|
||||
{
|
||||
////assert(0);
|
||||
throw std::out_of_range("memstreambuf::seekpos");
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
#if 0
|
||||
virtual int_type pbackfail(int_type c = traits_type::eof()) override
|
||||
{
|
||||
const auto* begin = eback();
|
||||
const auto* ptr = gptr();
|
||||
const auto gc = *(ptr - 1);
|
||||
|
||||
if (ptr == begin || (c != traits_type::eof() && c != gc))
|
||||
return traits_type::eof();
|
||||
|
||||
gbump(-1);
|
||||
|
||||
return traits_type::to_int_type(gc);
|
||||
}
|
||||
#endif
|
||||
};
|
41
libultraship/libultraship/Lib/BinaryTools/BinaryTools/Span.h
Normal file
41
libultraship/libultraship/Lib/BinaryTools/BinaryTools/Span.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
//Simple wrapper around a contiguous area of memory
|
||||
template<class T>
|
||||
struct Span
|
||||
{
|
||||
public:
|
||||
Span(T* ptr, size_t size) : ptr_(ptr), size_(size) {}
|
||||
|
||||
T* Data() { return ptr_; }
|
||||
size_t Size() { return size_; }
|
||||
|
||||
//Returns pointer to start of contiguous memory area
|
||||
T* begin() { return ptr_; }
|
||||
//Returns pointer to start of contiguous memory area
|
||||
const T* begin() const { return ptr_; }
|
||||
//Returns pointer to end of contiguous memory area (the end of the last memory).
|
||||
//To get the last member, use back() instead.
|
||||
T* end() { return ptr_ + size_; }
|
||||
//Returns pointer to end of contiguous memory area (the end of the last memory).
|
||||
//To get the last member, use back() instead.
|
||||
const T* end() const { return ptr_ + size_; }
|
||||
//Returns reference to first member of the span
|
||||
T& front() { return *ptr_; }
|
||||
//Returns reference to first member of the span
|
||||
const T& front() const { return *ptr_; }
|
||||
//Returns reference to last member of the span
|
||||
T& back() { return *(ptr_ + size_); }
|
||||
//Returns reference to last member of the span
|
||||
const T& back() const { return *(ptr_ + size_); }
|
||||
|
||||
//Todo: Add optional bounds checking for debug builds
|
||||
//Returns reference to element at provided index. Does no bounds checking
|
||||
T& operator[](size_t index) { return ptr_[index]; }
|
||||
//Returns reference to element at provided index. Does no bounds checking
|
||||
const T& operator[](size_t index) const { return ptr_[index]; }
|
||||
|
||||
private:
|
||||
T* ptr_ = nullptr;
|
||||
size_t size_ = 0;
|
||||
};
|
Binary file not shown.
Binary file not shown.
21
libultraship/libultraship/Lib/BinaryTools/License.txt
Normal file
21
libultraship/libultraship/Lib/BinaryTools/License.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 moneyl
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
80
libultraship/libultraship/Lib/BinaryTools/README.md
Normal file
80
libultraship/libultraship/Lib/BinaryTools/README.md
Normal file
@ -0,0 +1,80 @@
|
||||
# BinaryTools
|
||||
C++ classes for reading/writing binary data and some helper functions/classes. Based on C#'s `BinaryReader` and `BinaryWriter`.
|
||||
|
||||
## BinaryReader & BinaryWriter
|
||||
Classes which can read/write binary data to/from a file or memory buffer. Both have functions for the most common primitive types. Ex: `uint32_t`, `int32_t`, `uint64_t`, `int64_t`, `float`, `double`, etc. See `BinaryReader.h` and `BinaryWriter.h` for a full list. The constructor used determines whether the class reads from a file (the constructor provides a file path), or a memory region (it provides a memory address and size). They can also read and write entire structs to or from memory using `ReadToMemory` and `WriteFromMemory`, respectively.
|
||||
|
||||
## Other helpers and included classes
|
||||
- `Span<T>`: A very simple wrapper around a fixed sized memory region used by ReadAllBytes. You must free the memory the span points to if it's heap allocated.
|
||||
- `MemoryBuffer`: A simple class which inherits std::streambuf. Used by BinaryReader/Writer when interacting with a memory buffer.
|
||||
- `ReadAllBytes(const std::string& filePath)`: Function that reads all bytes from a file and returns them in a Span<T>. Since it's using a span you must free the memory it returns once you're done with it.
|
||||
|
||||
## Example
|
||||
This example shows how to read/write files and in memory buffers using `BinaryReader` and `BinaryWriter`.
|
||||
```c++
|
||||
#include <BinaryReader.h>
|
||||
#include <BinaryWriter.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
|
||||
//You can specify data layouts with a struct and read/write them in one go
|
||||
struct ExampleHeader
|
||||
{
|
||||
uint32_t Signature = 0xF00F;
|
||||
uint32_t Version = 10;
|
||||
uint32_t Value0 = 1024;
|
||||
float Value1 = 45.2f;
|
||||
float Value2 = 800.9f;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
//Write test file to your project directory
|
||||
std::string testFilePath = "TestFile0.bin";
|
||||
|
||||
//File writing
|
||||
{
|
||||
BinaryWriter writer(testFilePath);
|
||||
writer.WriteUint32(100);
|
||||
writer.WriteFloat(512.0f);
|
||||
writer.WriteNullTerminatedString("Hello binary!");
|
||||
//Write padding bytes to align to value. E.g. position = 13. After align(4), position = 16, the next multiple of 4.
|
||||
writer.Align(4);
|
||||
|
||||
//Can also write whole structs/classes to files
|
||||
ExampleHeader header;
|
||||
writer.WriteFromMemory(&header, sizeof(ExampleHeader));
|
||||
}
|
||||
|
||||
//File reading
|
||||
{
|
||||
BinaryReader reader(testFilePath); //Assumes that the file already exists
|
||||
assert(reader.ReadUint32() == 100);
|
||||
assert(reader.ReadFloat() == 512.0f);
|
||||
assert(reader.ReadNullTerminatedString() == "Hello binary!");
|
||||
reader.Align(4);
|
||||
|
||||
//Can also read whole structs/classes from files
|
||||
ExampleHeader header;
|
||||
reader.ReadToMemory(&header, sizeof(ExampleHeader));
|
||||
assert(header.Signature == 0xF00F);
|
||||
assert(header.Version == 10);
|
||||
assert(header.Value0 == 1024);
|
||||
assert(header.Value1 == 45.2f);
|
||||
assert(header.Value2 == 800.9f);
|
||||
}
|
||||
|
||||
//Reading from memory
|
||||
{
|
||||
uint32_t someBuffer[5] = { 256, 700, 12, 895, 5784 };
|
||||
BinaryReader reader2((char*)&someBuffer, 5 * sizeof(uint32_t));
|
||||
assert(reader2.ReadUint32() == 256);
|
||||
assert(reader2.ReadUint32() == 700);
|
||||
assert(reader2.ReadUint32() == 12);
|
||||
assert(reader2.ReadUint32() == 895);
|
||||
assert(reader2.ReadUint32() == 5784);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@ -42,7 +42,7 @@ namespace Ship
|
||||
ShaderParam::ShaderParam(BinaryReader* reader)
|
||||
{
|
||||
name = reader->ReadUInt32();
|
||||
dataType = (DataType)reader->ReadByte();
|
||||
dataType = (DataType)reader->ReadInt8();
|
||||
|
||||
switch (dataType)
|
||||
{
|
||||
@ -50,7 +50,7 @@ namespace Ship
|
||||
value = reader->ReadUByte();
|
||||
break;
|
||||
case DataType::S8:
|
||||
value = reader->ReadByte();
|
||||
value = reader->ReadInt8();
|
||||
break;
|
||||
case DataType::U16:
|
||||
value = reader->ReadUInt16();
|
||||
|
@ -24,7 +24,7 @@ namespace Ship
|
||||
|
||||
uint32_t headerStart = reader->GetBaseAddress();
|
||||
|
||||
modelType = (ModelType)reader->ReadByte();
|
||||
modelType = (ModelType)reader->ReadInt8();
|
||||
|
||||
numVerts = reader->ReadUInt32();
|
||||
numPolys = reader->ReadUInt32();
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Resource.h"
|
||||
#include "Vec2f.h"
|
||||
#include "Vec3f.h"
|
||||
#include "Vec3s.h"
|
||||
#include "Color3b.h"
|
||||
|
||||
namespace Ship
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Resource.h"
|
||||
#include "Vec2f.h"
|
||||
#include "Vec3f.h"
|
||||
#include "Vec3s.h"
|
||||
#include "Color3b.h"
|
||||
|
||||
namespace Ship
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "DisplayList.h"
|
||||
#include "ResourceMgr.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "Utils/BinaryReader.h"
|
||||
#include "Lib/tinyxml2/tinyxml2.h"
|
||||
#include "Lib/Fast3D/U64/PR/ultra64/gbi.h"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Utils/BinaryReader.h"
|
||||
#include "BinaryReader.h"
|
||||
#include "Utils/BinaryWriter.h"
|
||||
#include "File.h"
|
||||
#include "Lib/tinyxml2/tinyxml2.h"
|
||||
|
@ -9,17 +9,30 @@
|
||||
|
||||
namespace Ship {
|
||||
|
||||
ResourceMgr::ResourceMgr(std::shared_ptr<Window> Context, const std::string& MainPath, const std::string& PatchesPath) : Context(Context), bIsRunning(false), FileLoadThread(nullptr) {
|
||||
OTR = std::make_shared<Archive>(MainPath, PatchesPath, false);
|
||||
ResourceMgr::ResourceMgr(std::shared_ptr<Window> Context, const std::string& MainPath, const std::string& PatchesPath, const std::unordered_set<uint32_t>& ValidHashes)
|
||||
: Context(Context), bIsRunning(false), FileLoadThread(nullptr) {
|
||||
OTR = std::make_shared<Archive>(MainPath, PatchesPath, ValidHashes, false);
|
||||
|
||||
gameVersion = OOT_UNKNOWN;
|
||||
gameVersion = UNKNOWN;
|
||||
|
||||
if (OTR->IsMainMPQValid()) {
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
ResourceMgr::~ResourceMgr() {
|
||||
ResourceMgr::ResourceMgr(std::shared_ptr<Window> Context, const std::vector<std::string> &OTRFiles, const std::unordered_set<uint32_t> &ValidHashes)
|
||||
: Context(Context), bIsRunning(false), FileLoadThread(nullptr)
|
||||
{
|
||||
OTR = std::make_shared<Archive>(OTRFiles, ValidHashes, false);
|
||||
|
||||
gameVersion = UNKNOWN;
|
||||
|
||||
if (OTR->IsMainMPQValid()) {
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
ResourceMgr::~ResourceMgr() {
|
||||
SPDLOG_INFO("destruct ResourceMgr");
|
||||
Stop();
|
||||
|
||||
@ -168,6 +181,14 @@ namespace Ship {
|
||||
gameVersion = newGameVersion;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ResourceMgr::GetGameVersions() {
|
||||
return OTR->gameVersions;
|
||||
}
|
||||
|
||||
void ResourceMgr::PushGameVersion(uint32_t newGameVersion) {
|
||||
OTR->gameVersions.push_back(newGameVersion);
|
||||
}
|
||||
|
||||
std::shared_ptr<File> ResourceMgr::LoadFileAsync(const std::string& FilePath) {
|
||||
const std::lock_guard<std::mutex> Lock(FileLoadMutex);
|
||||
// File NOT already loaded...?
|
||||
|
@ -17,8 +17,9 @@ namespace Ship {
|
||||
// It works with the original game's assets because the entire ROM is 64MB and fits into RAM of any semi-modern PC.
|
||||
class ResourceMgr {
|
||||
public:
|
||||
ResourceMgr(std::shared_ptr<Window> Context, const std::string& MainPath, const std::string& PatchesPath);
|
||||
~ResourceMgr();
|
||||
ResourceMgr(std::shared_ptr<Window> Context, const std::string& MainPath, const std::string& PatchesPath, const std::unordered_set<uint32_t> &ValidHashes);
|
||||
ResourceMgr(std::shared_ptr<Window> Context, const std::vector<std::string> &OTRFiles, const std::unordered_set<uint32_t> &ValidHashes);
|
||||
~ResourceMgr();
|
||||
|
||||
bool IsRunning();
|
||||
bool DidLoadSuccessfully();
|
||||
@ -29,7 +30,9 @@ namespace Ship {
|
||||
void InvalidateResourceCache();
|
||||
uint32_t GetGameVersion();
|
||||
void SetGameVersion(uint32_t newGameVersion);
|
||||
std::shared_ptr<File> LoadFileAsync(const std::string& FilePath);
|
||||
std::vector<uint32_t> GetGameVersions();
|
||||
void PushGameVersion(uint32_t newGameVersion);
|
||||
std::shared_ptr<File> LoadFileAsync(const std::string& FilePath);
|
||||
std::shared_ptr<File> LoadFile(const std::string& FilePath);
|
||||
std::shared_ptr<Resource> GetCachedFile(const char* FilePath) const;
|
||||
std::shared_ptr<Resource> LoadResource(const char* FilePath);
|
||||
@ -61,5 +64,6 @@ namespace Ship {
|
||||
std::condition_variable FileLoadNotifier;
|
||||
std::condition_variable ResourceLoadNotifier;
|
||||
uint32_t gameVersion;
|
||||
std::vector<uint32_t> gameVersions;
|
||||
};
|
||||
}
|
@ -69,10 +69,10 @@ namespace Ship
|
||||
|
||||
SetWind::SetWind(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
windWest = reader->ReadByte();
|
||||
windVertical = reader->ReadByte();
|
||||
windSouth = reader->ReadByte();
|
||||
clothFlappingStrength = reader->ReadByte();
|
||||
windWest = reader->ReadInt8();
|
||||
windVertical = reader->ReadInt8();
|
||||
windSouth = reader->ReadInt8();
|
||||
clothFlappingStrength = reader->ReadInt8();
|
||||
}
|
||||
|
||||
ExitList::ExitList(BinaryReader* reader) : SceneCommand(reader)
|
||||
@ -86,46 +86,46 @@ namespace Ship
|
||||
|
||||
SetTimeSettings::SetTimeSettings(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
hour = reader->ReadByte();
|
||||
min = reader->ReadByte();
|
||||
unk = reader->ReadByte();
|
||||
hour = reader->ReadInt8();
|
||||
min = reader->ReadInt8();
|
||||
unk = reader->ReadInt8();
|
||||
}
|
||||
|
||||
SetSkyboxModifier::SetSkyboxModifier(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
disableSky = reader->ReadByte();
|
||||
disableSunMoon = reader->ReadByte();
|
||||
disableSky = reader->ReadInt8();
|
||||
disableSunMoon = reader->ReadInt8();
|
||||
}
|
||||
|
||||
SetEchoSettings::SetEchoSettings(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
echo = reader->ReadByte();
|
||||
echo = reader->ReadInt8();
|
||||
}
|
||||
|
||||
SetSoundSettings::SetSoundSettings(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
reverb = reader->ReadByte();
|
||||
nightTimeSFX = reader->ReadByte();
|
||||
musicSequence = reader->ReadByte();
|
||||
reverb = reader->ReadInt8();
|
||||
nightTimeSFX = reader->ReadInt8();
|
||||
musicSequence = reader->ReadInt8();
|
||||
}
|
||||
|
||||
SetSkyboxSettings::SetSkyboxSettings(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
unk1 = reader->ReadByte();
|
||||
skyboxNumber = reader->ReadByte();
|
||||
cloudsType = reader->ReadByte();
|
||||
isIndoors = reader->ReadByte();
|
||||
unk1 = reader->ReadInt8();
|
||||
skyboxNumber = reader->ReadInt8();
|
||||
cloudsType = reader->ReadInt8();
|
||||
isIndoors = reader->ReadInt8();
|
||||
}
|
||||
|
||||
SetRoomBehavior::SetRoomBehavior(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
gameplayFlags = reader->ReadByte();
|
||||
gameplayFlags = reader->ReadInt8();
|
||||
gameplayFlags2 = reader->ReadInt32();
|
||||
}
|
||||
|
||||
SetCsCamera::SetCsCamera(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
reader->ReadByte(); // camSize
|
||||
reader->ReadInt8(); // camSize
|
||||
reader->ReadInt32(); // segOffset
|
||||
|
||||
// OTRTODO: FINISH!
|
||||
@ -143,13 +143,13 @@ namespace Ship
|
||||
|
||||
SetMesh::SetMesh(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
data = reader->ReadByte();
|
||||
meshHeaderType = reader->ReadByte();
|
||||
data = reader->ReadInt8();
|
||||
meshHeaderType = reader->ReadInt8();
|
||||
|
||||
uint32_t numPoly = 1;
|
||||
|
||||
if (meshHeaderType != 1)
|
||||
numPoly = reader->ReadByte();
|
||||
numPoly = reader->ReadInt8();
|
||||
|
||||
meshes.reserve(numPoly);
|
||||
for (uint32_t i = 0; i < numPoly; i++)
|
||||
@ -158,7 +158,7 @@ namespace Ship
|
||||
|
||||
if (meshHeaderType == 0)
|
||||
{
|
||||
int polyType = reader->ReadByte();
|
||||
int polyType = reader->ReadInt8();
|
||||
mesh.x = 0;
|
||||
mesh.y = 0;
|
||||
mesh.z = 0;
|
||||
@ -166,7 +166,7 @@ namespace Ship
|
||||
}
|
||||
else if (meshHeaderType == 2)
|
||||
{
|
||||
int polyType = reader->ReadByte();
|
||||
int polyType = reader->ReadInt8();
|
||||
mesh.x = reader->ReadInt16();
|
||||
mesh.y = reader->ReadInt16();
|
||||
mesh.z = reader->ReadInt16();
|
||||
@ -201,7 +201,7 @@ namespace Ship
|
||||
mesh.images.push_back(img);
|
||||
}
|
||||
|
||||
int polyType = reader->ReadByte();
|
||||
int polyType = reader->ReadInt8();
|
||||
|
||||
int bp = 0;
|
||||
}
|
||||
@ -216,7 +216,7 @@ namespace Ship
|
||||
|
||||
SetCameraSettings::SetCameraSettings(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
cameraMovement = reader->ReadByte();
|
||||
cameraMovement = reader->ReadInt8();
|
||||
mapHighlights = reader->ReadInt32();
|
||||
}
|
||||
|
||||
@ -229,29 +229,29 @@ namespace Ship
|
||||
{
|
||||
LightingSettings entry = LightingSettings();
|
||||
|
||||
entry.ambientClrR = reader->ReadByte();
|
||||
entry.ambientClrG = reader->ReadByte();
|
||||
entry.ambientClrB = reader->ReadByte();
|
||||
entry.ambientClrR = reader->ReadInt8();
|
||||
entry.ambientClrG = reader->ReadInt8();
|
||||
entry.ambientClrB = reader->ReadInt8();
|
||||
|
||||
entry.diffuseDirA_X = reader->ReadByte();
|
||||
entry.diffuseDirA_Y = reader->ReadByte();
|
||||
entry.diffuseDirA_Z = reader->ReadByte();
|
||||
entry.diffuseDirA_X = reader->ReadInt8();
|
||||
entry.diffuseDirA_Y = reader->ReadInt8();
|
||||
entry.diffuseDirA_Z = reader->ReadInt8();
|
||||
|
||||
entry.diffuseClrA_R = reader->ReadByte();
|
||||
entry.diffuseClrA_G = reader->ReadByte();
|
||||
entry.diffuseClrA_B = reader->ReadByte();
|
||||
entry.diffuseClrA_R = reader->ReadInt8();
|
||||
entry.diffuseClrA_G = reader->ReadInt8();
|
||||
entry.diffuseClrA_B = reader->ReadInt8();
|
||||
|
||||
entry.diffuseDirB_X = reader->ReadByte();
|
||||
entry.diffuseDirB_Y = reader->ReadByte();
|
||||
entry.diffuseDirB_Z = reader->ReadByte();
|
||||
entry.diffuseDirB_X = reader->ReadInt8();
|
||||
entry.diffuseDirB_Y = reader->ReadInt8();
|
||||
entry.diffuseDirB_Z = reader->ReadInt8();
|
||||
|
||||
entry.diffuseClrB_R = reader->ReadByte();
|
||||
entry.diffuseClrB_G = reader->ReadByte();
|
||||
entry.diffuseClrB_B = reader->ReadByte();
|
||||
entry.diffuseClrB_R = reader->ReadInt8();
|
||||
entry.diffuseClrB_G = reader->ReadInt8();
|
||||
entry.diffuseClrB_B = reader->ReadInt8();
|
||||
|
||||
entry.fogClrR = reader->ReadByte();
|
||||
entry.fogClrG = reader->ReadByte();
|
||||
entry.fogClrB = reader->ReadByte();
|
||||
entry.fogClrR = reader->ReadInt8();
|
||||
entry.fogClrG = reader->ReadInt8();
|
||||
entry.fogClrB = reader->ReadInt8();
|
||||
|
||||
entry.fogNear = reader->ReadInt16();
|
||||
entry.fogFar = reader->ReadUInt16();
|
||||
@ -290,8 +290,8 @@ namespace Ship
|
||||
for (uint32_t i = 0; i < cnt; i++)
|
||||
{
|
||||
EntranceEntry entry = EntranceEntry();
|
||||
entry.startPositionIndex = reader->ReadByte();
|
||||
entry.roomToLoad = reader->ReadByte();
|
||||
entry.startPositionIndex = reader->ReadInt8();
|
||||
entry.roomToLoad = reader->ReadInt8();
|
||||
|
||||
entrances.push_back(entry);
|
||||
}
|
||||
@ -299,7 +299,7 @@ namespace Ship
|
||||
|
||||
SetSpecialObjects::SetSpecialObjects(BinaryReader* reader) : SceneCommand(reader)
|
||||
{
|
||||
elfMessage = reader->ReadByte();
|
||||
elfMessage = reader->ReadInt8();
|
||||
globalObject = reader->ReadInt16();
|
||||
}
|
||||
|
||||
|
@ -8,13 +8,13 @@ namespace Ship
|
||||
|
||||
ResourceFile::ParseFileBinary(reader, skel);
|
||||
|
||||
skel->type = (SkeletonType)reader->ReadByte();
|
||||
skel->limbType = (LimbType)reader->ReadByte();
|
||||
skel->type = (SkeletonType)reader->ReadInt8();
|
||||
skel->limbType = (LimbType)reader->ReadInt8();
|
||||
|
||||
skel->limbCount = reader->ReadUInt32();
|
||||
skel->dListCount = reader->ReadUInt32();
|
||||
|
||||
skel->limbTableType = (LimbType)reader->ReadByte();
|
||||
skel->limbTableType = (LimbType)reader->ReadInt8();
|
||||
|
||||
uint32_t limbTblCnt = reader->ReadUInt32();
|
||||
skel->limbTable.reserve(limbTblCnt);
|
||||
|
@ -8,8 +8,8 @@ namespace Ship
|
||||
|
||||
ResourceFile::ParseFileBinary(reader, limb);
|
||||
|
||||
limb->limbType = (LimbType)reader->ReadByte();
|
||||
limb->skinSegmentType = (ZLimbSkinType)reader->ReadByte();
|
||||
limb->limbType = (LimbType)reader->ReadInt8();
|
||||
limb->skinSegmentType = (ZLimbSkinType)reader->ReadInt8();
|
||||
limb->skinDList = reader->ReadString();
|
||||
|
||||
limb->skinVtxCnt = reader->ReadUInt16();
|
||||
@ -30,9 +30,9 @@ namespace Ship
|
||||
struc2.unk_0 = reader->ReadInt16();
|
||||
struc2.unk_2 = reader->ReadInt16();
|
||||
struc2.unk_4 = reader->ReadInt16();
|
||||
struc2.unk_6 = reader->ReadByte();
|
||||
struc2.unk_7 = reader->ReadByte();
|
||||
struc2.unk_8 = reader->ReadByte();
|
||||
struc2.unk_6 = reader->ReadInt8();
|
||||
struc2.unk_7 = reader->ReadInt8();
|
||||
struc2.unk_8 = reader->ReadInt8();
|
||||
struc2.unk_9 = reader->ReadUByte();
|
||||
|
||||
struc.unk_8_arr.push_back(struc2);
|
||||
@ -58,9 +58,9 @@ namespace Ship
|
||||
|
||||
limb->skinDList2 = reader->ReadString();
|
||||
|
||||
limb->legTransX = reader->ReadSingle();
|
||||
limb->legTransY = reader->ReadSingle();
|
||||
limb->legTransZ = reader->ReadSingle();
|
||||
limb->legTransX = reader->ReadFloat();
|
||||
limb->legTransY = reader->ReadFloat();
|
||||
limb->legTransZ = reader->ReadFloat();
|
||||
|
||||
limb->rotX = reader->ReadUInt16();
|
||||
limb->rotY = reader->ReadUInt16();
|
||||
|
@ -232,11 +232,11 @@ namespace Ship {
|
||||
return Context.lock();
|
||||
}
|
||||
|
||||
std::shared_ptr<Window> Window::CreateInstance(const std::string Name) {
|
||||
std::shared_ptr<Window> Window::CreateInstance(const std::string Name, const std::vector<std::string>& OTRFiles, const std::unordered_set<uint32_t>& ValidHashes) {
|
||||
if (Context.expired()) {
|
||||
auto Shared = std::make_shared<Window>(Name);
|
||||
Context = Shared;
|
||||
Shared->Initialize();
|
||||
Shared->Initialize(OTRFiles, ValidHashes);
|
||||
return Shared;
|
||||
}
|
||||
|
||||
@ -279,10 +279,10 @@ namespace Ship {
|
||||
}
|
||||
}
|
||||
|
||||
void Window::Initialize() {
|
||||
void Window::Initialize(const std::vector<std::string>& OTRFiles, const std::unordered_set<uint32_t>& ValidHashes) {
|
||||
InitializeLogging();
|
||||
InitializeConfiguration();
|
||||
InitializeResourceManager();
|
||||
InitializeResourceManager(OTRFiles, ValidHashes);
|
||||
CreateDefaults();
|
||||
InitializeControlDeck();
|
||||
|
||||
@ -579,10 +579,14 @@ namespace Ship {
|
||||
}
|
||||
}
|
||||
|
||||
void Window::InitializeResourceManager() {
|
||||
MainPath = Config->getString("Game.Main Archive", GetPathRelativeToAppDirectory("oot.otr"));
|
||||
void Window::InitializeResourceManager(const std::vector<std::string>& OTRFiles, const std::unordered_set<uint32_t>& ValidHashes) {
|
||||
MainPath = Config->getString("Game.Main Archive", GetAppDirectoryPath());
|
||||
PatchesPath = Config->getString("Game.Patches Archive", GetAppDirectoryPath() + "/mods");
|
||||
ResMan = std::make_shared<ResourceMgr>(GetInstance(), MainPath, PatchesPath);
|
||||
if (OTRFiles.empty()) {
|
||||
ResMan = std::make_shared<ResourceMgr>(GetInstance(), MainPath, PatchesPath, ValidHashes);
|
||||
} else {
|
||||
ResMan = std::make_shared<ResourceMgr>(GetInstance(), OTRFiles, ValidHashes);
|
||||
}
|
||||
|
||||
if (!ResMan->DidLoadSuccessfully())
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "ControlDeck.h"
|
||||
#include "AudioPlayer.h"
|
||||
@ -17,7 +18,7 @@ namespace Ship {
|
||||
class Window {
|
||||
public:
|
||||
static std::shared_ptr<Window> GetInstance();
|
||||
static std::shared_ptr<Window> CreateInstance(const std::string Name);
|
||||
static std::shared_ptr<Window> CreateInstance(const std::string Name, const std::vector<std::string>& OTRFiles = {}, const std::unordered_set<uint32_t>& ValidHashes = {});
|
||||
static std::string GetAppDirectoryPath();
|
||||
static std::string GetPathRelativeToAppDirectory(const char* path);
|
||||
|
||||
@ -27,7 +28,7 @@ namespace Ship {
|
||||
void ReadSaveFile(std::filesystem::path savePath, uintptr_t addr, void* dramAddr, size_t size);
|
||||
void CreateDefaults();
|
||||
void MainLoop(void (*MainFunction)(void));
|
||||
void Initialize();
|
||||
void Initialize(const std::vector<std::string>& OTRFiles = {}, const std::unordered_set<uint32_t>& ValidHashes = {});
|
||||
void StartFrame();
|
||||
void SetTargetFps(int32_t fps);
|
||||
void SetMaximumFrameLatency(int32_t latency);
|
||||
@ -64,7 +65,7 @@ namespace Ship {
|
||||
void InitializeControlDeck();
|
||||
void InitializeAudioPlayer();
|
||||
void InitializeLogging();
|
||||
void InitializeResourceManager();
|
||||
void InitializeResourceManager(const std::vector<std::string>& OTRFiles = {}, const std::unordered_set<uint32_t>& ValidHashes = {});
|
||||
void InitializeWindowManager();
|
||||
|
||||
std::shared_ptr<spdlog::logger> Logger;
|
||||
@ -84,6 +85,7 @@ namespace Ship {
|
||||
int32_t lastScancode;
|
||||
std::string Name;
|
||||
std::string MainPath;
|
||||
std::string BasePath;
|
||||
std::string PatchesPath;
|
||||
};
|
||||
}
|
||||
|
@ -1,9 +1,30 @@
|
||||
#ifndef ENDIANESS_H
|
||||
#define ENDIANESS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace Ship
|
||||
{
|
||||
enum class Endianness
|
||||
{
|
||||
Little = 0,
|
||||
Big = 1,
|
||||
|
||||
#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) || defined(__BIG_ENDIAN__)
|
||||
Native = Big,
|
||||
#else
|
||||
Native = Little,
|
||||
#endif
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
|
||||
#define BSWAP16 _byteswap_ushort
|
||||
#define BSWAP32 _byteswap_ulong
|
||||
#define BSWAP64 _byteswap_uint64
|
||||
|
||||
#define BOMSWAP16 _byteswap_ushort
|
||||
#define BOMSWAP32 _byteswap_ulong
|
||||
#define BOMSWAP64 _byteswap_uint64
|
||||
@ -19,6 +40,11 @@
|
||||
(((x) << 8) & 0x000000FF00000000) | (((x) << 24) & 0x0000FF0000000000) | \
|
||||
(((x) << 40) & 0x00FF000000000000) | (((x) << 56) & 0xFF00000000000000))
|
||||
#else
|
||||
|
||||
#define BSWAP16 __builtin_bswap16
|
||||
#define BSWAP32 __builtin_bswap32
|
||||
#define BSWAP64 __builtin_bswap64
|
||||
|
||||
#define BOMSWAP16 __builtin_bswap16
|
||||
#define BOMSWAP32 __builtin_bswap32
|
||||
#define BOMSWAP64 __builtin_bswap64
|
||||
|
@ -9,54 +9,92 @@ if [ -z ${SHIP_HOME+x} ]; then
|
||||
export SHIP_HOME=$PWD
|
||||
fi
|
||||
|
||||
while [[ ! -e "$SHIP_HOME"/oot.otr ]]; do
|
||||
export ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
|
||||
ln -s "$HERE"/usr/bin/{assets,soh.elf,OTRGui} "$ASSETDIR"
|
||||
export OLDPWD="$PWD"
|
||||
mkdir -p "$ASSETDIR"/tmp
|
||||
mkdir -p "$ASSETDIR"/Extract
|
||||
if [ -e "$SHIP_HOME"/*.*64 ]; then
|
||||
ln -s "$SHIP_HOME"/*.*64 "$ASSETDIR"/tmp/rom.z64
|
||||
cp -r "$ASSETDIR"/assets/game "$ASSETDIR"/Extract/assets
|
||||
cd "$ASSETDIR"
|
||||
ROMHASH=$(sha1sum -b "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
ROM=GC_NMQ_D;;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
ROM=GC_NMQ_PAL_F;;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
ROM=GC_MQ_D;;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
ROM=GC_MQ_D;;
|
||||
*)
|
||||
if [ -n "$ZENITY" ]; then
|
||||
zenity --error --timeout=10 --text="ROM hash <b>$ROMHASH</b> does not match" --title="Incorrect ROM file" --width=500 --width=200
|
||||
else
|
||||
echo -e "\nrom hash does not match\n"
|
||||
fi
|
||||
exit;;
|
||||
esac
|
||||
if [ -n "$ZENITY" ]; then
|
||||
(echo "# 25%"; echo "25"; sleep 2; echo "# 50%"; echo "50"; sleep 3; echo "# 75%"; echo "75"; sleep 2; echo "# 100%"; echo "100"; sleep 3) |
|
||||
zenity --progress --title="OTR Generating..." --timeout=10 --percentage=0 --icon-name=soh --window-icon=soh.png --height=80 --width=400 &
|
||||
else
|
||||
echo "Processing..."
|
||||
fi
|
||||
assets/extractor/ZAPD.out ed -eh -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR > /dev/null 2>&1
|
||||
cp "$ASSETDIR"/oot.otr "$SHIP_HOME"
|
||||
echo "Restart $APPIMAGE to play!"
|
||||
sleep 3
|
||||
rm -r "$ASSETDIR"
|
||||
break
|
||||
else
|
||||
if [ -n "$ZENITY" ]; then
|
||||
zenity --error --timeout=5 --text="Place ROM in $SHIP_HOME" --title="Missing ROM file" --width=500 --width=200
|
||||
else
|
||||
echo -e "\nPlace ROM in this folder\n"
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
while [[ (! -e "$SHIP_HOME"/oot.otr) || (! -e "$SHIP_HOME"/oot-mq.otr) ]]; do
|
||||
for romfile in "$SHIP_HOME"/*.*64
|
||||
do
|
||||
if [[ -e $romfile ]]; then
|
||||
export ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
|
||||
ln -s "$HERE"/usr/bin/{assets,soh.elf,OTRGui} "$ASSETDIR"
|
||||
export OLDPWD="$PWD"
|
||||
mkdir -p "$ASSETDIR"/tmp
|
||||
mkdir -p "$ASSETDIR"/Extract
|
||||
ln -s $romfile "$ASSETDIR"/tmp/rom.z64
|
||||
cd "$ASSETDIR"
|
||||
ROMHASH=$(sha1sum -b "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
if [[ ! -e "$SHIP_HOME"/oot.otr ]]; then
|
||||
ROM=GC_NMQ_D
|
||||
OTRNAME="oot.otr"
|
||||
fi
|
||||
;;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
if [[ ! -e "$SHIP_HOME"/oot.otr ]]; then
|
||||
ROM=GC_NMQ_PAL_F
|
||||
OTRNAME="oot.otr"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
517bd9714c73cb96c21e7c2ef640d7b55186102f)
|
||||
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo -e "\n$romfile - $ROMHASH rom hash does not match\n"
|
||||
continue;;
|
||||
esac
|
||||
cp -r "$ASSETDIR"/assets/game "$ASSETDIR"/Extract/assets
|
||||
if [ -n "$ZENITY" ]; then
|
||||
(echo "# 25%"; echo "25"; sleep 2; echo "# 50%"; echo "50"; sleep 3; echo "# 75%"; echo "75"; sleep 2; echo "# 100%"; echo "100"; sleep 3) |
|
||||
zenity --progress --title="OTR Generating..." --timeout=10 --percentage=0 --icon-name=soh --window-icon=soh.png --height=80 --width=400 &
|
||||
else
|
||||
echo "Processing..."
|
||||
fi
|
||||
assets/extractor/ZAPD.out ed -eh -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --otrfile "${OTRNAME}" > /dev/null 2>&1
|
||||
cp "$ASSETDIR"/"$OTRNAME" "$SHIP_HOME"
|
||||
else
|
||||
if [ -n "$ZENITY" ]; then
|
||||
zenity --error --timeout=5 --text="Place ROM in $SHIP_HOME" --title="Missing ROM file" --width=500 --width=200
|
||||
else
|
||||
echo -e "\nPlace ROM in this folder\n"
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
done
|
||||
if [[ (! -e "$SHIP_HOME"/oot.otr) && (! -e "$SHIP_HOME"/oot-mq.otr) ]]; then
|
||||
if [ -n "$ZENITY" ]; then
|
||||
zenity --error --timeout=10 --text="No valid ROMs were provided, No OTR was generated." --title="Incorrect ROM file" --width=500 --width=200
|
||||
else
|
||||
echo "No valid roms provided, no OTR was generated."
|
||||
fi
|
||||
rm -r "$ASSETDIR"
|
||||
exit
|
||||
else
|
||||
(cd "$HERE/usr/bin"; ./soh.elf)
|
||||
exit
|
||||
fi
|
||||
rm -r "$ASSETDIR"
|
||||
done
|
||||
(cd "$HERE/usr/bin"; ./soh.elf)
|
||||
exit
|
||||
|
@ -188,6 +188,7 @@ typedef struct {
|
||||
u8 temporaryWeapon;
|
||||
u16 adultTradeItems;
|
||||
u8 pendingIceTrapCount;
|
||||
u8 mqDungeonCount;
|
||||
} SaveContext; // size = 0x1428
|
||||
|
||||
typedef enum {
|
||||
|
@ -7,67 +7,150 @@ export RESPATH="${SNAME%/MacOS*}/Resources"
|
||||
export LIBPATH="${SNAME%/MacOS*}/Frameworks"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$LIBPATH"
|
||||
|
||||
while [ ! -e "$DATA_SHARE/oot.otr" ]; do
|
||||
ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
|
||||
export ASSETDIR
|
||||
cp -r "$RESPATH/assets" "$ASSETDIR"
|
||||
mkdir -p "$ASSETDIR"/tmp
|
||||
mkdir -p "$ASSETDIR"/Extract
|
||||
DROPROM="$(osascript -ss - "$ASSETDIR" <<-EOF
|
||||
set romFile to choose file of type {"b64","n64","v64","z64"} with prompt "Please select your ROM:"
|
||||
set destinationFolder to POSIX file "$ASSETDIR"
|
||||
tell application "Finder"
|
||||
duplicate romFile to destinationFolder
|
||||
end tell
|
||||
EOF
|
||||
)"
|
||||
"$DROPROM"
|
||||
for rom in "$ASSETDIR"/*.*64
|
||||
if [ ! -e "$DATA_SHARE" ]; then mkdir "$DATA_SHARE"; fi
|
||||
|
||||
# If either OTR doesn't exist kick off the OTR gen process
|
||||
if [ ! -e "$DATA_SHARE"/oot.otr ] || [ ! -e "$DATA_SHARE"/oot-mq.otr ]; then
|
||||
|
||||
# If no ROMs exist kick off the file selection prompts
|
||||
while [ ! -e "$DATA_SHARE"/*.*64 ] && [ ! -e "$DATA_SHARE"/oot*.otr ]; do
|
||||
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
while [ $SHOULD_PROMPT_FOR_ROM -eq 1 ]; do
|
||||
SHOULD_PROMPT_FOR_ROM=0
|
||||
# Use osascript to prompt the user to chose a file
|
||||
DROPROM=`osascript <<-EOF
|
||||
set romFile to choose file of type {"b64","n64","v64","z64"} with prompt "Please select your ROM:"
|
||||
return POSIX path of romFile
|
||||
EOF`
|
||||
|
||||
# If no rom was selected, the user cancelled, so exit
|
||||
if [[ -z $DROPROM ]] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
echo "No ROM selected. Exiting..."
|
||||
exit 1
|
||||
elif [[ -z $DROPROM ]]; then
|
||||
break;
|
||||
fi
|
||||
|
||||
# If an invalid rom was selected, let the user know and ask to try again
|
||||
ROMHASH="$(shasum "$DROPROM" | awk '{ print $1 }')"
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
ROM_TYPE=0;;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
ROM_TYPE=0;;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
ROM_TYPE=1;;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
ROM_TYPE=1;;
|
||||
517bd9714c73cb96c21e7c2ef640d7b55186102f)
|
||||
ROM_TYPE=1;;
|
||||
*)
|
||||
TRY_AGAIN_RESULT=`osascript <<-EOF
|
||||
set alertText to "Incompatible ROM hash"
|
||||
set alertMessage to "Incompatible ROM provided, would you like to try again?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
as critical \
|
||||
buttons {"Cancel", "Try Again"}
|
||||
EOF`
|
||||
if [[ "$TRY_AGAIN_RESULT" == "button returned:Try Again" ]]; then
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
continue;
|
||||
else
|
||||
echo "No ROM selected. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
esac
|
||||
|
||||
cp "$DROPROM" "$DATA_SHARE"
|
||||
|
||||
# Ask user if they would also like to select the other variant (MQ/Vanilla)
|
||||
if [ $ROM_TYPE -eq 0 ] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
|
||||
set alertText to "Success"
|
||||
set alertMessage to "Would you also like to provide a Master Quest ROM?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
buttons {"No", "Yes"}
|
||||
EOF`
|
||||
elif [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
|
||||
set alertText to "Success"
|
||||
set alertMessage to "Would you also like to provide a Vanilla (Non Master Quest) ROM?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
buttons {"No", "Yes"}
|
||||
EOF`
|
||||
fi
|
||||
|
||||
if [[ "$UPLOAD_ANOTHER_RESULT" == "button returned:Yes" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT="button returned:No"
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
continue;
|
||||
fi
|
||||
break
|
||||
done
|
||||
done
|
||||
|
||||
# At this point we should now have 1 or more valid roms in $DATA_SHARE directory
|
||||
|
||||
# Prepare tmp dir
|
||||
for ROMPATH in "$DATA_SHARE"/*.*64
|
||||
do
|
||||
if [ ! -e "$rom" ]; then
|
||||
echo "no ROM"
|
||||
osascript -e 'display dialog "Select ROM to generate OTR" giving up after 5'
|
||||
ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
|
||||
export ASSETDIR
|
||||
cp -r "$RESPATH/assets" "$ASSETDIR"
|
||||
mkdir -p "$ASSETDIR"/tmp
|
||||
mkdir -p "$ASSETDIR"/Extract
|
||||
cp "$ROMPATH" "$ASSETDIR"/tmp/rom.z64
|
||||
cp -r "$ASSETDIR"/assets/game "$ASSETDIR"/Extract/assets/
|
||||
cd "$ASSETDIR" || return
|
||||
|
||||
# If an invalid rom was detected, let the user know
|
||||
ROMHASH="$(shasum "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')"
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
ROM=GC_NMQ_D
|
||||
OTRNAME="oot.otr";;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
ROM=GC_NMQ_PAL_F
|
||||
OTRNAME="oot.otr";;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
517bd9714c73cb96c21e7c2ef640d7b55186102f)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
*)
|
||||
osascript -e 'display notification "One or more invalid ROM provided" with title "Ship Of Harkinian"'
|
||||
rm -r "$ASSETDIR"
|
||||
continue;
|
||||
esac
|
||||
|
||||
# Only generate OTR if we don't have on of this type yet
|
||||
if [ -e "$DATA_SHARE"/"$OTRNAME" ]; then
|
||||
rm -r "$ASSETDIR"
|
||||
continue;
|
||||
fi
|
||||
|
||||
osascript -e 'display notification "Generating OTR..." with title "Ship Of Harkinian"'
|
||||
assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR
|
||||
if [ -e "$ASSETDIR"/oot.otr ]; then
|
||||
osascript -e 'display notification "OTR successfully generated" with title "Ship Of Harkinian"'
|
||||
cp "$ASSETDIR"/oot.otr "$DATA_SHARE"/"$OTRNAME"
|
||||
rm -r "$ASSETDIR"
|
||||
exit
|
||||
fi
|
||||
done
|
||||
cp "$ASSETDIR"/*.*64 "$ASSETDIR"/tmp/rom.z64
|
||||
cp -r "$ASSETDIR"/assets/game "$ASSETDIR"/Extract/assets/
|
||||
cd "$ASSETDIR" || return
|
||||
ROMHASH="$(shasum "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')"
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
export ROM=GC_NMQ_D;;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
export ROM=GC_NMQ_PAL_F;;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
export ROM=GC_MQ_D;;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
export ROM=GC_MQ_D;;
|
||||
*)
|
||||
WRONGHASH="$(osascript -ss - "$ROMHASH" <<-EOF
|
||||
display dialog "Incompatible ROM hash $ROMHASH" \
|
||||
with title "Incompatible ROM hash" \
|
||||
with icon caution \
|
||||
giving up after 5
|
||||
EOF
|
||||
)"
|
||||
"$WRONGHASH"
|
||||
rm -r "$ASSETDIR"
|
||||
exit;;
|
||||
esac
|
||||
echo "$ROM"
|
||||
|
||||
osascript -e 'display notification "Processing OTR..." with title "SOH: Generating OTR"'
|
||||
assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR
|
||||
if [ -e "$PWD"/oot.otr ]; then
|
||||
osascript -e 'display notification "OTR Successfully Generated" with title "SOH: Generating OTR"'
|
||||
if [ ! -e "$DATA_SHARE" ]; then mkdir "$DATA_SHARE"; fi
|
||||
cp "$ASSETDIR"/oot.otr "$DATA_SHARE"
|
||||
rm -r "$ASSETDIR"
|
||||
fi
|
||||
break
|
||||
done
|
||||
if [ ! -e "$DATA_SHARE"/oot*.otr ]; then
|
||||
osascript -e 'display notification "OTR failed to generate" with title "Ship Of Harkinian"'
|
||||
exit 1;
|
||||
fi
|
||||
fi
|
||||
|
||||
arch_name="$(uname -m)"
|
||||
launch_arch="arm64"
|
||||
|
@ -27,10 +27,11 @@ void BootCommands_Init()
|
||||
CVar_RegisterS32("gHudColors", 0); //0 = N64 / 1 = NGC / 2 = Custom
|
||||
CVar_RegisterS32("gInvertYAxis", 1);
|
||||
CVar_RegisterS32("gTrailDuration", 4); // 4 = Default trail duration
|
||||
if (ResourceMgr_IsGameMasterQuest()) {
|
||||
if (ResourceMgr_GameHasMasterQuest() && !ResourceMgr_GameHasOriginal()) {
|
||||
CVar_SetS32("gMasterQuest", 1);
|
||||
CVar_SetS32("gRandomizer", 0);
|
||||
} else {
|
||||
CVar_RegisterS32("gRandomizer", 0);
|
||||
} else if (!ResourceMgr_GameHasMasterQuest()) {
|
||||
CVar_SetS32("gMasterQuest", 0);
|
||||
}
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
CVar_RegisterS32("gControlNav", 1); // always enable controller nav on switch/wii u
|
||||
|
@ -2535,6 +2535,13 @@ namespace Settings {
|
||||
} else {
|
||||
GanonsTrialsCount.SetSelectedIndex(cvarSettings[RSK_TRIAL_COUNT]);
|
||||
}
|
||||
if (cvarSettings[RSK_RANDOM_MQ_DUNGEONS] == 2) {
|
||||
MQDungeonCount.SetSelectedIndex(13);
|
||||
} else if (cvarSettings[RSK_RANDOM_MQ_DUNGEONS] == 0) {
|
||||
MQDungeonCount.SetSelectedIndex(0);
|
||||
} else {
|
||||
MQDungeonCount.SetSelectedIndex(cvarSettings[RSK_MQ_DUNGEON_COUNT]);
|
||||
}
|
||||
ShuffleRewards.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS]);
|
||||
ShuffleSongs.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SONGS]);
|
||||
Tokensanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_TOKENS]);
|
||||
|
@ -477,20 +477,13 @@ static void WriteEnabledGlitches(tinyxml2::XMLDocument& spoilerLog) {
|
||||
|
||||
// Writes the Master Quest dungeons to the spoiler log, if there are any.
|
||||
static void WriteMasterQuestDungeons(tinyxml2::XMLDocument& spoilerLog) {
|
||||
auto parentNode = spoilerLog.NewElement("master-quest-dungeons");
|
||||
|
||||
for (const auto* dungeon : Dungeon::dungeonList) {
|
||||
if (dungeon->IsVanilla()) {
|
||||
continue;
|
||||
for (const auto* dungeon : Dungeon::dungeonList) {
|
||||
std::string dungeonName;
|
||||
if (dungeon->IsVanilla()) {
|
||||
continue;
|
||||
}
|
||||
jsonData["masterQuestDungeons"].push_back(dungeon->GetName());
|
||||
}
|
||||
|
||||
auto node = parentNode->InsertNewChildElement("dungeon");
|
||||
node->SetAttribute("name", dungeon->GetName().c_str());
|
||||
}
|
||||
|
||||
if (!parentNode->NoChildren()) {
|
||||
spoilerLog.RootElement()->InsertEndChild(parentNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Writes the required trials to the spoiler log, if there are any.
|
||||
@ -741,7 +734,7 @@ const char* SpoilerLog_Write(int language) {
|
||||
//if (Settings::Logic.Is(LOGIC_GLITCHED)) {
|
||||
// WriteEnabledGlitches(spoilerLog);
|
||||
//}
|
||||
//WriteMasterQuestDungeons(spoilerLog);
|
||||
WriteMasterQuestDungeons(spoilerLog);
|
||||
WriteRequiredTrials();
|
||||
WritePlaythrough();
|
||||
//WriteWayOfTheHeroLocation(spoilerLog);
|
||||
|
@ -121,48 +121,62 @@ std::unordered_map<std::string, RandomizerInf> spoilerFileTrialToEnum = {
|
||||
{ "l'épreuve de la Lumière", RAND_INF_TRIALS_DONE_LIGHT_TRIAL }
|
||||
};
|
||||
|
||||
std::unordered_map<s16, s16> getItemIdToItemId = {
|
||||
{ GI_BOW, ITEM_BOW },
|
||||
{ GI_ARROW_FIRE, ITEM_ARROW_FIRE },
|
||||
{ GI_DINS_FIRE, ITEM_DINS_FIRE },
|
||||
{ GI_SLINGSHOT, ITEM_SLINGSHOT },
|
||||
{ GI_OCARINA_FAIRY, ITEM_OCARINA_FAIRY },
|
||||
{ GI_OCARINA_OOT, ITEM_OCARINA_TIME },
|
||||
{ GI_HOOKSHOT, ITEM_HOOKSHOT },
|
||||
{ GI_LONGSHOT, ITEM_LONGSHOT },
|
||||
{ GI_ARROW_ICE, ITEM_ARROW_ICE },
|
||||
{ GI_FARORES_WIND, ITEM_FARORES_WIND },
|
||||
{ GI_BOOMERANG, ITEM_BOOMERANG },
|
||||
{ GI_LENS, ITEM_LENS },
|
||||
{ GI_HAMMER, ITEM_HAMMER },
|
||||
{ GI_ARROW_LIGHT, ITEM_ARROW_LIGHT },
|
||||
{ GI_NAYRUS_LOVE, ITEM_NAYRUS_LOVE },
|
||||
{ GI_BOTTLE, ITEM_BOTTLE },
|
||||
{ GI_POTION_RED, ITEM_POTION_RED },
|
||||
{ GI_POTION_GREEN, ITEM_POTION_GREEN },
|
||||
{ GI_POTION_BLUE, ITEM_POTION_BLUE },
|
||||
{ GI_FAIRY, ITEM_FAIRY },
|
||||
{ GI_FISH, ITEM_FISH },
|
||||
{ GI_MILK_BOTTLE, ITEM_MILK_BOTTLE },
|
||||
{ GI_LETTER_RUTO, ITEM_LETTER_RUTO },
|
||||
{ GI_BLUE_FIRE, ITEM_BLUE_FIRE },
|
||||
{ GI_BUGS, ITEM_BUG },
|
||||
{ GI_BIG_POE, ITEM_BIG_POE },
|
||||
{ GI_POE, ITEM_POE },
|
||||
{ GI_WEIRD_EGG, ITEM_WEIRD_EGG },
|
||||
{ GI_LETTER_ZELDA, ITEM_LETTER_ZELDA },
|
||||
{ GI_POCKET_EGG, ITEM_POCKET_EGG },
|
||||
{ GI_COJIRO, ITEM_COJIRO },
|
||||
{ GI_ODD_MUSHROOM, ITEM_ODD_MUSHROOM },
|
||||
{ GI_ODD_POTION, ITEM_ODD_POTION },
|
||||
{ GI_SAW, ITEM_SAW },
|
||||
{ GI_SWORD_BROKEN, ITEM_SWORD_BROKEN },
|
||||
{ GI_PRESCRIPTION, ITEM_PRESCRIPTION },
|
||||
{ GI_FROG, ITEM_FROG },
|
||||
{ GI_EYEDROPS, ITEM_EYEDROPS },
|
||||
{ GI_CLAIM_CHECK, ITEM_CLAIM_CHECK }
|
||||
std::unordered_map<std::string, SceneID> spoilerFileDungeonToScene = {
|
||||
{ "Deku Tree", SCENE_YDAN },
|
||||
{ "Dodongo's Cavern", SCENE_DDAN },
|
||||
{ "Jabu Jabu's Belly", SCENE_BDAN },
|
||||
{ "Forest Temple", SCENE_BMORI1 },
|
||||
{ "Fire Temple", SCENE_HIDAN },
|
||||
{ "Water Temple", SCENE_MIZUSIN },
|
||||
{ "Spirit Temple", SCENE_JYASINZOU },
|
||||
{ "Shadow Temple", SCENE_HAKADAN },
|
||||
{ "Bottom of the Well", SCENE_HAKADANCH },
|
||||
{ "Ice Cavern", SCENE_ICE_DOUKUTO },
|
||||
{ "Gerudo Training Grounds", SCENE_MEN },
|
||||
{ "Ganon's Castle", SCENE_GANONTIKA }
|
||||
};
|
||||
|
||||
std::unordered_map<s16, s16>
|
||||
getItemIdToItemId = { { GI_BOW, ITEM_BOW },
|
||||
{ GI_ARROW_FIRE, ITEM_ARROW_FIRE },
|
||||
{ GI_DINS_FIRE, ITEM_DINS_FIRE },
|
||||
{ GI_SLINGSHOT, ITEM_SLINGSHOT },
|
||||
{ GI_OCARINA_FAIRY, ITEM_OCARINA_FAIRY },
|
||||
{ GI_OCARINA_OOT, ITEM_OCARINA_TIME },
|
||||
{ GI_HOOKSHOT, ITEM_HOOKSHOT },
|
||||
{ GI_LONGSHOT, ITEM_LONGSHOT },
|
||||
{ GI_ARROW_ICE, ITEM_ARROW_ICE },
|
||||
{ GI_FARORES_WIND, ITEM_FARORES_WIND },
|
||||
{ GI_BOOMERANG, ITEM_BOOMERANG },
|
||||
{ GI_LENS, ITEM_LENS },
|
||||
{ GI_HAMMER, ITEM_HAMMER },
|
||||
{ GI_ARROW_LIGHT, ITEM_ARROW_LIGHT },
|
||||
{ GI_NAYRUS_LOVE, ITEM_NAYRUS_LOVE },
|
||||
{ GI_BOTTLE, ITEM_BOTTLE },
|
||||
{ GI_POTION_RED, ITEM_POTION_RED },
|
||||
{ GI_POTION_GREEN, ITEM_POTION_GREEN },
|
||||
{ GI_POTION_BLUE, ITEM_POTION_BLUE },
|
||||
{ GI_FAIRY, ITEM_FAIRY },
|
||||
{ GI_FISH, ITEM_FISH },
|
||||
{ GI_MILK_BOTTLE, ITEM_MILK_BOTTLE },
|
||||
{ GI_LETTER_RUTO, ITEM_LETTER_RUTO },
|
||||
{ GI_BLUE_FIRE, ITEM_BLUE_FIRE },
|
||||
{ GI_BUGS, ITEM_BUG },
|
||||
{ GI_BIG_POE, ITEM_BIG_POE },
|
||||
{ GI_POE, ITEM_POE },
|
||||
{ GI_WEIRD_EGG, ITEM_WEIRD_EGG },
|
||||
{ GI_LETTER_ZELDA, ITEM_LETTER_ZELDA },
|
||||
{ GI_POCKET_EGG, ITEM_POCKET_EGG },
|
||||
{ GI_COJIRO, ITEM_COJIRO },
|
||||
{ GI_ODD_MUSHROOM, ITEM_ODD_MUSHROOM },
|
||||
{ GI_ODD_POTION, ITEM_ODD_POTION },
|
||||
{ GI_SAW, ITEM_SAW },
|
||||
{ GI_SWORD_BROKEN, ITEM_SWORD_BROKEN },
|
||||
{ GI_PRESCRIPTION, ITEM_PRESCRIPTION },
|
||||
{ GI_FROG, ITEM_FROG },
|
||||
{ GI_EYEDROPS, ITEM_EYEDROPS },
|
||||
{ GI_CLAIM_CHECK, ITEM_CLAIM_CHECK } };
|
||||
|
||||
std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEnum = {
|
||||
{ "Open Settings:Forest", RSK_FOREST },
|
||||
{ "Open Settings:Kakariko Gate", RSK_KAK_GATE },
|
||||
@ -212,6 +226,7 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
|
||||
{ "Timesaver Settings:Complete Mask Quest", RSK_COMPLETE_MASK_QUEST },
|
||||
{ "Timesaver Settings:Skip Scarecrow's Song", RSK_SKIP_SCARECROWS_SONG },
|
||||
{ "Timesaver Settings:Enable Glitch-Useful Cutscenes", RSK_ENABLE_GLITCH_CUTSCENES },
|
||||
{ "World Settings:MQ Dungeon Count", RSK_MQ_DUNGEON_COUNT }
|
||||
};
|
||||
|
||||
std::string sanitize(std::string stringValue) {
|
||||
@ -491,6 +506,13 @@ void Randomizer::LoadRequiredTrials(const char* spoilerFileName) {
|
||||
}
|
||||
}
|
||||
|
||||
void Randomizer::LoadMasterQuestDungeons(const char* spoilerFileName) {
|
||||
if (strcmp(spoilerFileName, "") != 0) {
|
||||
ParseMasterQuestDungeonsFile(spoilerFileName);
|
||||
}
|
||||
gSaveContext.mqDungeonCount = this->masterQuestDungeons.size();
|
||||
}
|
||||
|
||||
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream)
|
||||
@ -631,6 +653,7 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
|
||||
case RSK_SHUFFLE_ADULT_TRADE:
|
||||
case RSK_SHUFFLE_MAGIC_BEANS:
|
||||
case RSK_RANDOM_TRIALS:
|
||||
case RSK_RANDOM_MQ_DUNGEONS:
|
||||
case RSK_STARTING_DEKU_SHIELD:
|
||||
case RSK_STARTING_KOKIRI_SWORD:
|
||||
case RSK_COMPLETE_MASK_QUEST:
|
||||
@ -806,6 +829,13 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
|
||||
gSaveContext.randoSettings[index].value = 3;
|
||||
}
|
||||
break;
|
||||
case RSK_MQ_DUNGEON_COUNT:
|
||||
if (it.value() == "Random") {
|
||||
gSaveContext.randoSettings[index].value = 13;
|
||||
}
|
||||
numericValueString = it.value();
|
||||
gSaveContext.randoSettings[index].value = std::stoi(numericValueString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -987,6 +1017,27 @@ void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
|
||||
}
|
||||
}
|
||||
|
||||
void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->masterQuestDungeons.clear();
|
||||
|
||||
try {
|
||||
json spoilerFileJson;
|
||||
spoilerFileStream >> spoilerFileJson;
|
||||
json mqDungeonsJson = spoilerFileJson["masterQuestDungeons"];
|
||||
|
||||
for (auto it = mqDungeonsJson.begin(); it != mqDungeonsJson.end(); it++) {
|
||||
this->masterQuestDungeons.emplace(spoilerFileDungeonToScene[it.value()]);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream)
|
||||
@ -3733,23 +3784,36 @@ void GenerateRandomizerImgui() {
|
||||
// Link's Pocket has to have a dungeon reward if the other rewards are shuffled to end of dungeon.
|
||||
cvarSettings[RSK_LINKS_POCKET] = CVar_GetS32("gRandomizeShuffleDungeonReward", 0) != 0 ?
|
||||
CVar_GetS32("gRandomizeLinksPocket", 0) : 0;
|
||||
|
||||
// todo: this efficently when we build out cvar array support
|
||||
std::set<RandomizerCheck> excludedLocations;
|
||||
std::stringstream excludedLocationStringStream(CVar_GetString("gRandomizeExcludedLocations", ""));
|
||||
std::string excludedLocationString;
|
||||
while(getline(excludedLocationStringStream, excludedLocationString, ',')) {
|
||||
excludedLocations.insert((RandomizerCheck)std::stoi(excludedLocationString));
|
||||
if (OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal()) {
|
||||
// If both OTRs are loaded.
|
||||
cvarSettings[RSK_RANDOM_MQ_DUNGEONS] = CVar_GetS32("gRandomizeMqDungeons", 0);
|
||||
cvarSettings[RSK_MQ_DUNGEON_COUNT] = CVar_GetS32("gRandomizeMqDungeonCount", 0);
|
||||
} else if (OTRGlobals::Instance->HasMasterQuest()) {
|
||||
// If only Master Quest is loaded.
|
||||
cvarSettings[RSK_RANDOM_MQ_DUNGEONS] = 1;
|
||||
cvarSettings[RSK_MQ_DUNGEON_COUNT] = 12;
|
||||
} else {
|
||||
// If only Original Quest is loaded.
|
||||
cvarSettings[RSK_RANDOM_MQ_DUNGEONS] = 1;
|
||||
cvarSettings[RSK_MQ_DUNGEON_COUNT] = 0;
|
||||
}
|
||||
|
||||
RandoMain::GenerateRando(cvarSettings, excludedLocations);
|
||||
// todo: this efficently when we build out cvar array support
|
||||
std::set<RandomizerCheck> excludedLocations;
|
||||
std::stringstream excludedLocationStringStream(CVar_GetString("gRandomizeExcludedLocations", ""));
|
||||
std::string excludedLocationString;
|
||||
while (getline(excludedLocationStringStream, excludedLocationString, ',')) {
|
||||
excludedLocations.insert((RandomizerCheck)std::stoi(excludedLocationString));
|
||||
}
|
||||
|
||||
CVar_SetS32("gRandoGenerating", 0);
|
||||
CVar_Save();
|
||||
CVar_Load();
|
||||
RandoMain::GenerateRando(cvarSettings, excludedLocations);
|
||||
|
||||
generated = 1;
|
||||
}
|
||||
CVar_SetS32("gRandoGenerating", 0);
|
||||
CVar_Save();
|
||||
CVar_Load();
|
||||
|
||||
generated = 1;
|
||||
}
|
||||
|
||||
void DrawRandoEditor(bool& open) {
|
||||
if (generated) {
|
||||
@ -3762,11 +3826,6 @@ void DrawRandoEditor(bool& open) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ResourceMgr_IsGameMasterQuest()) {
|
||||
ImGui::Text("Master Quest Randomizer is not currently supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomizer settings
|
||||
// Logic Settings
|
||||
const char* randoLogicRules[2] = { "Glitchless", "No logic" };
|
||||
@ -3780,6 +3839,7 @@ void DrawRandoEditor(bool& open) {
|
||||
const char* randoRainbowBridge[7] = { "Vanilla", "Always open", "Stones", "Medallions",
|
||||
"Dungeon rewards", "Dungeons", "Tokens" };
|
||||
const char* randoGanonsTrial[3] = { "Skip", "Set Number", "Random Number" };
|
||||
const char* randoMqDungeons[3] = { "None", "Set Number", "Random Number" };
|
||||
|
||||
// World Settings
|
||||
const char* randoStartingAge[3] = { "Child", "Adult", "Random" };
|
||||
@ -3830,6 +3890,14 @@ void DrawRandoEditor(bool& open) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (OTRGlobals::Instance->HasMasterQuest() && !OTRGlobals::Instance->HasOriginal()) {
|
||||
ImGui::Text("Coming Soon! Randomizer is currently not compatible with Master Quest Dungeons.\nFor now, please "
|
||||
"generate an "
|
||||
"OTR using a non-Master Quest rom to play the Randomizer");
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
bool disableEditingRandoSettings = CVar_GetS32("gRandoGenerating", 0) || CVar_GetS32("gOnFileSelectNameEntry", 0);
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, disableEditingRandoSettings);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * (disableEditingRandoSettings ? 0.5f : 1.0f));
|
||||
@ -4071,6 +4139,29 @@ void DrawRandoEditor(bool& open) {
|
||||
|
||||
UIWidgets::PaddedSeparator();
|
||||
|
||||
//MQ Dungeons - Commented out until Logic can be updated to account for MQ Dungeons
|
||||
// if (OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal()) {
|
||||
// ImGui::PushItemWidth(-FLT_MIN);
|
||||
// ImGui::Text("Master Quest Dungeons");
|
||||
// UIWidgets::InsertHelpHoverText(
|
||||
// "Sets the number of Master Quest Dungeons that are shuffled into the pool.\n"
|
||||
// "\n"
|
||||
// "None - All Dungeons will be their Vanilla versions.\n"
|
||||
// "\n"
|
||||
// "Set Number - Select a number of dungeons that will be their Master Quest versions"
|
||||
// "using the slider below. Which dungeons are set to be the Master Quest variety will be random.\n"
|
||||
// "\n"
|
||||
// "Random Number - A Random number and set of dungeons will be their Master Quest varieties."
|
||||
// );
|
||||
// UIWidgets::EnhancementCombobox("gRandomizeMqDungeons", randoMqDungeons, 3, 1);
|
||||
// ImGui::PopItemWidth();
|
||||
// if (CVar_GetS32("gRandomizeMqDungeons", 1) == 1) {
|
||||
// ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
// UIWidgets::EnhancementSliderInt("Master Quest Dungeon Count: %d", "##RandoMqDungeonCount",
|
||||
// "gRandomizeMqDungeonCount", 1, 12, "", 12, true);
|
||||
// }
|
||||
// }
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
// COLUMN 3 - Shuffle Entrances
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include "../../../include/ultra64.h"
|
||||
#include "../../../include/z64item.h"
|
||||
@ -24,6 +25,7 @@ class Randomizer {
|
||||
void ParseRandomizerSettingsFile(const char* spoilerFileName);
|
||||
void ParseHintLocationsFile(const char* spoilerFileName);
|
||||
void ParseRequiredTrialsFile(const char* spoilerFileName);
|
||||
void ParseMasterQuestDungeonsFile(const char* spoilerFileName);
|
||||
void ParseItemLocationsFile(const char* spoilerFileName, bool silent);
|
||||
bool IsItemVanilla(RandomizerGet randoGet);
|
||||
GetItemEntry GetItemEntryFromRGData(RandomizerGetData rgData, GetItemID ogItemId, bool checkObtainability = true);
|
||||
@ -41,6 +43,7 @@ class Randomizer {
|
||||
|
||||
// Public for now to be accessed by SaveManager, will be made private again soon :tm:
|
||||
std::unordered_map<RandomizerInf, bool> trialsRequired;
|
||||
std::unordered_set<uint16_t> masterQuestDungeons;
|
||||
std::unordered_map<RandomizerCheck, u16> merchantPrices;
|
||||
|
||||
static Sprite* GetSeedTexture(uint8_t index);
|
||||
@ -52,6 +55,7 @@ class Randomizer {
|
||||
void LoadMerchantMessages(const char* spoilerFileName);
|
||||
void LoadItemLocations(const char* spoilerFileName, bool silent);
|
||||
void LoadRequiredTrials(const char* spoilerFileName);
|
||||
void LoadMasterQuestDungeons(const char* spoilerFileName);
|
||||
bool IsTrialRequired(RandomizerInf trial);
|
||||
u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey);
|
||||
RandomizerCheck GetCheckFromActor(s16 actorId, s16 sceneNum, s16 actorParams);
|
||||
|
@ -1034,6 +1034,8 @@ typedef enum {
|
||||
RSK_ENABLE_BOMBCHU_DROPS,
|
||||
RSK_BOMBCHUS_IN_LOGIC,
|
||||
RSK_LINKS_POCKET,
|
||||
RSK_RANDOM_MQ_DUNGEONS,
|
||||
RSK_MQ_DUNGEON_COUNT,
|
||||
RSK_MAX
|
||||
} RandomizerSettingKey;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "include/global.h"
|
||||
#include "include/z64audio.h"
|
||||
#include "soh/SaveManager.h"
|
||||
#include "OTRGlobals.h"
|
||||
|
||||
#define EXPERIMENTAL() \
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 50, 50, 255)); \
|
||||
@ -94,6 +95,35 @@ namespace GameMenuBar {
|
||||
Audio_SetGameVolume(SEQ_SFX, CVar_GetFloat("gFanfareVolume", 1));
|
||||
}
|
||||
|
||||
bool MasterQuestCheckboxDisabled() {
|
||||
return !(OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal())
|
||||
|| CVar_GetS32("gRandomizer", 0);
|
||||
}
|
||||
|
||||
std::string MasterQuestDisabledTooltip() {
|
||||
std::string tooltip = "";
|
||||
if (CVar_GetS32("gRandomizer", 0)) {
|
||||
tooltip = "This option is disabled because you have the Randomizer enabled.\nRandomizer has it's own "
|
||||
"settings surrounding Master Quest dungeons\nthat you can set from the Randomizer Settings Menu.";
|
||||
}
|
||||
if (!OTRGlobals::Instance->HasOriginal()) {
|
||||
tooltip = "This option is force-enabled because you have only loaded the\noot-mq.otr file. If you wish to "
|
||||
"play the Original Quest,\nplease provide the oot.otr file.";
|
||||
}
|
||||
if (!OTRGlobals::Instance->HasMasterQuest()) {
|
||||
tooltip = "This option is disabled because you have only loaded the\noot.otr file. If you wish to play "
|
||||
"the Master Quest,\nplease proivde the oot-mq.otr file.";
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
UIWidgets::CheckboxGraphics MasterQuestDisabledGraphic() {
|
||||
if (!OTRGlobals::Instance->HasOriginal()) {
|
||||
return UIWidgets::CheckboxGraphics::Checkmark;
|
||||
}
|
||||
return UIWidgets::CheckboxGraphics::Cross;
|
||||
}
|
||||
|
||||
void applyEnhancementPresetDefault(void) {
|
||||
// D-pad Support on Pause
|
||||
CVar_SetS32("gDpadPause", 0);
|
||||
@ -1254,6 +1284,10 @@ namespace GameMenuBar {
|
||||
UIWidgets::Tooltip("Holding down B skips text");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Free Camera", "gFreeCamera", true, false);
|
||||
UIWidgets::Tooltip("Enables camera control\nNote: You must remap C buttons off of the right stick in the controller config menu, and map the camera stick to the right stick.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Master Quest", "gMasterQuest", true, false, MasterQuestCheckboxDisabled(), MasterQuestDisabledTooltip().c_str(), MasterQuestDisabledGraphic());
|
||||
UIWidgets::Tooltip("Enables Master Quest.\n\nWhen checked, any non-rando save files you create will be "
|
||||
"Master Quest save files. Master Quest save files will still have Master Quest dungeons "
|
||||
"regardless of this setting and require oot-mq.otr to be present in order to play.");
|
||||
|
||||
#ifdef __SWITCH__
|
||||
UIWidgets::Spacer(0);
|
||||
|
@ -77,14 +77,78 @@ CustomMessageManager* CustomMessageManager::Instance;
|
||||
ItemTableManager* ItemTableManager::Instance;
|
||||
|
||||
OTRGlobals::OTRGlobals() {
|
||||
context = Ship::Window::CreateInstance("Ship of Harkinian");
|
||||
std::vector<std::string> OTRFiles;
|
||||
std::string mqPath = Ship::Window::GetPathRelativeToAppDirectory("oot-mq.otr");
|
||||
if (std::filesystem::exists(mqPath)) {
|
||||
OTRFiles.push_back(mqPath);
|
||||
}
|
||||
std::string ootPath = Ship::Window::GetPathRelativeToAppDirectory("oot.otr");
|
||||
if (std::filesystem::exists(ootPath)) {
|
||||
OTRFiles.push_back(ootPath);
|
||||
}
|
||||
std::unordered_set<uint32_t> ValidHashes = {
|
||||
OOT_PAL_MQ,
|
||||
OOT_NTSC_JP_MQ,
|
||||
OOT_NTSC_US_MQ,
|
||||
OOT_PAL_GC_MQ_DBG,
|
||||
OOT_NTSC_10,
|
||||
OOT_NTSC_11,
|
||||
OOT_NTSC_12,
|
||||
OOT_PAL_10,
|
||||
OOT_PAL_11,
|
||||
OOT_NTSC_JP_GC_CE,
|
||||
OOT_NTSC_JP_GC,
|
||||
OOT_NTSC_US_GC,
|
||||
OOT_PAL_GC,
|
||||
OOT_PAL_GC_DBG1,
|
||||
OOT_PAL_GC_DBG2
|
||||
};
|
||||
context = Ship::Window::CreateInstance("Ship of Harkinian", OTRFiles, ValidHashes);
|
||||
gSaveStateMgr = std::make_shared<SaveStateMgr>();
|
||||
gRandomizer = std::make_shared<Randomizer>();
|
||||
|
||||
hasMasterQuest = hasOriginal = false;
|
||||
|
||||
auto versions = context->GetResourceManager()->GetGameVersions();
|
||||
|
||||
for (uint32_t version : versions) {
|
||||
switch (version) {
|
||||
case OOT_PAL_MQ:
|
||||
case OOT_NTSC_JP_MQ:
|
||||
case OOT_NTSC_US_MQ:
|
||||
case OOT_PAL_GC_MQ_DBG:
|
||||
hasMasterQuest = true;
|
||||
break;
|
||||
case OOT_NTSC_10:
|
||||
case OOT_NTSC_11:
|
||||
case OOT_NTSC_12:
|
||||
case OOT_PAL_10:
|
||||
case OOT_PAL_11:
|
||||
case OOT_NTSC_JP_GC_CE:
|
||||
case OOT_NTSC_JP_GC:
|
||||
case OOT_NTSC_US_GC:
|
||||
case OOT_PAL_GC:
|
||||
case OOT_PAL_GC_DBG1:
|
||||
case OOT_PAL_GC_DBG2:
|
||||
hasOriginal = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OTRGlobals::~OTRGlobals() {
|
||||
}
|
||||
|
||||
bool OTRGlobals::HasMasterQuest() {
|
||||
return hasMasterQuest;
|
||||
}
|
||||
|
||||
bool OTRGlobals::HasOriginal() {
|
||||
return hasOriginal;
|
||||
}
|
||||
|
||||
struct ExtensionEntry {
|
||||
std::string path;
|
||||
std::string ext;
|
||||
@ -341,7 +405,10 @@ extern "C" void InitOTR() {
|
||||
|
||||
if (!t->bHasLoadError)
|
||||
{
|
||||
uint32_t gameVersion = LE32SWAP(*((uint32_t*)t->buffer.get()));
|
||||
Ship::BinaryReader reader(t->buffer.get(), t->dwBufferSize);
|
||||
Ship::Endianness endianness = (Ship::Endianness)reader.ReadUByte();
|
||||
reader.SetEndianness(endianness);
|
||||
uint32_t gameVersion = reader.ReadUInt32();
|
||||
OTRGlobals::Instance->context->GetResourceManager()->SetGameVersion(gameVersion);
|
||||
}
|
||||
|
||||
@ -549,32 +616,37 @@ extern "C" uint32_t ResourceMgr_GetGameVersion()
|
||||
return OTRGlobals::Instance->context->GetResourceManager()->GetGameVersion();
|
||||
}
|
||||
|
||||
extern "C" uint32_t ResourceMgr_IsGameMasterQuest() {
|
||||
uint32_t version = OTRGlobals::Instance->context->GetResourceManager()->GetGameVersion();
|
||||
|
||||
switch (version) {
|
||||
case OOT_PAL_MQ:
|
||||
case OOT_NTSC_JP_MQ:
|
||||
case OOT_NTSC_US_MQ:
|
||||
case OOT_PAL_GC_MQ_DBG:
|
||||
return 1;
|
||||
case OOT_NTSC_10:
|
||||
case OOT_NTSC_11:
|
||||
case OOT_NTSC_12:
|
||||
case OOT_PAL_10:
|
||||
case OOT_PAL_11:
|
||||
case OOT_NTSC_JP_GC_CE:
|
||||
case OOT_NTSC_JP_GC:
|
||||
case OOT_NTSC_US_GC:
|
||||
case OOT_PAL_GC:
|
||||
case OOT_PAL_GC_DBG1:
|
||||
case OOT_PAL_GC_DBG2:
|
||||
return 0;
|
||||
default:
|
||||
SPDLOG_WARN("Unknown rom detected. Defaulting to Non-mq {:x}", version);
|
||||
return 0;
|
||||
|
||||
uint32_t IsGameMasterQuest() {
|
||||
uint32_t value = 0;
|
||||
if (OTRGlobals::Instance->HasMasterQuest()) {
|
||||
if (!OTRGlobals::Instance->HasOriginal()) {
|
||||
value = 1;
|
||||
} else if (gSaveContext.isMasterQuest) {
|
||||
value = 1;
|
||||
} else {
|
||||
value = 0;
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
if (!OTRGlobals::Instance->gRandomizer->masterQuestDungeons.empty()) {
|
||||
if (gGlobalCtx != NULL && OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(gGlobalCtx->sceneNum)) {
|
||||
value = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
extern "C" uint32_t ResourceMgr_GameHasMasterQuest() {
|
||||
return OTRGlobals::Instance->HasMasterQuest();
|
||||
}
|
||||
|
||||
extern "C" uint32_t ResourceMgr_GameHasOriginal() {
|
||||
return OTRGlobals::Instance->HasOriginal();
|
||||
}
|
||||
|
||||
extern "C" uint32_t ResourceMgr_IsGameMasterQuest() {
|
||||
return IsGameMasterQuest();
|
||||
}
|
||||
|
||||
extern "C" void ResourceMgr_CacheDirectory(const char* resName) {
|
||||
@ -608,6 +680,17 @@ extern "C" void ResourceMgr_LoadFile(const char* resName) {
|
||||
OTRGlobals::Instance->context->GetResourceManager()->LoadResource(resName);
|
||||
}
|
||||
|
||||
std::shared_ptr<Ship::Resource> ResourceMgr_LoadResource(const char* path) {
|
||||
std::string Path = path;
|
||||
if (ResourceMgr_IsGameMasterQuest()) {
|
||||
size_t pos = 0;
|
||||
if ((pos = Path.find("/nonmq/", 0)) != std::string::npos) {
|
||||
Path.replace(pos, 7, "/mq/");
|
||||
}
|
||||
}
|
||||
return OTRGlobals::Instance->context->GetResourceManager()->LoadResource(Path.c_str());
|
||||
}
|
||||
|
||||
extern "C" char* ResourceMgr_LoadFileRaw(const char* resName) {
|
||||
return OTRGlobals::Instance->context->GetResourceManager()->LoadFile(resName)->buffer.get();
|
||||
}
|
||||
@ -673,14 +756,22 @@ extern "C" uint16_t ResourceMgr_LoadTexHeightByName(char* texPath);
|
||||
extern "C" uint32_t ResourceMgr_LoadTexSizeByName(const char* texPath);
|
||||
|
||||
extern "C" char* ResourceMgr_LoadTexOrDListByName(const char* filePath) {
|
||||
auto res = OTRGlobals::Instance->context->GetResourceManager()->LoadResource(filePath);
|
||||
auto res = ResourceMgr_LoadResource(filePath);
|
||||
|
||||
if (res->resType == Ship::ResourceType::DisplayList)
|
||||
return (char*)&((std::static_pointer_cast<Ship::DisplayList>(res))->instructions[0]);
|
||||
else if (res->resType == Ship::ResourceType::Array)
|
||||
return (char*)(std::static_pointer_cast<Ship::Array>(res))->vertices.data();
|
||||
else
|
||||
return ResourceMgr_LoadTexByName(filePath);
|
||||
else {
|
||||
std::string Path = filePath;
|
||||
if (ResourceMgr_IsGameMasterQuest()) {
|
||||
size_t pos = 0;
|
||||
if ((pos = Path.find("/nonmq/", 0)) != std::string::npos) {
|
||||
Path.replace(pos, 7, "/mq/");
|
||||
}
|
||||
}
|
||||
return ResourceMgr_LoadTexByName(Path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" Sprite* GetSeedTexture(uint8_t index) {
|
||||
@ -688,16 +779,14 @@ extern "C" Sprite* GetSeedTexture(uint8_t index) {
|
||||
}
|
||||
|
||||
extern "C" char* ResourceMgr_LoadPlayerAnimByName(const char* animPath) {
|
||||
auto anim = std::static_pointer_cast<Ship::PlayerAnimation>(
|
||||
OTRGlobals::Instance->context->GetResourceManager()->LoadResource(animPath));
|
||||
auto anim = std::static_pointer_cast<Ship::PlayerAnimation>(ResourceMgr_LoadResource(animPath));
|
||||
|
||||
return (char*)&anim->limbRotData[0];
|
||||
}
|
||||
|
||||
extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path)
|
||||
{
|
||||
auto res = std::static_pointer_cast<Ship::DisplayList>(
|
||||
OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto res = std::static_pointer_cast<Ship::DisplayList>(ResourceMgr_LoadResource(path));
|
||||
return (Gfx*)&res->instructions[0];
|
||||
}
|
||||
|
||||
@ -753,14 +842,13 @@ extern "C" void ResourceMgr_UnpatchGfxByName(const char* path, const char* patch
|
||||
|
||||
extern "C" char* ResourceMgr_LoadArrayByName(const char* path)
|
||||
{
|
||||
auto res = std::static_pointer_cast<Ship::Array>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto res = std::static_pointer_cast<Ship::Array>(ResourceMgr_LoadResource(path));
|
||||
|
||||
return (char*)res->scalars.data();
|
||||
}
|
||||
|
||||
extern "C" char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path) {
|
||||
auto res =
|
||||
std::static_pointer_cast<Ship::Array>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto res = std::static_pointer_cast<Ship::Array>(ResourceMgr_LoadResource(path));
|
||||
|
||||
if (res->cachedGameAsset != nullptr)
|
||||
return (char*)res->cachedGameAsset;
|
||||
@ -782,7 +870,7 @@ extern "C" char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path) {
|
||||
|
||||
extern "C" CollisionHeader* ResourceMgr_LoadColByName(const char* path)
|
||||
{
|
||||
auto colRes = std::static_pointer_cast<Ship::CollisionHeader>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto colRes = std::static_pointer_cast<Ship::CollisionHeader>(ResourceMgr_LoadResource(path));
|
||||
|
||||
if (colRes->cachedGameAsset != nullptr)
|
||||
return (CollisionHeader*)colRes->cachedGameAsset;
|
||||
@ -876,8 +964,8 @@ extern "C" CollisionHeader* ResourceMgr_LoadColByName(const char* path)
|
||||
|
||||
extern "C" Vtx* ResourceMgr_LoadVtxByName(const char* path)
|
||||
{
|
||||
auto res = std::static_pointer_cast<Ship::Array>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
return (Vtx*)res->vertices.data();
|
||||
auto res = std::static_pointer_cast<Ship::Array>(ResourceMgr_LoadResource(path));
|
||||
return (Vtx*)res->vertices.data();
|
||||
}
|
||||
|
||||
extern "C" SequenceData ResourceMgr_LoadSeqByName(const char* path)
|
||||
@ -971,8 +1059,7 @@ extern "C" SoundFontSample* ResourceMgr_LoadAudioSample(const char* path)
|
||||
if (cSample != nullptr)
|
||||
return cSample;
|
||||
|
||||
auto sample = std::static_pointer_cast<Ship::AudioSample>(
|
||||
OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto sample = std::static_pointer_cast<Ship::AudioSample>(ResourceMgr_LoadResource(path));
|
||||
|
||||
if (sample == nullptr)
|
||||
return NULL;
|
||||
@ -1018,8 +1105,7 @@ extern "C" SoundFontSample* ResourceMgr_LoadAudioSample(const char* path)
|
||||
}
|
||||
|
||||
extern "C" SoundFont* ResourceMgr_LoadAudioSoundFont(const char* path) {
|
||||
auto soundFont =
|
||||
std::static_pointer_cast<Ship::AudioSoundFont>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto soundFont = std::static_pointer_cast<Ship::AudioSoundFont>(ResourceMgr_LoadResource(path));
|
||||
|
||||
if (soundFont == nullptr)
|
||||
return NULL;
|
||||
@ -1162,8 +1248,7 @@ extern "C" int ResourceMgr_OTRSigCheck(char* imgData)
|
||||
}
|
||||
|
||||
extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path) {
|
||||
auto res = std::static_pointer_cast<Ship::Animation>(
|
||||
OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto res = std::static_pointer_cast<Ship::Animation>(ResourceMgr_LoadResource(path));
|
||||
|
||||
if (res->cachedGameAsset != nullptr)
|
||||
return (AnimationHeaderCommon*)res->cachedGameAsset;
|
||||
@ -1231,7 +1316,7 @@ extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path) {
|
||||
}
|
||||
|
||||
extern "C" SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path) {
|
||||
auto res = std::static_pointer_cast<Ship::Skeleton>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto res = std::static_pointer_cast<Ship::Skeleton>(ResourceMgr_LoadResource(path));
|
||||
|
||||
if (res->cachedGameAsset != nullptr)
|
||||
return (SkeletonHeader*)res->cachedGameAsset;
|
||||
@ -1264,8 +1349,7 @@ extern "C" SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path) {
|
||||
|
||||
for (size_t i = 0; i < res->limbTable.size(); i++) {
|
||||
std::string limbStr = res->limbTable[i];
|
||||
auto limb = std::static_pointer_cast<Ship::SkeletonLimb>(
|
||||
OTRGlobals::Instance->context->GetResourceManager()->LoadResource(limbStr.c_str()));
|
||||
auto limb = std::static_pointer_cast<Ship::SkeletonLimb>(ResourceMgr_LoadResource(limbStr.c_str()));
|
||||
|
||||
if (limb->limbType == Ship::LimbType::LOD) {
|
||||
LodLimb* limbC = (LodLimb*)malloc(sizeof(LodLimb));
|
||||
@ -1419,7 +1503,7 @@ extern "C" SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path) {
|
||||
|
||||
extern "C" s32* ResourceMgr_LoadCSByName(const char* path)
|
||||
{
|
||||
auto res = std::static_pointer_cast<Ship::Cutscene>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path));
|
||||
auto res = std::static_pointer_cast<Ship::Cutscene>(ResourceMgr_LoadResource(path));
|
||||
return (s32*)res->commands.data();
|
||||
}
|
||||
|
||||
@ -1670,6 +1754,10 @@ extern "C" void Randomizer_LoadRequiredTrials(const char* spoilerFileName) {
|
||||
OTRGlobals::Instance->gRandomizer->LoadRequiredTrials(spoilerFileName);
|
||||
}
|
||||
|
||||
extern "C" void Randomizer_LoadMasterQuestDungeons(const char* spoilerFileName) {
|
||||
OTRGlobals::Instance->gRandomizer->LoadMasterQuestDungeons(spoilerFileName);
|
||||
}
|
||||
|
||||
extern "C" void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent) {
|
||||
OTRGlobals::Instance->gRandomizer->LoadItemLocations(spoilerFileName, silent);
|
||||
}
|
||||
|
@ -25,13 +25,20 @@ public:
|
||||
OTRGlobals();
|
||||
~OTRGlobals();
|
||||
|
||||
bool HasMasterQuest();
|
||||
bool HasOriginal();
|
||||
|
||||
private:
|
||||
void CheckSaveFile(size_t sramSize) const;
|
||||
bool hasMasterQuest;
|
||||
bool hasOriginal;
|
||||
};
|
||||
|
||||
uint32_t IsGameMasterQuest();
|
||||
#endif
|
||||
|
||||
#ifndef __cplusplus
|
||||
void InitOTR(void);
|
||||
void InitOTR(void);
|
||||
void DeinitOTR(void);
|
||||
void VanillaItemTable_Init();
|
||||
void OTRAudio_Init();
|
||||
@ -43,8 +50,10 @@ void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char))
|
||||
void OTRGetPixelDepthPrepare(float x, float y);
|
||||
uint16_t OTRGetPixelDepth(float x, float y);
|
||||
int32_t OTRGetLastScancode();
|
||||
uint32_t ResourceMgr_GetGameVersion();
|
||||
uint32_t ResourceMgr_IsGameMasterQuest();
|
||||
uint32_t ResourceMgr_GameHasMasterQuest();
|
||||
uint32_t ResourceMgr_GameHasOriginal();
|
||||
uint32_t ResourceMgr_GetGameVersion();
|
||||
void ResourceMgr_CacheDirectory(const char* resName);
|
||||
char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize);
|
||||
void ResourceMgr_LoadFile(const char* resName);
|
||||
@ -106,6 +115,7 @@ ShopItemIdentity Randomizer_IdentifyShopItem(s32 sceneNum, u8 slotIndex);
|
||||
void Randomizer_LoadHintLocations(const char* spoilerFileName);
|
||||
void Randomizer_LoadMerchantMessages(const char* spoilerFileName);
|
||||
void Randomizer_LoadRequiredTrials(const char* spoilerFileName);
|
||||
void Randomizer_LoadMasterQuestDungeons(const char* spoilerFileName);
|
||||
void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent);
|
||||
bool Randomizer_IsTrialRequired(RandomizerInf trial);
|
||||
GetItemEntry Randomizer_GetItemFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogId);
|
||||
|
@ -50,7 +50,8 @@ SaveManager::SaveManager() {
|
||||
}
|
||||
|
||||
info.randoSave = 0;
|
||||
info.isMasterQuest = 0;
|
||||
info.requiresMasterQuest = 0;
|
||||
info.requiresOriginal = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,6 +195,15 @@ void SaveManager::LoadRandomizerVersion2() {
|
||||
randomizer->merchantPrices[rc] = price;
|
||||
});
|
||||
});
|
||||
|
||||
SaveManager::Instance->LoadData("masterQuestDungeonCount", gSaveContext.mqDungeonCount);
|
||||
|
||||
OTRGlobals::Instance->gRandomizer->masterQuestDungeons.clear();
|
||||
SaveManager::Instance->LoadArray("masterQuestDungeons", randomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT), [&](size_t i) {
|
||||
uint16_t scene;
|
||||
SaveManager::Instance->LoadData("", scene);
|
||||
randomizer->masterQuestDungeons.emplace(scene);
|
||||
});
|
||||
}
|
||||
|
||||
void SaveManager::SaveRandomizer() {
|
||||
@ -245,6 +255,16 @@ void SaveManager::SaveRandomizer() {
|
||||
SaveManager::Instance->SaveData("price", merchantPrices[i].second);
|
||||
});
|
||||
});
|
||||
|
||||
SaveManager::Instance->SaveData("masterQuestDungeonCount", gSaveContext.mqDungeonCount);
|
||||
|
||||
std::vector<uint16_t> masterQuestDungeons;
|
||||
for (const auto scene : randomizer->masterQuestDungeons) {
|
||||
masterQuestDungeons.push_back(scene);
|
||||
}
|
||||
SaveManager::Instance->SaveArray("masterQuestDungeons", masterQuestDungeons.size(), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", masterQuestDungeons[i]);
|
||||
});
|
||||
}
|
||||
|
||||
void SaveManager::Init() {
|
||||
@ -318,7 +338,8 @@ void SaveManager::InitMeta(int fileNum) {
|
||||
}
|
||||
|
||||
fileMetaInfo[fileNum].randoSave = gSaveContext.n64ddFlag;
|
||||
fileMetaInfo[fileNum].isMasterQuest = gSaveContext.isMasterQuest;
|
||||
fileMetaInfo[fileNum].requiresMasterQuest = gSaveContext.isMasterQuest || gSaveContext.mqDungeonCount > 0;
|
||||
fileMetaInfo[fileNum].requiresOriginal = !gSaveContext.isMasterQuest || gSaveContext.mqDungeonCount < 12;
|
||||
}
|
||||
|
||||
void SaveManager::InitFile(bool isDebug) {
|
||||
@ -465,7 +486,7 @@ void SaveManager::InitFileNormal() {
|
||||
gSaveContext.infTable[29] = 1;
|
||||
gSaveContext.sceneFlags[5].swch = 0x40000000;
|
||||
|
||||
gSaveContext.isMasterQuest = ResourceMgr_IsGameMasterQuest();
|
||||
gSaveContext.isMasterQuest = CVar_GetS32("gMasterQuest", 0) && !CVar_GetS32("gRandomizer", 0);
|
||||
|
||||
//RANDOTODO (ADD ITEMLOCATIONS TO GSAVECONTEXT)
|
||||
}
|
||||
@ -1297,7 +1318,8 @@ void SaveManager::CopyZeldaFile(int from, int to) {
|
||||
fileMetaInfo[to].defense = fileMetaInfo[from].defense;
|
||||
fileMetaInfo[to].health = fileMetaInfo[from].health;
|
||||
fileMetaInfo[to].randoSave = fileMetaInfo[from].randoSave;
|
||||
fileMetaInfo[to].isMasterQuest = fileMetaInfo[from].isMasterQuest;
|
||||
fileMetaInfo[to].requiresMasterQuest = fileMetaInfo[from].requiresMasterQuest;
|
||||
fileMetaInfo[to].requiresOriginal = fileMetaInfo[from].requiresOriginal;
|
||||
}
|
||||
|
||||
void SaveManager::DeleteZeldaFile(int fileNum) {
|
||||
|
@ -10,7 +10,8 @@ typedef struct {
|
||||
u32 questItems;
|
||||
s8 defense;
|
||||
u16 health;
|
||||
u32 isMasterQuest;
|
||||
u32 requiresMasterQuest;
|
||||
u32 requiresOriginal;
|
||||
u8 seedHash[5];
|
||||
u8 randoSave;
|
||||
} SaveFileMetaInfo;
|
||||
|
@ -28,7 +28,13 @@ extern "C" void OTRGameplay_SpawnScene(GlobalContext* globalCtx, s32 sceneNum, s
|
||||
|
||||
//osSyncPrintf("\nSCENE SIZE %fK\n", (scene->sceneFile.vromEnd - scene->sceneFile.vromStart) / 1024.0f);
|
||||
|
||||
std::string scenePath = StringHelper::Sprintf("scenes/%s/%s", scene->sceneFile.fileName, scene->sceneFile.fileName);
|
||||
std::string sceneVersion;
|
||||
if (IsGameMasterQuest()) {
|
||||
sceneVersion = "mq";
|
||||
} else {
|
||||
sceneVersion = "nonmq";
|
||||
}
|
||||
std::string scenePath = StringHelper::Sprintf("scenes/%s/%s/%s", sceneVersion.c_str(), scene->sceneFile.fileName, scene->sceneFile.fileName);
|
||||
|
||||
globalCtx->sceneSegment = (Ship::Scene*)OTRGameplay_LoadFile(globalCtx, scenePath.c_str());
|
||||
|
||||
|
@ -16,6 +16,20 @@ extern "C" s32 Object_Spawn(ObjectContext* objectCtx, s16 objectId);
|
||||
extern "C" RomFile sNaviMsgFiles[];
|
||||
s32 OTRScene_ExecuteCommands(GlobalContext* globalCtx, Ship::Scene* scene);
|
||||
|
||||
std::shared_ptr<Ship::File> ResourceMgr_LoadFile(const char* path) {
|
||||
std::string Path = path;
|
||||
if (IsGameMasterQuest()) {
|
||||
size_t pos = 0;
|
||||
if ((pos = Path.find("/nonmq/", 0)) != std::string::npos) {
|
||||
Path.replace(pos, 7, "/mq/");
|
||||
}
|
||||
}
|
||||
return OTRGlobals::Instance->context->GetResourceManager()->LoadFile(Path.c_str());
|
||||
}
|
||||
|
||||
// Forward Declaration of function declared in OTRGlobals.cpp
|
||||
std::shared_ptr<Ship::Resource> ResourceMgr_LoadResource(const char* path);
|
||||
|
||||
bool Scene_CommandSpawnList(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
{
|
||||
Ship::SetStartPositionList* cmdStartPos = (Ship::SetStartPositionList*)cmd;
|
||||
@ -103,7 +117,7 @@ bool Scene_CommandCollisionHeader(GlobalContext* globalCtx, Ship::SceneCommand*
|
||||
{
|
||||
Ship::SetCollisionHeader* cmdCol = (Ship::SetCollisionHeader*)cmd;
|
||||
|
||||
auto colRes = std::static_pointer_cast<Ship::CollisionHeader>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(cmdCol->filePath));
|
||||
auto colRes = std::static_pointer_cast<Ship::CollisionHeader>(ResourceMgr_LoadResource(cmdCol->filePath.c_str()));
|
||||
|
||||
CollisionHeader* colHeader = nullptr;
|
||||
|
||||
@ -298,7 +312,8 @@ bool Scene_CommandMeshHeader(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
|
||||
if (otrMesh->meshes[i].opa != "")
|
||||
{
|
||||
auto opaFile = std::static_pointer_cast<Ship::DisplayList>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[i].opa));
|
||||
auto opaFile =
|
||||
std::static_pointer_cast<Ship::DisplayList>(ResourceMgr_LoadResource(otrMesh->meshes[i].opa.c_str()));
|
||||
|
||||
dlist->opaDL = opaFile.get();
|
||||
dlist->opa = (Gfx*)&dlist->opaDL->instructions[0];
|
||||
@ -310,7 +325,8 @@ bool Scene_CommandMeshHeader(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
|
||||
if (otrMesh->meshes[i].xlu != "")
|
||||
{
|
||||
auto xluFile = std::static_pointer_cast<Ship::DisplayList>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[i].xlu));
|
||||
auto xluFile =
|
||||
std::static_pointer_cast<Ship::DisplayList>(ResourceMgr_LoadResource(otrMesh->meshes[i].xlu.c_str()));
|
||||
|
||||
dlist->xluDL = xluFile.get();
|
||||
dlist->xlu = (Gfx*)&dlist->xluDL->instructions[0];
|
||||
@ -330,12 +346,12 @@ bool Scene_CommandMeshHeader(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
PolygonDlist* pType = (PolygonDlist*)malloc(sizeof(PolygonDlist));
|
||||
|
||||
if (otrMesh->meshes[0].imgOpa != "")
|
||||
pType->opa = (Gfx*)&std::static_pointer_cast<Ship::DisplayList>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[0].imgOpa))->instructions[0];
|
||||
pType->opa = (Gfx*)&std::static_pointer_cast<Ship::DisplayList>(ResourceMgr_LoadResource(otrMesh->meshes[0].imgOpa.c_str()))->instructions[0];
|
||||
else
|
||||
pType->opa = 0;
|
||||
|
||||
if (otrMesh->meshes[0].imgXlu != "")
|
||||
pType->xlu = (Gfx*)&std::static_pointer_cast<Ship::DisplayList>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[0].imgXlu))->instructions[0];
|
||||
pType->xlu = (Gfx*)&std::static_pointer_cast<Ship::DisplayList>(ResourceMgr_LoadResource(otrMesh->meshes[0].imgXlu.c_str()))->instructions[0];
|
||||
else
|
||||
pType->xlu = 0;
|
||||
|
||||
@ -347,8 +363,7 @@ bool Scene_CommandMeshHeader(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
{
|
||||
globalCtx->roomCtx.curRoom.meshHeader->polygon1.single.fmt = otrMesh->meshes[0].images[0].fmt;
|
||||
globalCtx->roomCtx.curRoom.meshHeader->polygon1.single.source =
|
||||
(void*)(OTRGlobals::Instance->context->GetResourceManager()->LoadFile(
|
||||
otrMesh->meshes[0].images[0].sourceBackground))
|
||||
(void*)(ResourceMgr_LoadFile(otrMesh->meshes[0].images[0].sourceBackground.c_str()))
|
||||
.get()
|
||||
->buffer.get();
|
||||
globalCtx->roomCtx.curRoom.meshHeader->polygon1.single.siz = otrMesh->meshes[0].images[0].siz;
|
||||
@ -368,8 +383,7 @@ bool Scene_CommandMeshHeader(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
{
|
||||
globalCtx->roomCtx.curRoom.meshHeader->polygon1.multi.list[i].fmt = otrMesh->meshes[0].images[i].fmt;
|
||||
globalCtx->roomCtx.curRoom.meshHeader->polygon1.multi.list[i].source =
|
||||
(void*)(OTRGlobals::Instance->context->GetResourceManager()->LoadFile(
|
||||
otrMesh->meshes[0].images[i].sourceBackground))
|
||||
(void*)(ResourceMgr_LoadFile(otrMesh->meshes[0].images[i].sourceBackground.c_str()))
|
||||
.get()
|
||||
->buffer.get();
|
||||
globalCtx->roomCtx.curRoom.meshHeader->polygon1.multi.list[i].siz = otrMesh->meshes[0].images[i].siz;
|
||||
@ -395,7 +409,7 @@ bool Scene_CommandMeshHeader(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
|
||||
if (otrMesh->meshes[i].opa != "")
|
||||
{
|
||||
auto opaFile = std::static_pointer_cast<Ship::DisplayList>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[i].opa));
|
||||
auto opaFile = std::static_pointer_cast<Ship::DisplayList>(ResourceMgr_LoadResource(otrMesh->meshes[i].opa.c_str()));
|
||||
|
||||
dlist->opaDL = opaFile.get();
|
||||
dlist->opa = (Gfx*)&dlist->opaDL->instructions[0];
|
||||
@ -405,7 +419,7 @@ bool Scene_CommandMeshHeader(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
|
||||
if (otrMesh->meshes[i].xlu != "")
|
||||
{
|
||||
auto xluFile = std::static_pointer_cast<Ship::DisplayList>(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[i].xlu));
|
||||
auto xluFile = std::static_pointer_cast<Ship::DisplayList>(ResourceMgr_LoadResource(otrMesh->meshes[i].xlu.c_str()));
|
||||
|
||||
dlist->xluDL = xluFile.get();
|
||||
dlist->xlu = (Gfx*)&dlist->xluDL->instructions[0];
|
||||
@ -515,7 +529,7 @@ bool Scene_CommandPathList(GlobalContext* globalCtx, Ship::SceneCommand* cmd)
|
||||
{
|
||||
Ship::SetPathways* cmdPath = (Ship::SetPathways*)cmd;
|
||||
|
||||
Ship::Path* path = (Ship::Path*)OTRGlobals::Instance->context->GetResourceManager()->LoadResource(cmdPath->paths[0]).get();
|
||||
Ship::Path* path = (Ship::Path*)ResourceMgr_LoadResource(cmdPath->paths[0].c_str()).get();
|
||||
globalCtx->setupPathList = (Path*)malloc(path->paths.size() * sizeof(Path));
|
||||
|
||||
//for (int i = 0; i < cmdPath->paths.size(); i++)
|
||||
@ -740,8 +754,7 @@ bool Scene_CommandAlternateHeaderList(GlobalContext* globalCtx, Ship::SceneComma
|
||||
std::string desiredHeader = cmdHeaders->headers[gSaveContext.sceneSetupIndex - 1];
|
||||
Ship::Scene* headerData = nullptr;
|
||||
if (desiredHeader != "") {
|
||||
headerData =
|
||||
(Ship::Scene*)OTRGlobals::Instance->context->GetResourceManager()->LoadResource(desiredHeader).get();
|
||||
headerData = (Ship::Scene*)ResourceMgr_LoadResource(desiredHeader.c_str()).get();
|
||||
}
|
||||
|
||||
if (headerData != nullptr)
|
||||
@ -759,9 +772,7 @@ bool Scene_CommandAlternateHeaderList(GlobalContext* globalCtx, Ship::SceneComma
|
||||
std::string desiredHeader = cmdHeaders->headers[gSaveContext.sceneSetupIndex - 2];
|
||||
Ship::Scene* headerData = nullptr;
|
||||
if (desiredHeader != "") {
|
||||
headerData = (Ship::Scene*)OTRGlobals::Instance->context->GetResourceManager()
|
||||
->LoadResource(desiredHeader)
|
||||
.get();
|
||||
headerData = (Ship::Scene*)ResourceMgr_LoadResource(desiredHeader.c_str()).get();
|
||||
}
|
||||
|
||||
// "Using adult day data there!"
|
||||
@ -782,7 +793,7 @@ bool Scene_CommandCutsceneData(GlobalContext* globalCtx, Ship::SceneCommand* cmd
|
||||
{
|
||||
Ship::SetCutscenes* cmdCS = (Ship::SetCutscenes*)cmd;
|
||||
|
||||
Ship::Cutscene* csData = (Ship::Cutscene*)OTRGlobals::Instance->context->GetResourceManager()->LoadResource(cmdCS->cutscenePath).get();
|
||||
Ship::Cutscene* csData = (Ship::Cutscene*)ResourceMgr_LoadResource(cmdCS->cutscenePath.c_str()).get();
|
||||
globalCtx->csCtx.segment = csData->commands.data();
|
||||
|
||||
//osSyncPrintf("\ngame_play->demo_play.data=[%x]", globalCtx->csCtx.segment);
|
||||
@ -921,7 +932,7 @@ extern "C" s32 OTRfunc_8009728C(GlobalContext* globalCtx, RoomContext* roomCtx,
|
||||
//DmaMgr_SendRequest2(&roomCtx->dmaRequest, roomCtx->unk_34, globalCtx->roomList[roomNum].vromStart, size, 0,
|
||||
//&roomCtx->loadQueue, NULL, __FILE__, __LINE__);
|
||||
|
||||
auto roomData = OTRGlobals::Instance->context->GetResourceManager()->LoadResource(globalCtx->roomList[roomNum].fileName);
|
||||
auto roomData = ResourceMgr_LoadResource(globalCtx->roomList[roomNum].fileName);
|
||||
roomCtx->status = 1;
|
||||
roomCtx->roomToLoad = (Ship::Scene*)roomData.get();
|
||||
|
||||
|
@ -297,8 +297,9 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
|
||||
gSaveContext.playerName[offset] = Save_GetSaveMetaInfo(fileChooseCtx->buttonIndex)->playerName[offset];
|
||||
}
|
||||
|
||||
if (CVar_GetS32("gRandomizer", 0) != 0 &&
|
||||
strcmp(CVar_GetString("gSpoilerLog", ""), "") != 0) {
|
||||
if (CVar_GetS32("gRandomizer", 0) && strnlen(CVar_GetString("gSpoilerLog", ""), 1) != 0 &&
|
||||
!((Save_GetSaveMetaInfo(fileChooseCtx->buttonIndex)->requiresMasterQuest && !ResourceMgr_GameHasMasterQuest()) ||
|
||||
(Save_GetSaveMetaInfo(fileChooseCtx->buttonIndex)->requiresMasterQuest && !ResourceMgr_GameHasOriginal()))) {
|
||||
// Set N64DD Flags for save file
|
||||
fileChooseCtx->n64ddFlags[fileChooseCtx->buttonIndex] = 1;
|
||||
fileChooseCtx->n64ddFlag = 1;
|
||||
|
@ -32,7 +32,14 @@ static s16 sWindowContentColors[2][3] = {
|
||||
};
|
||||
|
||||
static int FileChoose_IsSaveCompatible(const SaveFileMetaInfo* restrict meta) {
|
||||
return meta->isMasterQuest == ResourceMgr_IsGameMasterQuest();
|
||||
bool valid = true;
|
||||
if (meta->requiresMasterQuest) {
|
||||
valid = valid && ResourceMgr_GameHasMasterQuest();
|
||||
}
|
||||
if (meta->requiresOriginal) {
|
||||
valid = valid && ResourceMgr_GameHasOriginal();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
void FileChoose_SetView(FileChooseContext* this, f32 eyeX, f32 eyeY, f32 eyeZ) {
|
||||
@ -219,13 +226,15 @@ void DrawSeedHashSprites(FileChooseContext* this) {
|
||||
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0xFF, 0xFF, 0xFF, this->fileButtonAlpha[this->buttonIndex]);
|
||||
|
||||
if (CVar_GetS32("gRandomizer", 0) && strnlen(CVar_GetString("gSpoilerLog", ""), 1) != 0) {
|
||||
if (CVar_GetS32("gRandomizer", 0) && strnlen(CVar_GetString("gSpoilerLog", ""), 1) != 0 &&
|
||||
!((gSaveContext.mqDungeonCount > 0 && !ResourceMgr_GameHasMasterQuest())
|
||||
|| (gSaveContext.mqDungeonCount < 12 && !ResourceMgr_GameHasOriginal()))) {
|
||||
u16 xStart = 64;
|
||||
for (unsigned int i = 0; i < 5; i++) {
|
||||
SpriteLoad(this, GetSeedTexture(gSaveContext.seedIcons[i]));
|
||||
SpriteDraw(this, GetSeedTexture(gSaveContext.seedIcons[i]), xStart + (40 * i), 10, 24, 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
@ -235,6 +244,7 @@ void DrawSeedHashSprites(FileChooseContext* this) {
|
||||
|
||||
u8 generating;
|
||||
bool fileSelectSpoilerFileLoaded;
|
||||
bool shouldLoadSpoilerFile;
|
||||
|
||||
/**
|
||||
* Update the cursor and wait for the player to select a button to change menus accordingly.
|
||||
@ -270,7 +280,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
|
||||
|
||||
if ((CVar_GetS32("gNewFileDropped", 0) != 0) ||
|
||||
(CVar_GetS32("gNewSeedGenerated", 0) != 0) ||
|
||||
(!fileSelectSpoilerFileLoaded &&
|
||||
(!fileSelectSpoilerFileLoaded && shouldLoadSpoilerFile &&
|
||||
SpoilerFileExists(CVar_GetString("gSpoilerLog", "")))) {
|
||||
if (CVar_GetS32("gNewFileDropped", 0) != 0) {
|
||||
CVar_SetString("gSpoilerLog", CVar_GetString("gDroppedFile", "None"));
|
||||
@ -288,6 +298,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
|
||||
Randomizer_LoadSettings(fileLoc);
|
||||
Randomizer_LoadHintLocations(fileLoc);
|
||||
Randomizer_LoadRequiredTrials(fileLoc);
|
||||
Randomizer_LoadMasterQuestDungeons(fileLoc);
|
||||
Randomizer_LoadItemLocations(fileLoc, silent);
|
||||
Randomizer_LoadMerchantMessages(fileLoc);
|
||||
fileSelectSpoilerFileLoaded = true;
|
||||
@ -1118,7 +1129,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
|
||||
gSP1Quadrangle(POLY_OPA_DISP++, 8, 10, 11, 9, 0);
|
||||
}
|
||||
//Draw MQ label
|
||||
if (Save_GetSaveMetaInfo(i)->isMasterQuest && Save_GetSaveMetaInfo(i)->valid) {
|
||||
if (Save_GetSaveMetaInfo(i)->requiresMasterQuest && !Save_GetSaveMetaInfo(i)->randoSave && Save_GetSaveMetaInfo(i)->valid) {
|
||||
if (CVar_GetS32("gHudColors", 1) == 2 && FileChoose_IsSaveCompatible(Save_GetSaveMetaInfo(i))) {
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, CVar_GetRGB("gCCFileChoosePrim", Background_Color).r, CVar_GetRGB("gCCFileChoosePrim", Background_Color).g, CVar_GetRGB("gCCFileChoosePrim", Background_Color).b, this->nameAlpha[i]);
|
||||
} else if (!FileChoose_IsSaveCompatible(Save_GetSaveMetaInfo(i))) {
|
||||
@ -1150,7 +1161,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
|
||||
G_TX_NOLOD);
|
||||
gSP1Quadrangle(POLY_OPA_DISP++, 12, 14, 15, 13, 0);
|
||||
|
||||
if (Save_GetSaveMetaInfo(i)->randoSave || Save_GetSaveMetaInfo(i)->isMasterQuest) {
|
||||
if (Save_GetSaveMetaInfo(i)->randoSave || Save_GetSaveMetaInfo(i)->requiresMasterQuest) {
|
||||
gSP1Quadrangle(POLY_OPA_DISP++, 16, 18, 19, 17, 0);
|
||||
}
|
||||
}
|
||||
@ -2091,6 +2102,7 @@ void FileChoose_Init(GameState* thisx) {
|
||||
size_t size = (u32)_title_staticSegmentRomEnd - (u32)_title_staticSegmentRomStart;
|
||||
s32 pad;
|
||||
fileSelectSpoilerFileLoaded = false;
|
||||
shouldLoadSpoilerFile = true;
|
||||
CVar_SetS32("gOnFileSelectNameEntry", 0);
|
||||
|
||||
SREG(30) = 1;
|
||||
|
Loading…
Reference in New Issue
Block a user