Merge branch 'Random06457:develop' into develop

This commit is contained in:
briaguya 2022-05-17 21:22:37 -04:00 committed by GitHub
commit c023cc7d15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
288 changed files with 12212 additions and 8152 deletions

403
.gitignore vendored Normal file
View file

@ -0,0 +1,403 @@
# Cache files
__pycache__/
.pyc
.DS_Store
# Text editor remnants
.vscode/
.vs/
.idea/
CMakeLists.txt
cmake-build-debug
venv/
# Project-specific ignores
build/
expected/
notes/
baserom/
docs/doxygen/
*.elf
*.sra
*.z64
*.n64
*.v64
*.map
*.dump
out.txt
# Tool artifacts
tools/mipspro7.2_compiler/
tools/overlayhelpers/batchdisasm/output/*
tools/overlayhelpers/batchdisasm/output2/*
tools/overlayhelpers/batchdisasm/mipsdisasm/*
tools/disasm/output/*
tools/asmsplitter/asm/*
tools/asmsplitter/c/*
ctx.c
tools/*dSYM/
graphs/
# Assets
*.png
*.jpg
*.mdli
*.anmi
*.obj
*.mtl
*.fbx
!*_custom*
.extracted-assets.json
# Docs
!docs/tutorial/
# Per-user configuration
.python-version
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# 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/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# 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
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.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
# Visual Studio Trace Files
*.e2e
# 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
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# 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
# Note: 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
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable 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
*.appx
# 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
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# 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
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# 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
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
*.out
*.o
*.d
lib/libgfxd/libgfxd.a
ExporterTest/ExporterTest.a
ZAPDUtils/ZAPDUtils.a
.vscode/
build/
external/
ZAPDUtils/build/
ZAPD/BuildInfo.h
DebugObj/*
ReleaseObj/*

66
BUILDING.md Normal file
View file

@ -0,0 +1,66 @@
# Building Ship of Harkinian
## Windows
1. Requires [Python](https://www.python.org/downloads/) >= 3.6.
2. Install [Visual Studio 2022 Community Edition](https://visualstudio.microsoft.com/vs/community/)
3. In the Visual Studio Installer, install `MSVC v142 - VS 2019 C++`.
4. Clone the Ship of Harkinian repository.
5. Place one or more [compatible](#compatible-roms) roms in the `OTRExporter` directory with namings of your choice.
6. Run `OTRExporter/OTRExporter.sln`.
7. Switch the solution to `Release x64`.
8. Build the solution.
9. Launching `OTRExporter/extract_assets.py` will generate an `oot.otr` archive file in `OTRExporter/oot.otr`.
10. Run `soh/soh.sln`
11. Switch the solution to `Release x86`.
12. Build the solution.
13. Copy the `OTRExporter/oot.otr` archive file to `soh/Release`.
14. Launch `soh.exe`.
## Linux
```bash
# Clone the repo
git clone git@github.com:HarbourMasters/ShipWright.git
cd ShipWright
# Copy the baserom to the OTRExporter folder
cp <path to your ROM> OTRExporter
# Build the docker image
sudo docker build . -t soh
# Run the docker image with the working directory mounted to /soh
sudo docker run --rm -it -v $(pwd):/soh soh /bin/bash
```
Inside the Docker container:
```bash
# Clone and build StormLib
git clone https://github.com/ladislav-zezula/StormLib external/StormLib
cmake -B external/StormLib/build -S external/StormLib
cmake --build external/StormLib/build
cp external/StormLib/build/libstorm.a external
cp /usr/local/lib/libGLEW.a external
cd soh
# Extract the assets/Compile the exporter/Run the exporter
make setup -j$(nproc) OPTFLAGS=-O0 DEBUG=0
# Compile the code
make -j $(nproc) OPTFLAGS=-O0 DEBUG=0
```
# Compatible Roms
```
OOT_PAL_GC checksum 0x09465AC3
OOT_PAL_GC_DBG1 checksum 0x871E1C92 (debug non-master quest)
```
# OTRExporter Usage
The OTRExporter exports an `oot.otr` archive file which Ship of Harkinian requires to play.
Use the `extract_assets.py` script file to run the exporter using any of the following methods:
1) Double click on the script after placing one or more roms in the directory.
2) Drag & Drop a rom onto the script.
3) In a terminal run `python3 extract_assets.py` after placing one or more roms in the directory.
4) In a terminal run `python3 extract_assets.py <path_to_rom>`
If the script finds multiple roms the user is prompted which to use. Selection is done using the number keys and then pressing the carriage return key.

38
Dockerfile Normal file
View file

@ -0,0 +1,38 @@
FROM ubuntu:21.04 as build
ENV LANG C.UTF-8
ARG DEBIAN_FRONTEND=noninteractive
RUN dpkg --add-architecture i386 && \
apt-get update && \
apt-get upgrade -y && \
apt-get install -y \
binutils:i386 \
gcc-10:i386 \
g++-10:i386 \
python3.10 \
python \
make \
cmake \
git \
lld \
libsdl2-dev:i386 \
zlib1g-dev:i386 \
libbz2-dev:i386 \
libpng-dev:i386 \
libgles2-mesa-dev && \
ln -sf /usr/bin/python3.10 /usr/bin/python3 && \
ln -s /usr/bin/gcc-10 /usr/bin/gcc && \
ln -s /usr/bin/gcc-10 /usr/bin/cc && \
ln -s /usr/bin/g++-10 /usr/bin/g++ && \
ln -s /usr/bin/g++-10 /usr/bin/c++
RUN git clone https://github.com/Perlmint/glew-cmake.git && \
cmake glew-cmake && \
make -j$(nproc) && \
make install ARCH64=false
RUN mkdir /soh
WORKDIR /soh

117
Jenkinsfile vendored Normal file
View file

@ -0,0 +1,117 @@
pipeline {
agent none
options {
timestamps()
skipDefaultCheckout(true)
}
stages {
stage ('Build Windows') {
environment {
MSBUILD='C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Msbuild\\Current\\Bin\\msbuild.exe'
CONFIG='Release'
OTRPLATFORM='x64'
PLATFORM='x86'
ZIP='C:\\Program Files\\7-Zip\\7z.exe'
PYTHON='C:\\Users\\jenkins\\AppData\\Local\\Programs\\Python\\Python310\\python.exe'
CMAKE='C:\\Program Files\\CMake\\bin\\cmake.exe'
TOOLSET='v142'
}
agent {
label "SoH-Builders"
}
steps {
checkout([
$class: 'GitSCM',
branches: scm.branches,
doGenerateSubmoduleConfigurations: scm.doGenerateSubmoduleConfigurations,
extensions: scm.extensions,
userRemoteConfigs: scm.userRemoteConfigs
])
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
bat """
"${env.MSBUILD}" ".\\OTRExporter\\OTRExporter.sln" -t:build -p:Configuration=${env.CONFIG};Platform=${env.OTRPLATFORM};PlatformToolset=${env.TOOLSET};RestorePackagesConfig=true /restore /nodeReuse:false /m
xcopy "..\\..\\ZELOOTD.z64" "OTRExporter\\"
cd "OTRExporter"
"${env.PYTHON}" ".\\extract_assets.py"
cd "..\\"
"${env.MSBUILD}" ".\\soh\\soh.sln" -t:build -p:Configuration=${env.CONFIG};Platform=${env.PLATFORM};PlatformToolset=${env.TOOLSET} /nodeReuse:false /m
cd OTRGui
mkdir build
cd build
"${env.CMAKE}" ..
"${env.CMAKE}" --build . --config Release
cd "..\\..\\"
move "soh\\Release\\soh.exe" ".\\"
move "OTRGui\\build\\assets" ".\\"
move ".\\OTRExporter\\x64\\Release\\ZAPD.exe" ".\\assets\\extractor\\"
move ".\\OTRGui\\build\\Release\\OTRGui.exe" ".\\"
rename README.md readme.txt
"${env.ZIP}" a soh.7z soh.exe OTRGui.exe assets readme.txt
"""
archiveArtifacts artifacts: 'soh.7z', followSymlinks: false, onlyIfSuccessful: true
}
}
post {
always {
step([$class: 'WsCleanup']) // Clean workspace
}
}
}
stage ('Build Linux') {
agent {
label "SoH-Linux-Builders"
}
steps {
checkout([
$class: 'GitSCM',
branches: scm.branches,
doGenerateSubmoduleConfigurations: scm.doGenerateSubmoduleConfigurations,
extensions: scm.extensions,
userRemoteConfigs: scm.userRemoteConfigs
])
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
sh '''
cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64
docker build . -t soh
docker run --name sohcont -dit --rm -v $(pwd):/soh soh /bin/bash
cp ../../buildsoh.bash soh
docker exec sohcont soh/buildsoh.bash
mkdir build
mv soh/soh.elf build/
mv OTRGui/build/OTRGui build/
mv OTRGui/build/assets build/
mv ZAPDTR/ZAPD.out build/assets/extractor/
mv README.md build/readme.txt
cd build
7z a soh-linux.7z soh.elf OTRGui assets readme.txt
mv soh-linux.7z ../
'''
}
sh 'sudo docker container stop sohcont'
archiveArtifacts artifacts: 'soh-linux.7z', followSymlinks: false, onlyIfSuccessful: true
}
post {
always {
step([$class: 'WsCleanup']) // Clean workspace
}
}
}
}
}

View file

@ -49,12 +49,12 @@ void OTRExporter_Animation::Save(ZResource* res, const fs::path& outPath, Binary
writer->Write((uint32_t)normalAnim->rotationValues.size());
for (int i = 0; i < normalAnim->rotationValues.size(); i++)
for (size_t i = 0; i < normalAnim->rotationValues.size(); i++)
writer->Write(normalAnim->rotationValues[i]);
writer->Write((uint32_t)normalAnim->rotationIndices.size());
for (int i = 0; i < normalAnim->rotationIndices.size(); i++)
for (size_t i = 0; i < normalAnim->rotationIndices.size(); i++)
{
writer->Write(normalAnim->rotationIndices[i].x);
writer->Write(normalAnim->rotationIndices[i].y);

View file

@ -10,7 +10,7 @@ void OTRExporter_Array::Save(ZResource* res, const fs::path& outPath, BinaryWrit
writer->Write((uint32_t)arr->resList[0]->GetResourceType());
writer->Write((uint32_t)arr->arrayCnt);
for (int i = 0; i < arr->arrayCnt; i++)
for (size_t i = 0; i < arr->arrayCnt; i++)
{
if (arr->resList[i]->GetResourceType() == ZResourceType::Vertex)
{
@ -32,7 +32,7 @@ void OTRExporter_Array::Save(ZResource* res, const fs::path& outPath, BinaryWrit
writer->Write((uint32_t)vec->scalarType);
writer->Write((uint32_t)vec->dimensions);
for (int k = 0; k < vec->dimensions; k++)
for (size_t k = 0; k < vec->dimensions; k++)
{
// OTRTODO: Duplicate code here. Cleanup at a later date...
switch (vec->scalarType)
@ -62,6 +62,8 @@ void OTRExporter_Array::Save(ZResource* res, const fs::path& outPath, BinaryWrit
writer->Write(vec->scalars[k].scalarData.u64);
break;
// OTRTODO: ADD OTHER TYPES
default:
break;
}
}
}
@ -98,6 +100,8 @@ void OTRExporter_Array::Save(ZResource* res, const fs::path& outPath, BinaryWrit
writer->Write(scal->scalarData.u64);
break;
// OTRTODO: ADD OTHER TYPES
default:
break;
}
}
}

View file

@ -29,7 +29,7 @@ void OTRExporter_Cutscene::Save(ZResource* res, const fs::path& outPath, BinaryW
for (auto& e : ((CutsceneCommandSetCameraPos*)cs->commands[i])->entries)
{
writer->Write(CMD_BBH(e->continueFlag, e->cameraRoll, e->nextPointFrame));
writer->Write(CMD_F(e->viewAngle));
writer->Write(e->viewAngle);
writer->Write(CMD_HH(e->posX, e->posY));
writer->Write(CMD_HH(e->posZ, e->unused));
}
@ -46,7 +46,7 @@ void OTRExporter_Cutscene::Save(ZResource* res, const fs::path& outPath, BinaryW
for (auto& e : ((CutsceneCommandSetCameraPos*)cs->commands[i])->entries)
{
writer->Write(CMD_BBH(e->continueFlag, e->cameraRoll, e->nextPointFrame));
writer->Write(CMD_F(e->viewAngle));
writer->Write(e->viewAngle);
writer->Write(CMD_HH(e->posX, e->posY));
writer->Write(CMD_HH(e->posZ, e->unused));
}
@ -105,7 +105,7 @@ void OTRExporter_Cutscene::Save(ZResource* res, const fs::path& outPath, BinaryW
for (auto& e : ((CutsceneCommandSetCameraPos*)cs->commands[i])->entries)
{
writer->Write(CMD_BBH(e->continueFlag, e->cameraRoll, e->nextPointFrame));
writer->Write(CMD_F(e->viewAngle));
writer->Write(e->viewAngle);
writer->Write(CMD_HH(e->posX, e->posY));
writer->Write(CMD_HH(e->posZ, e->unused));
}
@ -122,7 +122,7 @@ void OTRExporter_Cutscene::Save(ZResource* res, const fs::path& outPath, BinaryW
for (auto& e : ((CutsceneCommandSetCameraPos*)cs->commands[i])->entries)
{
writer->Write(CMD_BBH(e->continueFlag, e->cameraRoll, e->nextPointFrame));
writer->Write(CMD_F(e->viewAngle));
writer->Write(e->viewAngle);
writer->Write(CMD_HH(e->posX, e->posY));
writer->Write(CMD_HH(e->posZ, e->unused));
}

View file

@ -29,20 +29,6 @@
Ab1, Ad1)) \
}
typedef int32_t Mtx_t[4][4];
typedef union Mtx
{
//_Alignas(8)
Mtx_t m;
int32_t l[16];
struct
{
int16_t i[16];
uint16_t f[16];
};
} Mtx;
#define gsSPBranchLessZraw2(dl, vtx, zval) \
{ _SHIFTL(G_BRANCH_Z,24,8)|_SHIFTL((vtx)*5,12,12)|_SHIFTL((vtx)*2,0,12),\
(unsigned int)(zval), }
@ -71,7 +57,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
// DEBUG: Write in a marker
Declaration* dbgDecl = dList->parent->GetDeclaration(dList->GetRawDataIndex());
std::string dbgName = StringHelper::Sprintf("%s\\%s", GetParentFolderName(res).c_str(), dbgDecl->varName.c_str());
std::string dbgName = StringHelper::Sprintf("%s/%s", GetParentFolderName(res).c_str(), dbgDecl->varName.c_str());
uint64_t hash = CRC64(dbgName.c_str());
writer->Write((uint32_t)(G_MARKER << 24));
writer->Write((uint32_t)0xBEEFBEEF);
@ -81,7 +67,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
auto dlStart = std::chrono::steady_clock::now();
//for (auto data : dList->instructions)
for (int dataIdx = 0; dataIdx < dList->instructions.size(); dataIdx++)
for (size_t dataIdx = 0; dataIdx < dList->instructions.size(); dataIdx++)
{
auto data = dList->instructions[dataIdx];
uint32_t word0 = 0;
@ -216,7 +202,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
pp ^= G_MTX_PUSH;
mm = (mm & 0x0FFFFFFF) + 0xF0000000;
mm = (mm & 0x0FFFFFFF) + 1;
Gfx value = gsSPMatrix(mm, pp);
word0 = value.words.w0;
@ -243,7 +229,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
if (mtxDecl != nullptr)
{
std::string vName = StringHelper::Sprintf("%s\\%s", (GetParentFolderName(res).c_str()), mtxDecl->varName.c_str());
std::string vName = StringHelper::Sprintf("%s/%s", (GetParentFolderName(res).c_str()), mtxDecl->varName.c_str());
uint64_t hash = CRC64(vName.c_str());
@ -347,7 +333,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
if (dListDecl != nullptr)
{
std::string vName = StringHelper::Sprintf("%s\\%s", (GetParentFolderName(res).c_str()), dListDecl->varName.c_str());
std::string vName = StringHelper::Sprintf("%s/%s", (GetParentFolderName(res).c_str()), dListDecl->varName.c_str());
uint64_t hash = CRC64(vName.c_str());
@ -370,7 +356,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
//std::string fName = StringHelper::Sprintf("%s\\%s", GetParentFolderName(res).c_str(), dListDecl2->varName.c_str());
std::string fName = OTRExporter_DisplayList::GetPathToRes(res, dListDecl2->varName.c_str());
if (files.find(fName) == files.end() && !File::Exists("Extract\\" + fName))
if (files.find(fName) == files.end() && !File::Exists("Extract/" + fName))
{
MemoryStream* dlStream = new MemoryStream();
BinaryWriter dlWriter = BinaryWriter(dlStream);
@ -383,7 +369,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
#endif
if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory)
File::WriteAllBytes("Extract\\" + fName, dlStream->ToVector());
File::WriteAllBytes("Extract/" + fName, dlStream->ToVector());
else
files[fName] = dlStream->ToVector();
@ -411,7 +397,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
Gfx value;
u32 dListVal = (data & 0x0FFFFFFF) + 0xF0000000;
u32 dListVal = (data & 0x0FFFFFFF) + 1;
if (pp != 0)
value = gsSPBranchList(dListVal);
@ -444,7 +430,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
if (dListDecl != nullptr)
{
std::string vName = StringHelper::Sprintf("%s\\%s", (GetParentFolderName(res).c_str()), dListDecl->varName.c_str());
std::string vName = StringHelper::Sprintf("%s/%s", (GetParentFolderName(res).c_str()), dListDecl->varName.c_str());
uint64_t hash = CRC64(vName.c_str());
@ -467,7 +453,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
//std::string fName = StringHelper::Sprintf("%s\\%s", GetParentFolderName(res).c_str(), dListDecl2->varName.c_str());
std::string fName = OTRExporter_DisplayList::GetPathToRes(res, dListDecl2->varName.c_str());
if (files.find(fName) == files.end() && !File::Exists("Extract\\" + fName))
if (files.find(fName) == files.end() && !File::Exists("Extract/" + fName))
{
MemoryStream* dlStream = new MemoryStream();
BinaryWriter dlWriter = BinaryWriter(dlStream);
@ -475,7 +461,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
Save(dList->otherDLists[i], outPath, &dlWriter);
if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory)
File::WriteAllBytes("Extract\\" + fName, dlStream->ToVector());
File::WriteAllBytes("Extract/" + fName, dlStream->ToVector());
else
files[fName] = dlStream->ToVector();
}
@ -533,7 +519,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
int32_t bb = ((data & 0x0000FF0000000000ULL) >> 40) / 2;
int32_t cc = ((data & 0x000000FF00000000ULL) >> 32) / 2;
int32_t dd = ((data & 0x000000000000FFULL)) / 2;
Gfx test = gsSP1Quadrangle(aa, bb, cc, dd, 0);
word0 = test.words.w0;
word1 = test.words.w1;
@ -667,7 +653,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
{
int sss = (data & 0x00FFF00000000000) >> 44;
int ttt = (data & 0x00000FFF00000000) >> 32;
int i = (data & 0x000000000F000000) >> 16;
int i = (data & 0x000000000F000000) >> 24;
int uuu = (data & 0x0000000000FFF000) >> 12;
int vvv= (data & 0x0000000000000FFF);
@ -689,7 +675,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
uint32_t fmt = (__ & 0xE0) >> 5;
uint32_t siz = (__ & 0x18) >> 3;
Gfx value = gsDPSetTextureImage(fmt, siz, www - 1, (seg & 0x0FFFFFFF) + 0xF0000000);
Gfx value = gsDPSetTextureImage(fmt, siz, www + 1, (seg & 0x0FFFFFFF) + 1);
word0 = value.words.w0;
word1 = value.words.w1;
@ -707,7 +693,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
uint32_t fmt = (__ & 0xE0) >> 5;
uint32_t siz = (__ & 0x18) >> 3;
Gfx value = gsDPSetTextureImage(fmt, siz, www - 1, __);
Gfx value = gsDPSetTextureImage(fmt, siz, www + 1, __);
word0 = value.words.w0 & 0x00FFFFFF;
word0 += (G_SETTIMG_OTR << 24);
//word1 = value.words.w1;
@ -721,7 +707,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
ZFile* assocFile = Globals::Instance->GetSegment(GETSEGNUM(seg), res->parent->workerID);
std::string assocFileName = assocFile->GetName();
std::string fName = "";
if (GETSEGNUM(seg) == SEGMENT_SCENE || GETSEGNUM(seg) == SEGMENT_ROOM)
fName = GetPathToRes(res, texName.c_str());
else
@ -753,7 +739,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
Gfx value = gsSPVertex(data & 0xFFFFFFFF, nn, ((aa >> 1) - nn));
word0 = value.words.w0;
word1 = value.words.w1 | 0xF0000000;
word1 = value.words.w1 | 1;
}
else
{
@ -790,7 +776,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
word0 = hash >> 32;
word1 = hash & 0xFFFFFFFF;
if (files.find(fName) == files.end() && !File::Exists("Extract\\" + fName))
if (files.find(fName) == files.end() && !File::Exists("Extract/" + fName))
{
// Write vertices to file
MemoryStream* vtxStream = new MemoryStream();
@ -800,7 +786,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
auto split = StringHelper::Split(vtxDecl->text, "\n");
for (int i = 0; i < split.size(); i++)
for (size_t i = 0; i < split.size(); i++)
{
std::string line = split[i];
@ -842,7 +828,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
}
if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory)
File::WriteAllBytes("Extract\\" + fName, vtxStream->ToVector());
File::WriteAllBytes("Extract/" + fName, vtxStream->ToVector());
else
files[fName] = vtxStream->ToVector();
@ -858,7 +844,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
}
break;
}
writer->Write(word0);
writer->Write(word1);
}
@ -872,7 +858,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina
std::string OTRExporter_DisplayList::GetPathToRes(ZResource* res, std::string varName)
{
std::string prefix = GetPrefix(res);
std::string fName = StringHelper::Sprintf("%s\\%s", GetParentFolderName(res).c_str(), varName.c_str());
std::string fName = StringHelper::Sprintf("%s/%s", GetParentFolderName(res).c_str(), varName.c_str());
return fName;
}
@ -886,7 +872,7 @@ std::string OTRExporter_DisplayList::GetParentFolderName(ZResource* res)
{
auto split = StringHelper::Split(oName, "_");
oName = "";
for (int i = 0; i < split.size() - 1; i++)
for (size_t i = 0; i < split.size() - 1; i++)
oName += split[i] + "_";
oName += "scene";
@ -897,7 +883,7 @@ std::string OTRExporter_DisplayList::GetParentFolderName(ZResource* res)
}
if (prefix != "")
oName = prefix + "\\" + oName;
oName = prefix + "/" + oName;
return oName;
}

View file

@ -52,7 +52,7 @@ static void ExporterParseFileMode(const std::string& buildMode, ZFileMode& fileM
for (auto item : lst)
{
auto fileData = File::ReadAllBytes(item);
otrArchive->AddFile(StringHelper::Split(item, "Extract\\")[1], (uintptr_t)fileData.data(), fileData.size());
otrArchive->AddFile(StringHelper::Split(item, "Extract/")[1], (uintptr_t)fileData.data(), fileData.size());
}
}
}
@ -76,8 +76,12 @@ static void ExporterProgramEnd()
for (auto item : lst)
{
auto fileData = File::ReadAllBytes(item);
otrArchive->AddFile(StringHelper::Split(item, "Extract\\")[1], (uintptr_t)fileData.data(), fileData.size());
otrArchive->AddFile(StringHelper::Split(item, "Extract/")[1], (uintptr_t)fileData.data(), fileData.size());
}
otrArchive->AddFile("Audiobank", (uintptr_t)Globals::Instance->GetBaseromFile("Audiobank").data(), Globals::Instance->GetBaseromFile("Audiobank").size());
otrArchive->AddFile("Audioseq", (uintptr_t)Globals::Instance->GetBaseromFile("Audioseq").data(), Globals::Instance->GetBaseromFile("Audioseq").size());
otrArchive->AddFile("Audiotable", (uintptr_t)Globals::Instance->GetBaseromFile("Audiotable").data(), Globals::Instance->GetBaseromFile("Audiotable").size());
}
}
@ -113,7 +117,7 @@ static void ExporterFileBegin(ZFile* file)
static void ExporterFileEnd(ZFile* file)
{
int bp = 0;
// delete fileWriter;
}
static void ExporterResourceEnd(ZResource* res, BinaryWriter& writer)
@ -136,7 +140,7 @@ static void ExporterResourceEnd(ZResource* res, BinaryWriter& writer)
{
auto split = StringHelper::Split(oName, "_");
oName = "";
for (int i = 0; i < split.size() - 1; i++)
for (size_t i = 0; i < split.size() - 1; i++)
oName += split[i] + "_";
oName += "scene";
@ -149,14 +153,14 @@ static void ExporterResourceEnd(ZResource* res, BinaryWriter& writer)
std::string fName = "";
if (prefix != "")
fName = StringHelper::Sprintf("%s\\%s\\%s", prefix.c_str(), oName.c_str(), rName.c_str());
fName = StringHelper::Sprintf("%s/%s/%s", prefix.c_str(), oName.c_str(), rName.c_str());
else
fName = StringHelper::Sprintf("%s\\%s", oName.c_str(), rName.c_str());
fName = StringHelper::Sprintf("%s/%s", oName.c_str(), rName.c_str());
if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory)
files[fName] = strem->ToVector();
else
File::WriteAllBytes("Extract\\" + fName, strem->ToVector());
File::WriteAllBytes("Extract/" + fName, strem->ToVector());
}
auto end = std::chrono::steady_clock::now();

View file

@ -39,13 +39,13 @@ D_FILES := $(O_FILES:%.o=%.d)
LIB := OTRExporter.a
INC_DIRS := $(addprefix -I, \
../../ZAPD/ZAPD \
../../ZAPD/lib/tinyxml2 \
../../ZAPD/lib/libgfxd \
../../ZAPD/ZAPDUtils \
../../OtrLib/otrlib \
../../OtrLib/otrlib/Lib/spdlog/include \
../../OtrLib/otrlib/Lib/Fast3D/U64 \
../../ZAPDTR/ZAPD \
../../ZAPDTR/lib/tinyxml2 \
../../ZAPDTR/lib/libgfxd \
../../ZAPDTR/ZAPDUtils \
../../libultraship/libultraship \
../../libultraship/libultraship/Lib/spdlog/include \
../../libultraship/libultraship/Lib/Fast3D/U64 \
)
# create build directories

View file

@ -9,11 +9,11 @@ void OTRExporter_Path::Save(ZResource* res, const fs::path& outPath, BinaryWrite
writer->Write((uint32_t)path->pathways.size());
for (int k = 0; k < path->pathways.size(); k++)
for (size_t k = 0; k < path->pathways.size(); k++)
{
writer->Write((uint32_t)path->pathways[k].points.size());
for (int i = 0; i < path->pathways[k].points.size(); i++)
for (size_t i = 0; i < path->pathways[k].points.size(); i++)
{
writer->Write(path->pathways[k].points[i].scalars[0].scalarData.s16);
writer->Write(path->pathways[k].points[i].scalars[1].scalarData.s16);

View file

@ -46,7 +46,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
for (size_t i = 0; i < room->commands.size(); i++)
{
ZRoomCommand* cmd = room->commands[i];
writer->Write((uint32_t)cmd->cmdID);
switch (cmd->cmdID)
@ -172,7 +172,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
writer->Write((uint32_t)cmdCsCam->points.size());
for (int i = 0; i < cmdCsCam->points.size(); i++)
for (size_t i = 0; i < cmdCsCam->points.size(); i++)
{
writer->Write(cmdCsCam->points[i].scalars[0].scalarData.s16);
writer->Write(cmdCsCam->points[i].scalars[1].scalarData.s16);
@ -183,7 +183,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
case RoomCommand::SetMesh:
{
SetMesh* cmdMesh = (SetMesh*)cmd;
writer->Write((uint8_t)cmdMesh->data); // 0x01
writer->Write(cmdMesh->meshHeaderType);
@ -207,12 +207,12 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
Declaration* dListDeclXlu = poly->parent->GetDeclaration(GETSEGOFFSET(test->xlu));
if (test->opa != 0)
writer->Write(StringHelper::Sprintf("%s\\%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclOpa->varName.c_str()));
writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclOpa->varName.c_str()));
else
writer->Write("");
if (test->xlu != 0)
writer->Write(StringHelper::Sprintf("%s\\%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclXlu->varName.c_str()));
writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclXlu->varName.c_str()));
else
writer->Write("");
@ -228,7 +228,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
Declaration* bgDecl = poly->parent->GetDeclarationRanged(GETSEGOFFSET(poly->multiList[i].source));
writer->Write(OTRExporter_DisplayList::GetPathToRes(poly->multiList[i].sourceBackground, bgDecl->varName));
writer->Write(poly->multiList[i].unk_0C);
writer->Write(poly->multiList[i].tlut);
writer->Write(poly->multiList[i].width);
@ -338,7 +338,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
for (size_t i = 0;i < cmdRoom->romfile->numRooms; i++)
{
//std::string roomName = StringHelper::Sprintf("%s\\%s_room_%i", (StringHelper::Split(room->GetName(), "_")[0] + "_scene").c_str(), StringHelper::Split(room->GetName(), "_scene")[0].c_str(), i);
//std::string roomName = StringHelper::Sprintf("%s/%s_room_%i", (StringHelper::Split(room->GetName(), "_")[0] + "_scene").c_str(), StringHelper::Split(room->GetName(), "_scene")[0].c_str(), i);
std::string roomName = OTRExporter_DisplayList::GetPathToRes(room, StringHelper::Sprintf("%s_room_%i", StringHelper::Split(room->GetName(), "_scene")[0].c_str(), i));
writer->Write(roomName);
writer->Write(cmdRoom->romfile->rooms[i].virtualAddressStart);
@ -383,7 +383,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
uint32_t baseStreamEnd = writer->GetStream().get()->GetLength();
writer->Write((uint32_t)cmdStartPos->actors.size()); // 0x01
for (const ActorSpawnEntry& entry : cmdStartPos->actors)
{
writer->Write(entry.actorNum);
@ -441,7 +441,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
case RoomCommand::SetCutscenes:
{
SetCutscenes* cmdSetCutscenes = (SetCutscenes*)cmd;
std::string listName;
Globals::Instance->GetSegmentedPtrName(cmdSetCutscenes->cmdArg2, room->parent, "CutsceneData", listName, res->parent->workerID);
std::string fName = OTRExporter_DisplayList::GetPathToRes(room, listName);
@ -452,9 +452,9 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
BinaryWriter csWriter = BinaryWriter(csStream);
OTRExporter_Cutscene cs;
cs.Save(cmdSetCutscenes->cutscenes[0], "", &csWriter);
if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory)
File::WriteAllBytes("Extract\\" + fName, csStream->ToVector());
File::WriteAllBytes("Extract/" + fName, csStream->ToVector());
else
files[fName] = csStream->ToVector();
@ -468,7 +468,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
writer->Write((uint32_t)cmdSetPathways->pathwayList.pathways.size());
for (int i = 0; i < cmdSetPathways->pathwayList.pathways.size(); i++)
for (size_t i = 0; i < cmdSetPathways->pathwayList.pathways.size(); i++)
{
Declaration* decl = room->parent->GetDeclaration(GETSEGOFFSET(cmdSetPathways->pathwayList.pathways[i].listSegmentAddress));
//std::string path = StringHelper::Sprintf("%s\\%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), decl->varName.c_str());
@ -481,7 +481,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite
pathExp.Save(&cmdSetPathways->pathwayList, outPath, &pathWriter);
if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory)
File::WriteAllBytes("Extract\\" + path, pathStream->ToVector());
File::WriteAllBytes("Extract/" + path, pathStream->ToVector());
else
files[path] = pathStream->ToVector();
@ -514,12 +514,12 @@ void OTRExporter_Room::WritePolyDList(BinaryWriter* writer, ZRoom* room, Polygon
writer->Write(dlist->unk_06);
[[fallthrough]];
default:
//writer->Write(StringHelper::Sprintf("%s\\%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclOpa->varName.c_str()));
//writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclOpa->varName.c_str()));
if (dlist->opaDList != nullptr)
{
auto opaDecl = room->parent->GetDeclaration(GETSEGOFFSET(dlist->opaDList->GetRawDataIndex()));
writer->Write(StringHelper::Sprintf("%s\\%s", OTRExporter_DisplayList::GetParentFolderName(room).c_str(), opaDecl->varName.c_str()));
writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(room).c_str(), opaDecl->varName.c_str()));
}
else
writer->Write("");
@ -527,7 +527,7 @@ void OTRExporter_Room::WritePolyDList(BinaryWriter* writer, ZRoom* room, Polygon
if (dlist->xluDList != nullptr)
{
auto xluDecl = room->parent->GetDeclaration(GETSEGOFFSET(dlist->xluDList->GetRawDataIndex()));
writer->Write(StringHelper::Sprintf("%s\\%s", OTRExporter_DisplayList::GetParentFolderName(room).c_str(), xluDecl->varName.c_str()));
writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(room).c_str(), xluDecl->varName.c_str()));
}
else
writer->Write("");

View file

@ -8,8 +8,8 @@ void OTRExporter_Text::Save(ZResource* res, const fs::path& outPath, BinaryWrite
WriteHeader(txt, outPath, writer, Ship::ResourceType::Text);
writer->Write((uint32_t)txt->messages.size());
for (int i = 0; i < txt->messages.size(); i++)
for (size_t i = 0; i < txt->messages.size(); i++)
{
writer->Write(txt->messages[i].id);
writer->Write(txt->messages[i].textboxType);

View file

@ -5,21 +5,23 @@ std::map<Ship::ResourceType, uint32_t> resourceVersions;
void InitVersionInfo()
{
resourceVersions[Ship::ResourceType::Animation] = 0;
resourceVersions[Ship::ResourceType::Model] = 0;
resourceVersions[Ship::ResourceType::Texture] = 0;
resourceVersions[Ship::ResourceType::Material] = 0;
resourceVersions[Ship::ResourceType::PlayerAnimation] = 0;
resourceVersions[Ship::ResourceType::DisplayList] = 0;
resourceVersions[Ship::ResourceType::Room] = 0;
resourceVersions[Ship::ResourceType::CollisionHeader] = 0;
resourceVersions[Ship::ResourceType::Skeleton] = 0;
resourceVersions[Ship::ResourceType::SkeletonLimb] = 0;
resourceVersions[Ship::ResourceType::Matrix] = 0;
resourceVersions[Ship::ResourceType::Path] = 0;
resourceVersions[Ship::ResourceType::Vertex] = 0;
resourceVersions[Ship::ResourceType::Cutscene] = 0;
resourceVersions[Ship::ResourceType::Array] = 0;
resourceVersions[Ship::ResourceType::Text] = 0;
resourceVersions[Ship::ResourceType::Blob] = 0;
resourceVersions = {
{ Ship::ResourceType::Animation, 0 },
{ Ship::ResourceType::Model, 0 },
{ Ship::ResourceType::Texture, 0 },
{ Ship::ResourceType::Material, 0 },
{ Ship::ResourceType::PlayerAnimation, 0 },
{ Ship::ResourceType::DisplayList, 0 },
{ Ship::ResourceType::Room, 0 },
{ Ship::ResourceType::CollisionHeader, 0 },
{ Ship::ResourceType::Skeleton, 0 },
{ Ship::ResourceType::SkeletonLimb, 0 },
{ Ship::ResourceType::Matrix, 0 },
{ Ship::ResourceType::Path, 0 },
{ Ship::ResourceType::Vertex, 0 },
{ Ship::ResourceType::Cutscene, 0 },
{ Ship::ResourceType::Array, 0 },
{ Ship::ResourceType::Text, 0 },
{ Ship::ResourceType::Blob, 0 },
};
}

View file

@ -1,24 +1,15 @@
#!/usr/bin/env python3
import argparse, json, os, signal, time, sys, shutil
from multiprocessing import Pool, cpu_count, Event, Manager, ProcessError
import os, sys, shutil
import shutil
from rom_info import Z64Rom
import rom_chooser
def SignalHandler(sig, frame):
print(f'Signal {sig} received. Aborting...')
mainAbort.set()
# Don't exit immediately to update the extracted assets file.
def BuildOTR():
shutil.copyfile("baserom/Audiobank", "Extract/Audiobank")
shutil.copyfile("baserom/Audioseq", "Extract/Audioseq")
shutil.copyfile("baserom/Audiotable", "Extract/Audiotable")
def BuildOTR(xmlPath, rom):
shutil.copytree("assets", "Extract/assets")
execStr = "x64\\Release\\ZAPD.exe" if sys.platform == "win32" else "../ZAPD/ZAPD.out"
execStr += " botr -se OTR"
execStr = "x64\\Release\\ZAPD.exe" if sys.platform == "win32" else "../ZAPDTR/ZAPD.out"
execStr += " ed -i %s -b %s -fl CFG/filelists -o placeholder -osf placeholder -gsf 1 -rconf CFG/Config.xml -se OTR" % (xmlPath, rom)
print(execStr)
exitValue = os.system(execStr)
@ -28,98 +19,14 @@ def BuildOTR():
print("Aborting...", file=os.sys.stderr)
print("\n")
def ExtractFile(xmlPath, outputPath, outputSourcePath):
execStr = "x64\\Release\\ZAPD.exe" if sys.platform == "win32" else "../ZAPD/ZAPD.out"
execStr += " e -eh -i %s -b baserom/ -o %s -osf %s -gsf 1 -rconf CFG/Config.xml -se OTR" % (xmlPath, outputPath, outputSourcePath)
if "overlays" in xmlPath:
execStr += " --static"
print(execStr)
exitValue = os.system(execStr)
#exitValue = 0
if exitValue != 0:
print("\n")
print("Error when extracting from file " + xmlPath, file=os.sys.stderr)
print("Aborting...", file=os.sys.stderr)
print("\n")
def ExtractFunc(fullPath):
*pathList, xmlName = fullPath.split(os.sep)
objectName = os.path.splitext(xmlName)[0]
outPath = os.path.join("..\\soh\\assets\\", *pathList[5:], objectName)
os.makedirs(outPath, exist_ok=True)
outSourcePath = outPath
ExtractFile(fullPath, outPath, outSourcePath)
def initializeWorker(abort, test):
global globalAbort
globalAbort = abort
def main():
parser = argparse.ArgumentParser(description="baserom asset extractor")
parser.add_argument("-s", "--single", help="asset path relative to assets/, e.g. objects/gameplay_keep")
parser.add_argument("-f", "--force", help="Force the extraction of every xml instead of checking the touched ones.", action="store_true")
parser.add_argument("-u", "--unaccounted", help="Enables ZAPD unaccounted detector warning system.", action="store_true")
parser.add_argument("-v", "--version", help="Sets game version.")
args = parser.parse_args()
rom_path = rom_chooser.chooseROM()
rom = Z64Rom(rom_path)
global mainAbort
mainAbort = Event()
manager = Manager()
signal.signal(signal.SIGINT, SignalHandler)
extractedAssetsTracker = manager.dict()
xmlVer = "GC_NMQ_D"
if (args.version == "gc_pal_nmpq"):
xmlVer = "GC_NMQ_PAL_F"
elif (args.version == "dbg_mq"):
xmlVer = "GC_MQ_D"
asset_path = args.single
if asset_path is not None:
fullPath = os.path.join("..\\soh\\assets", "xml", asset_path + ".xml")
if not os.path.exists(fullPath):
print(f"Error. File {fullPath} doesn't exists.", file=os.sys.stderr)
exit(1)
ExtractFunc(fullPath)
else:
extract_text_path = "assets/text/message_data.h"
if os.path.isfile(extract_text_path):
extract_text_path = None
extract_staff_text_path = "assets/text/message_data_staff.h"
if os.path.isfile(extract_staff_text_path):
extract_staff_text_path = None
xmlFiles = []
for currentPath, _, files in os.walk(os.path.join("..\\soh\\assets\\xml\\", xmlVer)):
for file in files:
fullPath = os.path.join(currentPath, file)
if file.endswith(".xml"):
xmlFiles.append(fullPath)
try:
numCores = 2
print("Extracting assets with " + str(numCores) + " CPU cores.")
with Pool(numCores, initializer=initializeWorker, initargs=(mainAbort, 0)) as p:
p.map(ExtractFunc, xmlFiles)
except Exception as e:
print("Warning: Multiprocessing exception ocurred.", file=os.sys.stderr)
print("Disabling mutliprocessing.", file=os.sys.stderr)
initializeWorker(mainAbort, 0)
for singlePath in xmlFiles:
ExtractFunc(singlePath)
BuildOTR()
if (os.path.exists("Extract")):
shutil.rmtree("Extract")
BuildOTR("../soh/assets/xml/" + rom.version.xml_ver + "/", rom_path)
if __name__ == "__main__":
main()
main()

View file

@ -0,0 +1,53 @@
#!/usr/bin/python3
import os
import sys
import struct
from multiprocessing import Pool, cpu_count
from rom_info import Z64Rom
import rom_chooser
rom = None
def initialize_worker(input_rom):
global rom
rom = input_rom
def ExtractFunc(i):
dma_file = rom.getDmaEntryByIndex(i)
dma_data = rom.readDmaEntry(dma_file)
filename = '../soh/baserom/' + rom.version.file_table[i]
print('extracting ' + filename + " (0x%08X, 0x%08X)" % (dma_file.virtStart, dma_file.virtEnd))
try:
with open(filename, 'wb') as f:
f.write(dma_data)
except IOError:
print('failed to write file ' + filename)
# TODO: handle this better
if dma_file.compressed:
os.system('tools/yaz0 -d ' + filename + ' ' + filename)
#####################################################################
def main():
try:
os.mkdir('../soh/baserom')
except:
pass
rom_path = rom_chooser.chooseROM()
input_rom = Z64Rom(rom_path)
# extract files
num_cores = cpu_count()
print("Extracting baserom with " + str(num_cores) + " CPU cores.")
with Pool(num_cores, initialize_worker, (input_rom,)) as p:
p.map(ExtractFunc, range(len(input_rom.version.file_table)))
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
import os, sys, glob
from rom_info import Z64Rom
def chooseROM():
roms = []
for file in glob.glob("*.z64"):
if Z64Rom.isValidRom(file):
roms.append(file)
if not (roms):
print("Error: No roms located, place one in the OTRExporter directory", file=os.sys.stderr)
sys.exit(1)
if (len(roms) == 1):
return roms[0]
print(str(len(roms))+ " roms found, please select one by pressing 1-"+str(len(roms)))
for i in range(len(roms)):
print(str(i+1)+ ". " + roms[i])
while(1):
try:
selection = int(input())
except:
print("Bad input. Try again with the number keys.")
continue
if (selection < 1 or selection > len(roms)):
print("Bad input. Try again.")
continue
else: break
return roms[selection - 1]

87
OTRExporter/rom_info.py Normal file
View file

@ -0,0 +1,87 @@
from enum import Enum
from tabnanny import check
import struct
class Checksums(Enum):
OOT_NTSC_10 = "EC7011B7"
OOT_NTSC_11 = "D43DA81F"
OOT_NTSC_12 = "693BA2AE"
OOT_PAL_10 = "B044B569"
OOT_PAL_11 = "B2055FBD"
OOT_NTSC_JP_GC_CE = "F7F52DB8"
OOT_NTSC_JP_GC = "F611F4BA"
OOT_NTSC_US_GC = "F3DD35BA"
OOT_PAL_GC = "09465AC3"
OOT_NTSC_JP_MQ = "F43B45BA"
OOT_NTSC_US_MQ = "F034001A"
OOT_PAL_MQ = "1D4136F3"
OOT_PAL_GC_DBG1 = "871E1C92"
OOT_PAL_GC_DBG2 = "87121EFE"
OOT_PAL_GC_MQ_DBG = "917D18F6"
OOT_IQUE_TW = "3D81FB3E"
OOT_IQUE_CN = "B1E1E07B"
OOT_UNKNOWN = "FFFFFFFF"
@classmethod
def has_value(self, value):
return value in self._value2member_map_
class RomVersion:
def __init__(self, file_table_path, file_table_off, xml_ver):
self.file_table_off = file_table_off
self.xml_ver = xml_ver
with open(file_table_path, 'r') as f:
self.file_table = [line.strip('\n') for line in f]
ROM_INFO_TABLE = dict()
ROM_INFO_TABLE[Checksums.OOT_PAL_GC] = RomVersion("CFG/filelists/gamecube_pal.txt", 0x7170, "GC_NMQ_PAL_F")
ROM_INFO_TABLE[Checksums.OOT_PAL_GC_DBG1] = RomVersion("CFG/filelists/dbg.txt", 0x12F70, "GC_NMQ_D")
class RomDmaEntry:
def __init__(self, rom, i):
off = rom.version.file_table_off + 16 * i
(self.virtStart, \
self.virtEnd, \
self.physStart, \
self.physEnd) = struct.unpack('>IIII', rom.rom_data[off:off+4*4])
self.compressed = self.physEnd != 0
self.size = self.physEnd - self.physStart \
if self.compressed \
else self.virtEnd - self.virtStart
self.name = rom.version.file_table[i]
class Z64Rom:
def __init__(self, file_path):
self.file_path = file_path
with open(file_path, 'rb') as f:
self.rom_data = f.read()
self.is_valid = len(self.rom_data) > 20 * 1024 * 1024
if not self.is_valid:
return
# get checkum
checksum_str = self.rom_data[16:16+4].hex().upper()
self.checksum = Checksums(checksum_str) if Checksums.has_value(checksum_str) else Checksums.OOT_UNKNOWN
if self.checksum == Checksums.OOT_UNKNOWN:
self.is_valid = False
return
# get rom version
self.version = ROM_INFO_TABLE[self.checksum]
def getDmaEntryByIndex(self, i):
return RomDmaEntry(self, i)
def readDmaEntry(self, entry):
return self.rom_data[entry.physStart:entry.physStart + entry.size]
@staticmethod
def isValidRom(rom_path):
return Z64Rom(rom_path).is_valid

View file

@ -67,13 +67,13 @@ void OTRGame::init(){
mat.shader = shader;
}
if(fs::exists("soh.exe") && !fs::exists("oot.otr")) {
if((fs::exists("soh.exe") || fs::exists("soh.elf")) && !fs::exists("oot.otr")) {
hide_second_btn = true;
sohFolder = ".";
}
}
void ExtractRom()
void ExtractRom()
{
WriteResult result;
@ -87,10 +87,11 @@ void ExtractRom()
if (MoonUtils::exists("Extract")) MoonUtils::rm("Extract");
MoonUtils::mkdir("Extract");
MoonUtils::copy("tmp/baserom/Audiobank", "Extract/Audiobank");
MoonUtils::copy("tmp/baserom/Audioseq", "Extract/Audioseq");
MoonUtils::copy("tmp/baserom/Audiotable", "Extract/Audiotable");
MoonUtils::copy("tmp/baserom/version", "Extract/version");
//MoonUtils::copy("tmp/baserom/Audiobank", "Extract/Audiobank");
//MoonUtils::copy("tmp/baserom/Audioseq", "Extract/Audioseq");
//MoonUtils::copy("tmp/baserom/Audiotable", "Extract/Audiotable");
//MoonUtils::copy("tmp/baserom/version", "Extract/version");
MoonUtils::write("Extract/version", (char*)&version.crc, sizeof(version.crc));
MoonUtils::copy("assets/game/", "Extract/assets/");
@ -187,7 +188,7 @@ void OTRGame::draw() {
sohFolder = path;
}
if (UIUtils::GuiIconButton("Cartridge", "Open\nOoT Rom", 32, 50, currentStep != NULLSTR, "Select an Ocarina of Time\nMaster Quest or Vanilla Debug Rom\n\nYou can dump it or lend one from Nintendo")) {
if (UIUtils::GuiIconButton("Cartridge", "Open\nOoT Rom", 32, 50, currentStep != NULLSTR, "Select an Ocarina of Time\nGameCube PAL or Vanilla Debug Rom\n\nYou can dump it or lend one from Nintendo")) {
const std::string path = NativeFS->LaunchFileExplorer(LaunchType::FILE);
if (path != NULLSTR) {
const std::string patched_n64 = std::string(patched_rom);
@ -219,4 +220,4 @@ void setCurrentStep(const std::string& step) {
void OTRGame::exit(){
}
}

View file

@ -83,7 +83,6 @@ void startWorker(RomVersion version) {
Util::write("tmp/baserom/version", (char*)&version.crc, sizeof(version.crc));
if (oldExtractMode)
{
std::vector<std::string> files;

110
README.md
View file

@ -6,15 +6,22 @@ The Ship does not include assets and as such requires a prior copy of the game t
## Quick Start
1) Download The Ship of Harkinian from Discord.
2) Requires an `oot debug` rom (not Master Quest).
1) Download The Ship of Harkinian from [Discord](https://discord.com/invite/BtBmd55HVH).
2) Requires a supported copy of the game (See supported games below).
3) Use the OTRGui to generate an `oot.otr` archive file.
4) Launch `soh.exe`
### Supported Games
Ocarina of Time Debug (not Master Quest)
```
Build team: `zelda@srd022j`
Build date: `03-02-21 00:49:18` (year-month-day)
sha1: cee6bc3c2a634b41728f2af8da54d9bf8cc14099
```
3) Use the OTRGui to generate an `oot.otr` archive file.
4) Launch `soh.exe`
Ocarina of Time PAL GameCube
```
sha1: 0227d7c0074f2d0ac935631990da8ec5914597b4
```
Congratulations, you are now sailing with the Ship of Harkinian! Have fun!
@ -37,7 +44,7 @@ If you still cannot get the tool to work, join our [Discord Server](https://disc
### Running The Ship of Harkinian
Launch the game. If the window immediately closes, or if there are visual artifacts, you may have selected the wrong rom in the OTRGui tool.
Launch the game. If the window immediately closes, or if there are visual artifacts, you may have selected the wrong rom in the OTRGui tool.
Currently, DirectX 11 and OpenGL is supported. Change the renderer by opening the `shipofharkinian.ini` configuration file in notepad and add `sdl` to `gfx backend` for OpenGL or leave blank for DirectX.
@ -51,64 +58,57 @@ Official Discord: https://discord.com/invite/BtBmd55HVH
## Building The Ship of Harkinian
1. Install [Python](https://www.python.org/ftp/python/3.10.2/python-3.10.2-amd64.exe)
2. Install [Visual Studio 2022 Community Edition](https://visualstudio.microsoft.com/vs/community/)
2b. In the Visual Studio Installer, install `MSVC v142 - VS 2019 C++`.
4. Clone the Ship of Harkinian repository.
5. Place `oot debug` rom (not Master Quest) in the `soh` folder named `baserom_original_non_mq`.
6. Launch `soh/fixbaserom.py`.
7. Launch `soh/extract_baserom.py`.
8. Copy the `baserom` folder from the `soh` folder into the `OTRExporter` folder.
9. Run `OTRExporter/OTRExporter.sln`.
10. Switch the solution to `Release x64`.
11. Build the solution.
12. Launching `OTRExporter/extract_assets.py` will generate an `oot.otr` archive file in `OTRExporter/oot.otr`.
13. Run `soh/soh.sln`
14. Switch the solution to `Release x86`.
15. Build the solution.
16. Copy the `OTRExporter/oot.otr` archive file to `soh/Release`.
17. Launch `soh.exe`.
Refer to the [building instructions](BUILDING.md) to compile SoH.
## Troubleshooting The Exporter
- Affirm that you have an `/assets` folder filled with XMLs in the same directory as OTRGui.exe
- Affirm that `zapd.exe` exists in the `/assets/extractor` folder
## Nightly Builds
Nightly builds of Ship of Harkinian are available at [https://builds.shipofharkinian.com/job/SoH_Multibranch/job/develop]
## The Harbour Masters Are...
Kenix | Lead Developer/Public Relations - Resource Management Programmer, Audio System Programmer, and General Programmer
Jack Walker | Lead Developer - OTR Format Programmer, Resource Load Programmer, and General Programmer
Louist103 | Developer - Save System Programmer and General Programmer
Emil | Developer - Fast3D Programmer
m4xw | Developer - Shipwright, Throwing Baguettes, and General Programmer
MelonSpeedruns | Developer - General Programmer
Rozlette | Developer - General Programmer
JoshDuMan | Developer - General Programmer
KiritoDev/Lywx | Developer - General Programmer
Theo3 | Developer - General Programmer
Random06457 | Developer - Linux Build
Kenix | Lead Developer/Public Relations - Resource Management Programmer, Audio System Programmer, and General Programmer
Jack Walker | Lead Developer - OTR Format Programmer, Resource Load Programmer, and General Programmer
Louist103 | Developer - Save System Programmer and General Programmer
Emil | Developer - Fast3D Programmer
m4xw | Developer - Shipwright, Throwing Baguettes, and General Programmer
MelonSpeedruns | Developer - General Programmer
Rozlette | Developer - General Programmer
JoshDuMan | Developer - General Programmer
KiritoDev/Lywx | Developer - General Programmer
Theo3 | Developer - General Programmer
Random06457 | Developer - Linux Build
## Special Thanks
Decomp & ZAPD | Made this project even possible in the first place!
MNGoldenEagle | Patiently explained audio data formats, encouragement, and founding ZSO which was the first source of the game's code and resource format documentation.
Rrrrry123 | Speedbunner, encouragement, and community moderation
Fierce deity | Encouragement and community moderation
mzxrules | For his contributions to decomp
zel. | For his contributions to decomp
Aloxado | Developer - General Programmer
MegaMech | Developer - General Programmer
Revo | Tester - GCC support and General Testing
zfg | Tester - General Testing
Horseless Headman | Tester - General Testing
Steven Pritchett | Tester - General Testing
Trenton May | Tester - General Testing
Zeldaboy14 | Tester - General Testing, encouragement, and community moderation
Koby Howell | Tester - General Testing
Logg | Tester - General Testing
Taylor Daley | Graphic Design
Can't Sleep | Graphic Design
MicTheMicrophone | Voice actor for the King
Amphibibro | Voice actor for Link
Lemons
Decomp & ZAPD | Made this project even possible in the first place!
MNGoldenEagle | Patiently explained audio data formats, encouragement, and founding ZSO which was the first source of the game's code and resource format documentation.
Rrrrry123 | Speedbunner, encouragement, and community moderation
Fierce deity | Encouragement and community moderation
mzxrules | For his contributions to decomp
zel. | For his contributions to decomp
Aloxado | Developer - General Programmer
MegaMech | Developer - General Programmer
Revo | Tester - GCC support and General Testing
zfg | Tester - General Testing
Horseless Headman | Tester - General Testing
Steven Pritchett | Tester - General Testing
Trenton May | Tester - General Testing
Zeldaboy14 | Tester - General Testing, encouragement, and community moderation
Koby Howell | Tester - General Testing
Logg | Tester - General Testing
Taylor Daley | Graphic Design
Can't Sleep | Graphic Design
## Video Credits
Kenix | Producer / Writer
rainbow_fash | Executive Producer
TheLegendOfXela | Editor
MicTheMicrophone | Gwonam / The King
Amphibibro | Link
AceHeart | Zelda
###### Lemons

View file

@ -1,6 +1,6 @@
#include "CollisionExporter.h"
void ExporterExample_Collision::Save(ZResource* res, [[maybe_unused]] fs::path outPath,
void ExporterExample_Collision::Save(ZResource* res, [[maybe_unused]] const fs::path& outPath,
BinaryWriter* writer)
{
ZCollisionHeader* col = (ZCollisionHeader*)res;

View file

@ -6,5 +6,5 @@
class ExporterExample_Collision : public ZResourceExporter
{
public:
void Save(ZResource* res, fs::path outPath, BinaryWriter* writer) override;
void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override;
};

View file

@ -20,7 +20,7 @@
#include "ZRoom/Commands/SetTimeSettings.h"
#include "ZRoom/Commands/SetWind.h"
void ExporterExample_Room::Save(ZResource* res, fs::path outPath, BinaryWriter* writer)
void ExporterExample_Room::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer)
{
ZRoom* room = dynamic_cast<ZRoom*>(res);

View file

@ -6,5 +6,5 @@
class ExporterExample_Room : public ZResourceExporter
{
public:
void Save(ZResource* res, fs::path outPath, BinaryWriter* writer) override;
void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override;
};

View file

@ -1,7 +1,7 @@
#include "TextureExporter.h"
#include "../ZAPD/ZFile.h"
void ExporterExample_Texture::Save(ZResource* res, [[maybe_unused]] fs::path outPath,
void ExporterExample_Texture::Save(ZResource* res, [[maybe_unused]] const fs::path& outPath,
BinaryWriter* writer)
{
ZTexture* tex = (ZTexture*)res;

View file

@ -7,5 +7,5 @@
class ExporterExample_Texture : public ZResourceExporter
{
public:
void Save(ZResource* res, fs::path outPath, BinaryWriter* writer) override;
void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override;
};

View file

@ -44,7 +44,8 @@ ifneq ($(DEPRECATION_ON),0)
endif
# CXXFLAGS += -DTEXTURE_DEBUG
LDFLAGS := -lm -ldl -lpng
LDFLAGS := -lm -ldl -lpng \
-L../external -L../libultraship -lz -lbz2 -pthread -lpulse -lultraship -lstorm -lSDL2 -lGLEW -lGL -lX11
# Use LLD if available. Set LLD=0 to not use it
ifeq ($(shell command -v ld.lld >/dev/null 2>&1; echo $$?),0)
@ -59,9 +60,9 @@ UNAME := $(shell uname)
UNAMEM := $(shell uname -m)
ifneq ($(UNAME), Darwin)
LDFLAGS += -Wl,-export-dynamic -lstdc++fs
EXPORTERS := -Wl,--whole-archive ExporterTest/ExporterTest.a -Wl,--no-whole-archive
EXPORTERS := -Wl,--whole-archive ../OTRExporter/OTRExporter/OTRExporter.a -Wl,--no-whole-archive
else
EXPORTERS := -Wl,-force_load ExporterTest/ExporterTest.a
EXPORTERS := -Wl,-force_load ../OTRExporter/OTRExporter/OTRExporter.a
ifeq ($(UNAMEM),arm64)
ifeq ($(shell brew list libpng > /dev/null 2>&1; echo $$?),0)
LDFLAGS += -L $(shell brew --prefix)/lib

View file

@ -10,7 +10,7 @@
#include "ZFile.h"
#include "ZTexture.h"
#if !defined(_MSC_VER) && !defined(__CYGWIN__)
#ifdef __linux__
#include <csignal>
#include <cstdlib>
#include <ctime>
@ -28,6 +28,31 @@
//extern const char gBuildHash[];
const char gBuildHash[] = "";
// LINUX_TODO: remove, those are because of soh <-> lus dependency problems
float divisor_num = 0.0f;
extern "C" void Audio_SetGameVolume(int player_id, float volume)
{
}
extern "C" int ResourceMgr_OTRSigCheck(char* imgData)
{
}
void DebugConsole_SaveCVars()
{
}
void DebugConsole_LoadCVars()
{
}
bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path& outPath,
ZFileMode fileMode, int workerID);
@ -38,7 +63,7 @@ int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileM
volatile int numWorkersLeft = 0;
#if !defined(_MSC_VER) && !defined(__CYGWIN__)
#ifdef __linux__
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof(arr[0]))
void ErrorHandler(int sig)
{
@ -196,7 +221,7 @@ int main(int argc, char* argv[])
}
else if (arg == "-eh") // Enable Error Handler
{
#if !defined(_MSC_VER) && !defined(__CYGWIN__)
#ifdef __linux__
signal(SIGSEGV, ErrorHandler);
signal(SIGABRT, ErrorHandler);
#else
@ -302,7 +327,7 @@ int main(int argc, char* argv[])
ctpl::thread_pool pool(num_threads / 2);
bool parseSuccessful;
auto start = std::chrono::steady_clock::now();
int fileListSize = fileList.size();
Globals::Instance->singleThreaded = false;
@ -387,18 +412,6 @@ int main(int argc, char* argv[])
{
BuildAssetBlob(Globals::Instance->inputPath, Globals::Instance->outputPath);
}
/*
else if (fileMode == ZFileMode::BuildOverlay)
{
ZOverlay* overlay =
ZOverlay::FromBuild(Path::GetDirectoryName(Globals::Instance->inputPath),
Path::GetDirectoryName(Globals::Instance->cfgPath));
if (overlay != nullptr)
File::WriteAllText(Globals::Instance->outputPath.string(),
overlay->GetSourceOutputCode(""));
}
*/
if (exporterSet != nullptr && exporterSet->endProgramFunc != nullptr)
exporterSet->endProgramFunc();
@ -465,6 +478,7 @@ int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileM
numWorkersLeft--;
}
return 0;
}
bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path& outPath,

View file

@ -96,7 +96,7 @@ int OutputFormatter::Write(const std::string& buf)
return Write(buf.data(), buf.size());
}
__declspec(thread) OutputFormatter* OutputFormatter::Instance;
thread_local OutputFormatter* OutputFormatter::Instance;
int OutputFormatter::WriteStatic(const char* buf, int count)
{

View file

@ -25,7 +25,7 @@ private:
void Flush();
static __declspec(thread) OutputFormatter* Instance;
static thread_local OutputFormatter* Instance;
static int WriteStatic(const char* buf, int count);
public:

View file

@ -15,6 +15,124 @@
#include "WarningHandler.h"
#include "gfxd.h"
#define G_MDSFT_ALPHACOMPARE 0
#define G_MDSFT_ZSRCSEL 2
#define G_MDSFT_RENDERMODE 3
#define G_MDSFT_BLENDER 16
#define G_RM_FOG_SHADE_A 0xC8000000
#define G_RM_FOG_PRIM_A 0xC4000000
#define G_RM_PASS 0x0C080000
#define G_RM_AA_ZB_OPA_SURF 0x442078
#define G_RM_AA_ZB_OPA_SURF2 0x112078
#define G_RM_AA_ZB_XLU_SURF 0x4049D8
#define G_RM_AA_ZB_XLU_SURF2 0x1049D8
#define G_RM_AA_ZB_OPA_DECAL 0x442D58
#define G_RM_AA_ZB_OPA_DECAL2 0x112D58
#define G_RM_AA_ZB_XLU_DECAL 0x404DD8
#define G_RM_AA_ZB_XLU_DECAL2 0x104DD8
#define G_RM_AA_ZB_OPA_INTER 0x442478
#define G_RM_AA_ZB_OPA_INTER2 0x112478
#define G_RM_AA_ZB_XLU_INTER 0x4045D8
#define G_RM_AA_ZB_XLU_INTER2 0x1045D8
#define G_RM_AA_ZB_XLU_LINE 0x407858
#define G_RM_AA_ZB_XLU_LINE2 0x107858
#define G_RM_AA_ZB_DEC_LINE 0x407F58
#define G_RM_AA_ZB_DEC_LINE2 0x107F58
#define G_RM_AA_ZB_TEX_EDGE 0x443078
#define G_RM_AA_ZB_TEX_EDGE2 0x113078
#define G_RM_AA_ZB_TEX_INTER 0x443478
#define G_RM_AA_ZB_TEX_INTER2 0x113478
#define G_RM_AA_ZB_SUB_SURF 0x442878
#define G_RM_AA_ZB_SUB_SURF2 0x112278
#define G_RM_AA_ZB_PCL_SURF 0x40007B
#define G_RM_AA_ZB_PCL_SURF2 0x10007B
#define G_RM_AA_ZB_OPA_TERR 0x402078
#define G_RM_AA_ZB_OPA_TERR2 0x102078
#define G_RM_AA_ZB_TEX_TERR 0x403078
#define G_RM_AA_ZB_TEX_TERR2 0x103078
#define G_RM_AA_ZB_SUB_TERR 0x402278
#define G_RM_AA_ZB_SUB_TERR2 0x102278
#define G_RM_RA_ZB_OPA_SURF 0x442038
#define G_RM_RA_ZB_OPA_SURF2 0x112038
#define G_RM_RA_ZB_OPA_DECAL 0x442D18
#define G_RM_RA_ZB_OPA_DECAL2 0x112D18
#define G_RM_RA_ZB_OPA_INTER 0x442438
#define G_RM_RA_ZB_OPA_INTER2 0x112438
#define G_RM_AA_OPA_SURF 0x442048
#define G_RM_AA_OPA_SURF2 0x112048
#define G_RM_AA_XLU_SURF 0x4041C8
#define G_RM_AA_XLU_SURF2 0x1041C8
#define G_RM_AA_XLU_LINE 0x407048
#define G_RM_AA_XLU_LINE2 0x107048
#define G_RM_AA_DEC_LINE 0x407248
#define G_RM_AA_DEC_LINE2 0x107248
#define G_RM_AA_TEX_EDGE 0x443048
#define G_RM_AA_TEX_EDGE2 0x113048
#define G_RM_AA_SUB_SURF 0x442248
#define G_RM_AA_SUB_SURF2 0x112248
#define G_RM_AA_PCL_SURF 0x40004B
#define G_RM_AA_PCL_SURF2 0x10004B
#define G_RM_AA_OPA_TERR 0x402048
#define G_RM_AA_OPA_TERR2 0x102048
#define G_RM_AA_TEX_TERR 0x403048
#define G_RM_AA_TEX_TERR2 0x103048
#define G_RM_AA_SUB_TERR 0x402248
#define G_RM_AA_SUB_TERR2 0x102248
#define G_RM_RA_OPA_SURF 0x442008
#define G_RM_RA_OPA_SURF2 0x112008
#define G_RM_ZB_OPA_SURF 0x442230
#define G_RM_ZB_OPA_SURF2 0x112230
#define G_RM_ZB_XLU_SURF 0x404A50
#define G_RM_ZB_XLU_SURF2 0x104A50
#define G_RM_ZB_OPA_DECAL 0x442E10
#define G_RM_ZB_OPA_DECAL2 0x112E10
#define G_RM_ZB_XLU_DECAL 0x404E50
#define G_RM_ZB_XLU_DECAL2 0x104E50
#define G_RM_ZB_CLD_SURF 0x404B50
#define G_RM_ZB_CLD_SURF2 0x104B50
#define G_RM_ZB_OVL_SURF 0x404F50
#define G_RM_ZB_OVL_SURF2 0x104F50
#define G_RM_ZB_PCL_SURF 0x0C080233
#define G_RM_ZB_PCL_SURF2 0x03020233
#define G_RM_OPA_SURF 0x0C084000
#define G_RM_OPA_SURF2 0x03024000
#define G_RM_XLU_SURF 0x00404200
#define G_RM_XLU_SURF2 0x00104240
#define G_RM_CLD_SURF 0x00404340
#define G_RM_CLD_SURF2 0x00104340
#define G_RM_TEX_EDGE 0x0C087008
#define G_RM_TEX_EDGE2 0x03027008
#define G_RM_PCL_SURF 0x0C084203
#define G_RM_PCL_SURF2 0x03024203
#define G_RM_ADD 0x04484340
#define G_RM_ADD2 0x01124340
#define G_RM_NOOP 0x00000000
#define G_RM_NOOP2 0x00000000
#define G_RM_VISCVG 0x0C844040
#define G_RM_VISCVG2 0x03214040
#define G_RM_OPA_CI 0x0C080000
#define G_RM_OPA_CI2 0x03020000
#define AA_EN 0x8
#define Z_CMP 0x10
#define Z_UPD 0x20
#define IM_RD 0x40
#define CLR_ON_CVG 0x80
#define CVG_DST_CLAMP 0
#define CVG_DST_WRAP 0x100
#define CVG_DST_FULL 0x200
#define CVG_DST_SAVE 0x300
#define ZMODE_OPA 0
#define ZMODE_INTER 0x400
#define ZMODE_XLU 0x800
#define ZMODE_DEC 0xc00
#define CVG_X_ALPHA 0x1000
#define ALPHA_CVG_SEL 0x2000
#define FORCE_BL 0x4000
#define TEX_EDGE 0x0000
REGISTER_ZFILENODE(DList, ZDisplayList);
ZDisplayList::ZDisplayList(ZFile* nParent) : ZResource(nParent)

View file

@ -166,122 +166,6 @@ enum class OoTSegments
FrameBuffer = 16,
};
#define G_MDSFT_ALPHACOMPARE 0
#define G_MDSFT_ZSRCSEL 2
#define G_MDSFT_RENDERMODE 3
#define G_MDSFT_BLENDER 16
#define G_RM_FOG_SHADE_A 0xC8000000
#define G_RM_FOG_PRIM_A 0xC4000000
#define G_RM_PASS 0x0C080000
#define G_RM_AA_ZB_OPA_SURF 0x442078
#define G_RM_AA_ZB_OPA_SURF2 0x112078
#define G_RM_AA_ZB_XLU_SURF 0x4049D8
#define G_RM_AA_ZB_XLU_SURF2 0x1049D8
#define G_RM_AA_ZB_OPA_DECAL 0x442D58
#define G_RM_AA_ZB_OPA_DECAL2 0x112D58
#define G_RM_AA_ZB_XLU_DECAL 0x404DD8
#define G_RM_AA_ZB_XLU_DECAL2 0x104DD8
#define G_RM_AA_ZB_OPA_INTER 0x442478
#define G_RM_AA_ZB_OPA_INTER2 0x112478
#define G_RM_AA_ZB_XLU_INTER 0x4045D8
#define G_RM_AA_ZB_XLU_INTER2 0x1045D8
#define G_RM_AA_ZB_XLU_LINE 0x407858
#define G_RM_AA_ZB_XLU_LINE2 0x107858
#define G_RM_AA_ZB_DEC_LINE 0x407F58
#define G_RM_AA_ZB_DEC_LINE2 0x107F58
#define G_RM_AA_ZB_TEX_EDGE 0x443078
#define G_RM_AA_ZB_TEX_EDGE2 0x113078
#define G_RM_AA_ZB_TEX_INTER 0x443478
#define G_RM_AA_ZB_TEX_INTER2 0x113478
#define G_RM_AA_ZB_SUB_SURF 0x442878
#define G_RM_AA_ZB_SUB_SURF2 0x112278
#define G_RM_AA_ZB_PCL_SURF 0x40007B
#define G_RM_AA_ZB_PCL_SURF2 0x10007B
#define G_RM_AA_ZB_OPA_TERR 0x402078
#define G_RM_AA_ZB_OPA_TERR2 0x102078
#define G_RM_AA_ZB_TEX_TERR 0x403078
#define G_RM_AA_ZB_TEX_TERR2 0x103078
#define G_RM_AA_ZB_SUB_TERR 0x402278
#define G_RM_AA_ZB_SUB_TERR2 0x102278
#define G_RM_RA_ZB_OPA_SURF 0x442038
#define G_RM_RA_ZB_OPA_SURF2 0x112038
#define G_RM_RA_ZB_OPA_DECAL 0x442D18
#define G_RM_RA_ZB_OPA_DECAL2 0x112D18
#define G_RM_RA_ZB_OPA_INTER 0x442438
#define G_RM_RA_ZB_OPA_INTER2 0x112438
#define G_RM_AA_OPA_SURF 0x442048
#define G_RM_AA_OPA_SURF2 0x112048
#define G_RM_AA_XLU_SURF 0x4041C8
#define G_RM_AA_XLU_SURF2 0x1041C8
#define G_RM_AA_XLU_LINE 0x407048
#define G_RM_AA_XLU_LINE2 0x107048
#define G_RM_AA_DEC_LINE 0x407248
#define G_RM_AA_DEC_LINE2 0x107248
#define G_RM_AA_TEX_EDGE 0x443048
#define G_RM_AA_TEX_EDGE2 0x113048
#define G_RM_AA_SUB_SURF 0x442248
#define G_RM_AA_SUB_SURF2 0x112248
#define G_RM_AA_PCL_SURF 0x40004B
#define G_RM_AA_PCL_SURF2 0x10004B
#define G_RM_AA_OPA_TERR 0x402048
#define G_RM_AA_OPA_TERR2 0x102048
#define G_RM_AA_TEX_TERR 0x403048
#define G_RM_AA_TEX_TERR2 0x103048
#define G_RM_AA_SUB_TERR 0x402248
#define G_RM_AA_SUB_TERR2 0x102248
#define G_RM_RA_OPA_SURF 0x442008
#define G_RM_RA_OPA_SURF2 0x112008
#define G_RM_ZB_OPA_SURF 0x442230
#define G_RM_ZB_OPA_SURF2 0x112230
#define G_RM_ZB_XLU_SURF 0x404A50
#define G_RM_ZB_XLU_SURF2 0x104A50
#define G_RM_ZB_OPA_DECAL 0x442E10
#define G_RM_ZB_OPA_DECAL2 0x112E10
#define G_RM_ZB_XLU_DECAL 0x404E50
#define G_RM_ZB_XLU_DECAL2 0x104E50
#define G_RM_ZB_CLD_SURF 0x404B50
#define G_RM_ZB_CLD_SURF2 0x104B50
#define G_RM_ZB_OVL_SURF 0x404F50
#define G_RM_ZB_OVL_SURF2 0x104F50
#define G_RM_ZB_PCL_SURF 0x0C080233
#define G_RM_ZB_PCL_SURF2 0x03020233
#define G_RM_OPA_SURF 0x0C084000
#define G_RM_OPA_SURF2 0x03024000
#define G_RM_XLU_SURF 0x00404200
#define G_RM_XLU_SURF2 0x00104240
#define G_RM_CLD_SURF 0x00404340
#define G_RM_CLD_SURF2 0x00104340
#define G_RM_TEX_EDGE 0x0C087008
#define G_RM_TEX_EDGE2 0x03027008
#define G_RM_PCL_SURF 0x0C084203
#define G_RM_PCL_SURF2 0x03024203
#define G_RM_ADD 0x04484340
#define G_RM_ADD2 0x01124340
#define G_RM_NOOP 0x00000000
#define G_RM_NOOP2 0x00000000
#define G_RM_VISCVG 0x0C844040
#define G_RM_VISCVG2 0x03214040
#define G_RM_OPA_CI 0x0C080000
#define G_RM_OPA_CI2 0x03020000
#define AA_EN 0x8
#define Z_CMP 0x10
#define Z_UPD 0x20
#define IM_RD 0x40
#define CLR_ON_CVG 0x80
#define CVG_DST_CLAMP 0
#define CVG_DST_WRAP 0x100
#define CVG_DST_FULL 0x200
#define CVG_DST_SAVE 0x300
#define ZMODE_OPA 0
#define ZMODE_INTER 0x400
#define ZMODE_XLU 0x800
#define ZMODE_DEC 0xc00
#define CVG_X_ALPHA 0x1000
#define ALPHA_CVG_SEL 0x2000
#define FORCE_BL 0x4000
#define TEX_EDGE 0x0000
class ZDisplayList : public ZResource
{

View file

@ -823,6 +823,32 @@ void ZFile::GenerateSourceHeaderFiles()
if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory)
File::WriteAllText(headerFilename, formatter.GetOutput());
else if (Globals::Instance->sourceOutputPath != "")
{
std::string xmlPath = xmlFilePath.string();
xmlPath = StringHelper::Replace(xmlPath, "\\", "/");
auto pathList = StringHelper::Split(xmlPath, "/");
std::string outPath = "";
for (int i = 0; i < 3; i++)
outPath += pathList[i] + "/";
for (int i = 5; i < pathList.size(); i++)
{
if (i == pathList.size() - 1)
{
outPath += Path::GetFileNameWithoutExtension(pathList[i]) + "/";
outPath += outName.string() + ".h";
}
else
outPath += pathList[i];
if (i < pathList.size() - 1)
outPath += "/";
}
File::WriteAllText(outPath, formatter.GetOutput());
}
}
std::string ZFile::GetHeaderInclude() const

View file

@ -25,19 +25,19 @@ public:
ZLimbSkinType skinSegmentType = ZLimbSkinType::SkinType_0; // Skin only
segptr_t skinSegment = 0; // Skin only
Struct_800A5E28 segmentStruct; // Skin only
Struct_800A5E28 segmentStruct = {0}; // Skin only
// Legacy only
float legTransX, legTransY, legTransZ; // Vec3f
uint16_t rotX, rotY, rotZ; // Vec3s
segptr_t childPtr; // LegacyLimb*
segptr_t siblingPtr; // LegacyLimb*
float legTransX = 0, legTransY = 0, legTransZ = 0; // Vec3f
uint16_t rotX = 0, rotY = 0, rotZ = 0; // Vec3s
segptr_t childPtr = 0; // LegacyLimb*
segptr_t siblingPtr = 0; // LegacyLimb*
segptr_t dListPtr = 0;
segptr_t dList2Ptr = 0; // LOD and Curve Only
int16_t transX, transY, transZ;
uint8_t childIndex, siblingIndex;
int16_t transX = 0, transY = 0, transZ = 0;
uint8_t childIndex = 0, siblingIndex = 0;
ZLimb(ZFile* nParent);

View file

@ -12,6 +12,7 @@ REGISTER_ZFILENODE(Text, ZText);
ZText::ZText(ZFile* nParent) : ZResource(nParent)
{
RegisterRequiredAttribute("CodeOffset");
RegisterOptionalAttribute("LangOffset", "0");
}
void ZText::ParseRawData()
@ -20,6 +21,16 @@ void ZText::ParseRawData()
const auto& rawData = parent->GetRawData();
uint32_t currentPtr = StringHelper::StrToL(registeredAttributes.at("CodeOffset").value, 16);
uint32_t langPtr = currentPtr;
bool isPalLang = false;
if (StringHelper::StrToL(registeredAttributes.at("LangOffset").value, 16) != 0)
{
langPtr = StringHelper::StrToL(registeredAttributes.at("LangOffset").value, 16);
if (langPtr != currentPtr)
isPalLang = true;
}
std::vector<uint8_t> codeData;
@ -34,8 +45,18 @@ void ZText::ParseRawData()
msgEntry.id = BitConverter::ToInt16BE(codeData, currentPtr + 0);
msgEntry.textboxType = (codeData[currentPtr + 2] & 0xF0) >> 4;
msgEntry.textboxYPos = (codeData[currentPtr + 2] & 0x0F);
msgEntry.segmentId = (codeData[currentPtr + 4]);
msgEntry.msgOffset = BitConverter::ToInt32BE(codeData, currentPtr + 4) & 0x00FFFFFF;
if (isPalLang)
{
msgEntry.segmentId = (codeData[langPtr + 0]);
msgEntry.msgOffset = BitConverter::ToInt32BE(codeData, langPtr + 0) & 0x00FFFFFF;
}
else
{
msgEntry.segmentId = (codeData[langPtr + 4]);
msgEntry.msgOffset = BitConverter::ToInt32BE(codeData, langPtr + 4) & 0x00FFFFFF;
}
uint32_t msgPtr = msgEntry.msgOffset;
unsigned char c = rawData[msgPtr];
@ -82,6 +103,11 @@ void ZText::ParseRawData()
break;
currentPtr += 8;
if (isPalLang)
langPtr += 4;
else
langPtr += 8;
}
int bp2 = 0;

View file

@ -3,6 +3,7 @@
#include <cstdint>
#include <limits>
#include <vector>
#include <cstring>
class BitConverter
{

View file

@ -52,7 +52,7 @@ public:
for (auto& p : fs::recursive_directory_iterator(dir))
{
if (!p.is_directory())
lst.push_back(p.path().string());
lst.push_back(p.path().generic_string());
}
}

View file

@ -3,6 +3,10 @@
#pragma optimize("2", on)
#define _CRT_SECURE_NO_WARNINGS
#ifndef _MSC_VER
#define vsprintf_s vsprintf
#endif
std::vector<std::string> StringHelper::Split(std::string s, const std::string& delimiter)
{
std::vector<std::string> result;
@ -44,7 +48,7 @@ std::string StringHelper::Replace(std::string str, const std::string& from,
while (start_pos != std::string::npos)
{
str.replace(start_pos, from.length(), to);
str.replace(start_pos, from.length(), to);
start_pos = str.find(from);
}

View file

@ -353,4 +353,6 @@ MigrationBackup/
.ionide/
!libultraship/Lib/**
libultraship/DebugObj/*
libultraship/DebugObj/*
build/
libultraship.a

View file

@ -1,15 +1,78 @@
# Only used for standalone compilation, usually inherits these from the main makefile
CXXFLAGS ?= -Wall -Wextra -O2 -g -std=c++17
CXX := g++
CC := gcc
AR := ar
FORMAT := clang-format-11
ASAN ?= 0
DEBUG ?= 1
OPTFLAGS ?= -O0
LTO ?= 0
WARN := -Wall -Wextra -Werror \
-Wno-unused-variable \
-Wno-unused-parameter \
-Wno-unused-function \
-Wno-parentheses \
-Wno-narrowing \
-Wno-missing-field-initializers
CXXFLAGS := $(WARN) -std=c++20 -D_GNU_SOURCE -DENABLE_OPENGL -DSPDLOG_ACTIVE_LEVEL=0 -m32
CFLAGS := $(WARN) -std=c99 -D_GNU_SOURCE -DENABLE_OPENGL -DSPDLOG_ACTIVE_LEVEL=0 -m32
CPPFLAGS := -MMD
ifneq ($(DEBUG),0)
CXXFLAGS += -g -D_DEBUG
CFLAGS += -g -D_DEBUG
endif
ifneq ($(ASAN),0)
CXXFLAGS += -fsanitize=address
CFLAGS += -fsanitize=address
endif
ifneq ($(LTO),0)
CXXFLAGS += -flto
CFLAGS += -flto
endif
SRC_DIRS := $(shell find -type d -not -path "*build*")
CPP_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.cpp))
H_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.h))
O_FILES := $(foreach f,$(CPP_FILES:.cpp=.o),build/$f)
LIB := otrlib.a
CXX_FILES := \
$(shell find libultraship/Factories -name *.cpp) \
$(shell find libultraship/Lib/Fast3D -name *.cpp) \
$(shell find libultraship -maxdepth 1 -name *.cpp) \
$(shell find libultraship/Lib/ImGui -maxdepth 1 -name *.cpp) \
libultraship/Lib/ImGui/backends/imgui_impl_opengl3.cpp \
libultraship/Lib/ImGui/backends/imgui_impl_sdl.cpp \
libultraship/Lib/StrHash64.cpp \
libultraship/Lib/tinyxml2/tinyxml2.cpp
C_FILES := \
libultraship/mixer.c \
libultraship/Lib/stb/stb_impl.c
FMT_FILES := $(shell find libultraship/ -type f \( -name *.cpp -o -name *.h \) -a -not -path "libultraship/Lib/*")
O_FILES := \
$(CXX_FILES:%.cpp=build/%.o) \
$(C_FILES:%.c=build/%.o)
D_FILES := $(O_FILES:%.o=%.d)
LIB := libultraship.a
INC_DIRS := $(addprefix -I, \
../ZAPDTR/ZAPDUtils \
libultraship/Lib/Fast3D/U64 \
libultraship/Lib/spdlog \
libultraship/Lib/spdlog/include \
libultraship \
)
# create build directories
$(shell mkdir -p $(foreach dir,$(SRC_DIRS),build/$(dir)))
$(shell mkdir -p $(SRC_DIRS:%=build/%))
all: $(LIB)
@ -17,12 +80,17 @@ clean:
rm -rf build $(LIB)
format:
clang-format-11 -i $(CPP_FILES) $(H_FILES)
$(FORMAT) -i $(FMT_FILES)
.PHONY: all clean format
build/%.o: %.cpp
$(CXX) $(CXXFLAGS) $(OPTFLAGS) -I ./ -I ../ZAPD/ZAPD -I ../ZAPD/ZAPDUtils -I ../../ZAPD/lib/tinyxml2 -I otrlib/Lib/spdlog/include -c $(OUTPUT_OPTION) $<
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPTFLAGS) $(INC_DIRS) -c $< -o $@
build/%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(OPTFLAGS) $(INC_DIRS) -c $< -o $@
$(LIB): $(O_FILES)
$(AR) rcs $@ $^
-include $(D_FILES)

View file

@ -21,7 +21,7 @@ void Ship::AnimationV0::ParseFileBinary(BinaryReader* reader, Resource* res)
uint32_t rotIndCnt = reader->ReadUInt32();
anim->rotationIndices.reserve(rotIndCnt);
for (int i = 0; i < rotIndCnt; i++)
for (size_t i = 0; i < rotIndCnt; i++)
{
uint16_t x = reader->ReadUInt16();
uint16_t y = reader->ReadUInt16();

View file

@ -7,7 +7,7 @@
#include <filesystem>
namespace Ship {
Archive::Archive(const std::string& MainPath, bool enableWriting) : Archive(MainPath, "", enableWriting)
Archive::Archive(const std::string& MainPath, bool enableWriting) : Archive(MainPath, "", enableWriting)
{
mainMPQ = nullptr;
}
@ -28,7 +28,7 @@ namespace Ship {
std::shared_ptr<Archive> Archive::CreateArchive(const std::string& archivePath, int fileCapacity)
{
Archive* archive = new Archive(archivePath, true);
auto archive = std::make_shared<Archive>(archivePath, true);
TCHAR* t_filename = new TCHAR[archivePath.size() + 1];
t_filename[archivePath.size()] = 0;
@ -37,10 +37,15 @@ namespace Ship {
bool success = SFileCreateArchive(t_filename, MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES | MPQ_CREATE_ARCHIVE_V2, fileCapacity, &archive->mainMPQ);
int error = GetLastError();
if (success) {
delete[] t_filename;
if (success)
{
archive->mpqHandles[archivePath] = archive->mainMPQ;
return std::make_shared<Archive>(*archive);
} else {
return archive;
}
else
{
SPDLOG_ERROR("({}) We tried to create an archive, but it has fallen and cannot get up.");
return nullptr;
}
@ -49,11 +54,16 @@ namespace Ship {
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;
}
if (!SFileOpenFileEx(mainMPQ, filePath.c_str(), 0, &fileHandle)) {
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 nullptr;
return FileToLoad;
}
DWORD dwFileSize = SFileGetFileSize(fileHandle, 0);
@ -67,18 +77,13 @@ namespace Ship {
}
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
FileToLoad->bHasLoadError = true;
return nullptr;
return FileToLoad;
}
if (!SFileCloseFile(fileHandle)) {
SPDLOG_ERROR("({}) Failed to close file {} from mpq archive {}", GetLastError(), filePath.c_str(), MainPath.c_str());
}
if (FileToLoad == nullptr) {
FileToLoad = std::make_shared<File>();
FileToLoad->path = filePath;
}
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
FileToLoad->parent = includeParent ? shared_from_this() : nullptr;
FileToLoad->buffer = fileData;
@ -92,6 +97,11 @@ namespace Ship {
HANDLE fileHandle = NULL;
HANDLE mpqHandle = NULL;
if (FileToLoad == nullptr) {
FileToLoad = std::make_shared<File>();
FileToLoad->path = filePath;
}
for(auto [path, handle] : mpqHandles) {
if (SFileOpenFileEx(mpqHandle, filePath.c_str(), 0, &fileHandle)) {
std::unique_lock Lock(FileToLoad->FileLoadMutex);
@ -116,18 +126,13 @@ namespace Ship {
}
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
FileToLoad->bHasLoadError = true;
return nullptr;
return FileToLoad;
}
if (!SFileCloseFile(fileHandle)) {
SPDLOG_ERROR("({}) Failed to close file {} from mpq archive {}", GetLastError(), filePath.c_str(), MainPath.c_str());
}
if (FileToLoad == nullptr) {
FileToLoad = std::make_shared<File>();
FileToLoad->path = filePath;
}
std::unique_lock<std::mutex> Lock(FileToLoad->FileLoadMutex);
FileToLoad->parent = includeParent ? shared_from_this() : nullptr;
FileToLoad->buffer = fileData;
@ -139,13 +144,16 @@ namespace Ship {
bool Archive::AddFile(const std::string& path, uintptr_t fileData, DWORD dwFileSize) {
HANDLE hFile;
#ifdef _WIN32
SYSTEMTIME sysTime;
GetSystemTime(&sysTime);
FILETIME t;
SystemTimeToFileTime(&sysTime, &t);
ULONGLONG stupidHack = static_cast<uint64_t>(t.dwHighDateTime) << (sizeof(t.dwHighDateTime) * 8) | t.dwLowDateTime;
#else
time_t stupidHack;
time(&stupidHack);
#endif
if (!SFileCreateFile(mainMPQ, path.c_str(), stupidHack, dwFileSize, 0, MPQ_FILE_COMPRESS, &hFile)) {
SPDLOG_ERROR("({}) Failed to create file of {} bytes {} in archive {}", GetLastError(), dwFileSize, path.c_str(), MainPath.c_str());
return false;
@ -181,7 +189,7 @@ namespace Ship {
SPDLOG_ERROR("({}) Failed to remove file {} in archive {}", GetLastError(), path.c_str(), MainPath.c_str());
return false;
}
return true;
}
@ -196,12 +204,12 @@ namespace Ship {
return true;
}
std::vector<SFILE_FIND_DATA> Archive::ListFiles(const std::string& searchMask) {
std::vector<SFILE_FIND_DATA> Archive::ListFiles(const std::string& searchMask) const {
auto fileList = std::vector<SFILE_FIND_DATA>();
SFILE_FIND_DATA findContext;
HANDLE hFind;
hFind = SFileFindFirstFile(mainMPQ, searchMask.c_str(), &findContext, nullptr);
//if (hFind && GetLastError() != ERROR_NO_MORE_FILES) {
if (hFind != nullptr) {
@ -240,12 +248,12 @@ namespace Ship {
return fileList;
}
bool Archive::HasFile(const std::string& filename) {
bool Archive::HasFile(const std::string& filename) const {
bool result = false;
auto start = std::chrono::steady_clock::now();
auto lst = ListFiles(filename);
for (const auto& item : lst) {
if (item.cFileName == filename) {
result = true;
@ -259,15 +267,16 @@ namespace Ship {
return result;
}
std::string Archive::HashToString(uint64_t hash) {
return hashes[hash];
const std::string* Archive::HashToString(uint64_t hash) const {
auto it = hashes.find(hash);
return it != hashes.end() ? &it->second : nullptr;
}
bool Archive::Load(bool enableWriting, bool genCRCMap) {
return LoadMainMPQ(enableWriting, genCRCMap) && LoadPatchMPQs();
}
bool Archive::Unload()
bool Archive::Unload()
{
bool success = true;
for (const auto& mpqHandle : mpqHandles) {
@ -302,11 +311,16 @@ namespace Ship {
bool Archive::LoadMainMPQ(bool enableWriting, bool genCRCMap) {
HANDLE mpqHandle = NULL;
#ifdef _WIN32
std::wstring wfullPath = std::filesystem::absolute(MainPath).wstring();
#endif
std::string fullPath = std::filesystem::absolute(MainPath).string();
std::wstring wFileName = std::filesystem::absolute(MainPath).wstring();
if (!SFileOpenArchive(wFileName.c_str(), 0, enableWriting ? 0 : MPQ_OPEN_READ_ONLY, &mpqHandle)) {
#ifdef _WIN32
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)) {
#endif
SPDLOG_ERROR("({}) Failed to open main mpq file {}.", GetLastError(), fullPath.c_str());
return false;
}
@ -340,12 +354,19 @@ namespace Ship {
std::wstring wPath = std::filesystem::absolute(path).wstring();
#ifdef _WIN32
if (!SFileOpenArchive(wPath.c_str(), 0, MPQ_OPEN_READ_ONLY, &patchHandle)) {
#else
if (!SFileOpenArchive(fullPath.c_str(), 0, MPQ_OPEN_READ_ONLY, &patchHandle)) {
#endif
SPDLOG_ERROR("({}) Failed to open patch mpq file {} while applying to {}.", GetLastError(), path.c_str(), MainPath.c_str());
return false;
}
#ifdef _WIN32
if (!SFileOpenPatchArchive(mainMPQ, wPath.c_str(), "", 0)) {
#else
if (!SFileOpenPatchArchive(mainMPQ, fullPath.c_str(), "", 0)) {
#endif
SPDLOG_ERROR("({}) Failed to apply patch mpq file {} to main mpq {}.", GetLastError(), path.c_str(), MainPath.c_str());
return false;
}

View file

@ -34,9 +34,9 @@ namespace Ship
bool AddFile(const std::string& path, uintptr_t fileData, DWORD dwFileSize);
bool RemoveFile(const std::string& path);
bool RenameFile(const std::string& oldPath, const std::string& newPath);
std::vector<SFILE_FIND_DATA> ListFiles(const std::string& searchMask);
bool HasFile(const std::string& searchMask);
std::string HashToString(uint64_t hash);
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;
protected:
bool Load(bool enableWriting, bool genCRCMap);
bool Unload();

View file

@ -51,6 +51,8 @@ namespace Ship
data.u16 = reader->ReadUInt16();
break;
// OTRTODO: IMPLEMENT OTHER TYPES!
default:
break;
}
arr->scalars.push_back(data);

View file

@ -2,6 +2,7 @@
#include "spdlog/spdlog.h"
#include "GlobalCtx2.h"
#include "Window.h"
#include "GameSettings.h"
namespace Ship {
ConfigFile::ConfigFile(std::shared_ptr<GlobalCtx2> Context, const std::string& Path) : Context(Context), Path(Path), File(Path.c_str()) {
@ -71,7 +72,7 @@ namespace Ship {
(*this)["WINDOW"]["FULLSCREEN WIDTH"] = std::to_string(1920);
(*this)["WINDOW"]["FULLSCREEN HEIGHT"] = std::to_string(1080);
(*this)["WINDOW"]["FULLSCREEN"] = std::to_string(false);
(*this)["WINDOW"]["GFX BACKEND"] = "sdl";
(*this)["WINDOW"]["GFX BACKEND"] = "";
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_CRIGHT)] = std::to_string(0x14D);
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_CLEFT)] = std::to_string(0x14B);
@ -149,6 +150,8 @@ namespace Ship {
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_STICKDOWN)] = std::to_string(0x01F);
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_STICKUP)] = std::to_string(0x011);
(*this)["ENHANCEMENT SETTINGS"]["TEXT_SPEED"] = "1";
(*this)["SDL CONTROLLER 1"]["GUID"] = "";
(*this)["SDL CONTROLLER 2"]["GUID"] = "";
(*this)["SDL CONTROLLER 3"]["GUID"] = "";

View file

@ -1,3 +1,6 @@
#ifndef CONFIG_FILE_H
#define CONFIG_FILE_H
#pragma once
#include <string>
@ -29,9 +32,11 @@ namespace Ship {
bool CreateDefaultConfig();
private:
mINI::INIFile File;
mINI::INIStructure Val;
std::weak_ptr<GlobalCtx2> Context;
std::string Path;
mINI::INIFile File;
};
}
#endif

View file

@ -3,6 +3,7 @@
#include <memory>
#include <map>
#include <string>
#include <optional>
#include "stdint.h"
#include "UltraController.h"
#include "ControllerAttachment.h"
@ -19,12 +20,17 @@ namespace Ship {
void Read(OSContPad* pad);
virtual void ReadFromSource() = 0;
virtual void WriteToSource(ControllerCallback* controller) = 0;
virtual bool Connected() const = 0;
virtual bool CanRumble() const = 0;
bool isRumbling;
void SetButtonMapping(const std::string& szButtonName, int32_t dwScancode);
std::shared_ptr<ControllerAttachment> GetAttachment() { return Attachment; }
int32_t GetControllerNumber() { return dwControllerNumber; }
virtual bool HasPadConf() const = 0;
virtual std::optional<std::string> GetPadConfSection() = 0;
protected:
int32_t dwPressedButtons;
std::map<int32_t, int32_t> ButtonMapping;

View file

@ -1,23 +1,22 @@
#include "cvar.h"
#include "Cvar.h"
#include <map>
#include <string>
#include <functional>
#include <memory>
#include <utility>
#include <PR/ultra64/gbi.h>
std::map<std::string, CVar*> cvars;
std::map<std::string, std::unique_ptr<CVar>, std::less<>> cvars;
CVar* CVar_GetVar(char* name) {
std::string key(name);
return cvars.contains(key) ? cvars[key] : nullptr;
extern "C" CVar* CVar_Get(const char* name) {
auto it = cvars.find(name);
return (it != cvars.end()) ? it->second.get() : nullptr;
}
extern "C" CVar* CVar_Get(char* name) {
return CVar_GetVar(name);
}
extern "C" s32 CVar_GetS32(char* name, s32 defaultValue) {
extern "C" s32 CVar_GetS32(const char* name, s32 defaultValue) {
CVar* cvar = CVar_Get(name);
if (cvar != nullptr) {
if (cvar) {
if (cvar->type == CVAR_TYPE_S32)
return cvar->value.valueS32;
}
@ -25,10 +24,10 @@ extern "C" s32 CVar_GetS32(char* name, s32 defaultValue) {
return defaultValue;
}
extern "C" float CVar_GetFloat(char* name, float defaultValue) {
extern "C" float CVar_GetFloat(const char* name, float defaultValue) {
CVar* cvar = CVar_Get(name);
if (cvar != nullptr) {
if (cvar) {
if (cvar->type == CVAR_TYPE_FLOAT)
return cvar->value.valueFloat;
}
@ -36,10 +35,10 @@ extern "C" float CVar_GetFloat(char* name, float defaultValue) {
return defaultValue;
}
extern "C" char* CVar_GetString(char* name, char* defaultValue) {
extern "C" const char* CVar_GetString(const char* name, const char* defaultValue) {
CVar* cvar = CVar_Get(name);
if (cvar != nullptr) {
if (cvar) {
if (cvar->type == CVAR_TYPE_STRING)
return cvar->value.valueStr;
}
@ -47,54 +46,44 @@ extern "C" char* CVar_GetString(char* name, char* defaultValue) {
return defaultValue;
}
extern "C" void CVar_SetS32(char* name, s32 value) {
CVar* cvar = CVar_Get(name);
extern "C" void CVar_SetS32(const char* name, s32 value) {
auto& cvar = cvars[name];
if (!cvar) {
cvar = new CVar;
cvars[std::string(name)] = cvar;
cvar = std::make_unique<CVar>();
}
cvar->type = CVAR_TYPE_S32;
cvar->value.valueS32 = value;
}
void CVar_SetFloat(char* name, float value) {
CVar* cvar = CVar_Get(name);
void CVar_SetFloat(const char* name, float value) {
auto& cvar = cvars[name];
if (!cvar) {
cvar = new CVar;
cvars[std::string(name)] = cvar;
cvar = std::make_unique<CVar>();
}
cvar->type = CVAR_TYPE_FLOAT;
cvar->value.valueFloat = value;
}
void CVar_SetString(char* name, char* value) {
CVar* cvar = CVar_Get(name);
void CVar_SetString(const char* name, const char* value) {
auto& cvar = cvars[name];
if (!cvar) {
cvar = new CVar;
cvars[std::string(name)] = cvar;
cvar = std::make_unique<CVar>();
}
cvar->type = CVAR_TYPE_STRING;
cvar->value.valueStr = value;
}
extern "C" void CVar_RegisterS32(char* name, s32 defaultValue) {
CVar* cvar = CVar_Get(name);
if (cvar == nullptr)
extern "C" void CVar_RegisterS32(const char* name, s32 defaultValue) {
if (!CVar_Get(name))
CVar_SetS32(name, defaultValue);
}
extern "C" void CVar_RegisterFloat(char* name, float defaultValue) {
CVar* cvar = CVar_Get(name);
if (cvar == nullptr)
extern "C" void CVar_RegisterFloat(const char* name, float defaultValue) {
if (!CVar_Get(name))
CVar_SetFloat(name, defaultValue);
}
extern "C" void CVar_RegisterString(char* name, char* defaultValue) {
CVar* cvar = CVar_Get(name);
if (cvar == nullptr)
extern "C" void CVar_RegisterString(const char* name, const char* defaultValue) {
if (!CVar_Get(name))
CVar_SetString(name, defaultValue);
}
}

View file

@ -6,13 +6,13 @@
typedef enum CVarType { CVAR_TYPE_S32, CVAR_TYPE_FLOAT, CVAR_TYPE_STRING } CVarType;
typedef struct CVar {
char* name;
const char* name;
CVarType type;
union {
s32 valueS32;
float valueFloat;
char* valueStr;
const char* valueStr;
} value;
} CVar;
@ -22,16 +22,15 @@ extern "C"
#endif
//#include <ultra64.h>
CVar* CVar_Get(const char* name);
s32 CVar_GetS32(const char* name, s32 defaultValue);
float CVar_GetFloat(const char* name, float defaultValue);
const char* CVar_GetString(const char* name, const char* defaultValue);
void CVar_SetS32(const char* name, s32 value);
CVar* CVar_Get(char* name);
s32 CVar_GetS32(char* name, s32 defaultValue);
float CVar_GetFloat(char* name, float defaultValue);
char* CVar_GetString(char* name, char* defaultValue);
void CVar_SetS32(char* name, s32 value);
void CVar_RegisterS32(char* name, s32 defaultValue);
void CVar_RegisterFloat(char* name, float defaultValue);
void CVar_RegisterString(char* name, char* defaultValue);
void CVar_RegisterS32(const char* name, s32 defaultValue);
void CVar_RegisterFloat(const char* name, float defaultValue);
void CVar_RegisterString(const char* name, const char* defaultValue);
#ifdef __cplusplus
};
@ -40,10 +39,11 @@ void CVar_RegisterString(char* name, char* defaultValue);
#ifdef __cplusplus
#include <map>
#include <string>
#include <functional>
#include <memory>
extern std::map<std::string, CVar*> cvars;
CVar* CVar_GetVar(char* name);
void CVar_SetFloat(char* name, float value);
void CVar_SetString(char* name, char* value);
extern std::map<std::string, std::unique_ptr<CVar>, std::less<>> cvars;
void CVar_SetFloat(const char* name, float value);
void CVar_SetString(const char* name, const char* value);
#endif
#endif
#endif

View file

@ -1,71 +0,0 @@
#include "OTRResourceLoader.h"
#include "OTRMaterialFactory.h"
#include "OTRSceneFactory.h"
#include "OTRCollisionHeaderFactory.h"
#include "OTRDisplayListFactory.h"
#include "OTRPlayerAnimationFactory.h"
#include "OTRSkeletonFactory.h"
#include "OTRSkeletonLimbFactory.h"
#include "OTRAnimationFactory.h"
#include "OTRVtxFactory.h"
#include "OTRCutsceneFactory.h"
#include "OTRArrayFactory.h"
#include "OTRPathFactory.h"
namespace OtrLib
{
OTRResource* OTRResourceLoader::LoadResource(BinaryReader* reader)
{
Endianess endianess = (Endianess)reader->ReadByte();
// TODO: Setup the binaryreader to use the resource's endianess
ResourceType resourceType = (ResourceType)reader->ReadUInt32();
OTRResource* result = nullptr;
switch (resourceType)
{
case ResourceType::OTRMaterial:
result = OTRMaterialFactory::ReadMaterial(reader);
break;
case ResourceType::OTRRoom:
result = OTRSceneFactory::ReadScene(reader);
break;
case ResourceType::OTRCollisionHeader:
result = OTRCollisionHeaderFactory::ReadCollisionHeader(reader);
break;
case ResourceType::OTRDisplayList:
result = OTRDisplayListFactory::ReadDisplayList(reader);
break;
case ResourceType::OTRPlayerAnimation:
result = OTRPlayerAnimationFactory::ReadPlayerAnimation(reader);
break;
case ResourceType::OTRSkeleton:
result = OTRSkeletonFactory::ReadSkeleton(reader);
break;
case ResourceType::OTRSkeletonLimb:
result = OTRSkeletonLimbFactory::ReadSkeletonLimb(reader);
break;
case ResourceType::OTRVtx:
result = OTRVtxFactory::ReadVtx(reader);
break;
case ResourceType::OTRAnimation:
result = OTRAnimationFactory::ReadAnimation(reader);
break;
case ResourceType::OTRCutscene:
result = OTRCutsceneFactory::ReadCutscene(reader);
break;
case ResourceType::OTRArray:
result = OTRArrayFactory::ReadArray(reader);
break;
case ResourceType::OTRPath:
result = OTRPathFactory::ReadPath(reader);
break;
default:
// RESOURCE TYPE NOT SUPPORTED
break;
}
return result;
}
}

View file

@ -0,0 +1,225 @@
#include "GameOverlay.h"
#include "Cvar.h"
#include "File.h"
#include "Archive.h"
#include "ResourceMgr.h"
#include "SohConsole.h"
#include "SohImGuiImpl.h"
#include "TextureMod.h"
#include "Lib/ImGui/imgui_internal.h"
#include "Utils/StringHelper.h"
void Ship::GameOverlay::LoadFont(const std::string& name, const std::string& path, float fontSize) {
ImGuiIO& io = ImGui::GetIO();
std::shared_ptr<Archive> base = GlobalCtx2::GetInstance()->GetResourceManager()->GetArchive();
std::shared_ptr<File> font = std::make_shared<File>();
base->LoadFile(path, false, font);
if (font->bIsLoaded) {
char* font_data = new char[font->dwBufferSize];
memcpy(font_data, font->buffer.get(), font->dwBufferSize);
Fonts[name] = io.Fonts->AddFontFromMemoryTTF(font_data, font->dwBufferSize, fontSize);
}
}
void Ship::GameOverlay::TextDraw(float x, float y, bool shadow, ImVec4 color, const char* fmt, ...) {
char buf[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);
buf[IM_ARRAYSIZE(buf) - 1] = 0;
va_end(args);
ImGui::PushStyleColor(ImGuiCol_Text, color);
ImGui::PushFont(Fonts[this->CurrentFont]);
if (shadow) {
ImGui::SetCursorPos(ImVec2(x + 1, y + 1));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(.0f, .0f, .0f, color.w));
ImGui::Text(buf, args);
}
ImGui::PopStyleColor();
ImGui::SetCursorPos(ImVec2(x, y));
ImGui::Text(buf, args);
ImGui::PopFont();
ImGui::PopStyleColor();
}
void Ship::GameOverlay::TextDrawNotification(float duration, bool shadow, const char* fmt, ...) {
char buf[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);
buf[IM_ARRAYSIZE(buf) - 1] = 0;
va_end(args);
this->RegisteredOverlays[StringHelper::Sprintf("NotificationID:%d%d", rand(), this->RegisteredOverlays.size())] = new Overlay({ OverlayType::NOTIFICATION, ImStrdup(buf), duration, duration });
NeedsCleanup = true;
}
void Ship::GameOverlay::CleanupNotifications() {
if(!NeedsCleanup) return;
for (auto it = this->RegisteredOverlays.begin(); it != this->RegisteredOverlays.end(); ) {
if (it->second->type == OverlayType::NOTIFICATION && it->second->duration <= 0.0f) {
it = this->RegisteredOverlays.erase(it);
} else {
++it;
}
}
NeedsCleanup = false;
}
float Ship::GameOverlay::GetScreenWidth() {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
return viewport->Size.x;
}
float Ship::GameOverlay::GetScreenHeight() {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
return viewport->Size.y;
}
float Ship::GameOverlay::GetStringWidth(const char* text) {
return CalculateTextSize(text).x;
}
ImVec2 Ship::GameOverlay::CalculateTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) {
ImGuiContext& g = *GImGui;
const char* text_display_end;
if (hide_text_after_double_hash)
text_display_end = ImGui::FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
else
text_display_end = text_end;
GameOverlay* overlay = SohImGui::overlay;
ImFont* font = overlay->CurrentFont == "Default" ? g.Font : overlay->Fonts[overlay->CurrentFont];
const float font_size = font->FontSize;
if (text == text_display_end)
return ImVec2(0.0f, font_size);
ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
// Round
// FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
// FIXME: Investigate using ceilf or e.g.
// - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
// - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
text_size.x = IM_FLOOR(text_size.x + 0.99999f);
return text_size;
}
void Ship::GameOverlay::Init() {
this->LoadFont("Press Start 2P", "assets/ship_of_harkinian/fonts/PressStart2P-Regular.ttf", 12.0f);
this->LoadFont("Fipps", "assets/ship_of_harkinian/fonts/Fipps-Regular.otf", 32.0f);
const std::string DefaultFont = this->Fonts.begin()->first;
if(!this->Fonts.empty()) {
const std::string font = CVar_GetString("gOverlayFont", ImStrdup(DefaultFont.c_str()));
for (auto& [name, _] : this->Fonts) {
if (font.starts_with(name)) {
this->CurrentFont = name;
break;
}
this->CurrentFont = DefaultFont;
}
}
SohImGui::console->Commands["overlay"] = { OverlayCommand, "Draw an overlay using a cvar value" };
}
void Ship::GameOverlay::DrawSettings() {
ImGui::Text("Overlays Text Font");
if (ImGui::BeginCombo("##TextFont", this->CurrentFont.c_str())) {
for (auto& [name, font] : this->Fonts) {
if (ImGui::Selectable(name.c_str(), name == this->CurrentFont)) {
this->CurrentFont = name;
CVar_SetString("gOverlayFont", ImStrdup(name.c_str()));
SohImGui::needs_save = true;
}
}
ImGui::EndCombo();
}
}
void Ship::GameOverlay::Draw() {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos, ImGuiCond_Always);
ImGui::SetNextWindowSize(viewport->Size, ImGuiCond_Always);
ImGui::Begin("SoHOverlay", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoInputs);
this->CleanupNotifications();
float textY = 50;
float notY = 0;
for (auto &[key, overlay] : this->RegisteredOverlays) {
if (overlay->type == OverlayType::TEXT) {
const char* text = ImStrdup(overlay->value);
const CVar* var = CVar_Get(text);
ImVec4 color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
switch (var->type) {
case CVAR_TYPE_FLOAT:
this->TextDraw(30, textY, true, color, "%s %.2f", text, var->value.valueFloat);
break;
case CVAR_TYPE_S32:
this->TextDraw(30, textY, true, color, "%s %d", text, var->value.valueS32);
break;
case CVAR_TYPE_STRING:
this->TextDraw(30, textY, true, color, "%s %s", text, var->value.valueStr);
break;
}
free((void*) text);
textY += 30;
}
if (overlay->type == OverlayType::NOTIFICATION && overlay->duration > 0) {
const char* text = overlay->value;
const float duration = overlay->duration / overlay->fadeTime;
const ImVec4 color = ImVec4(1.0f, 1.0f, 1.0f, duration);
const float textWidth = this->GetStringWidth(overlay->value);
this->TextDraw(GetScreenWidth() - textWidth - 40, GetScreenHeight() - 40 - notY, true, color, text);
notY += 30;
overlay->duration -= .05f;
}
}
ImGui::End();
}
bool Ship::OverlayCommand(const std::vector<std::string>& args) {
if (args.size() < 3) {
return CMD_FAILED;
}
if (CVar_Get(args[2].c_str()) != nullptr) {
const char* key = args[2].c_str();
GameOverlay* overlay = SohImGui::overlay;
if (args[1] == "add") {
if (!overlay->RegisteredOverlays.contains(key)) {
overlay->RegisteredOverlays[key] = new Overlay({ OverlayType::TEXT, ImStrdup(key), -1.0f });
INFO("Added overlay: %s ", key);
} else {
ERROR("Overlay already exists: %s", key);
}
} else if (args[1] == "remove") {
if (overlay->RegisteredOverlays.contains(key)) {
overlay->RegisteredOverlays.erase(key);
INFO("Removed overlay: %s ", key);
} else {
ERROR("Overlay not found: %s ", key);
}
}
} else {
ERROR("CVar %s does not exist", args[2].c_str());
}
return CMD_SUCCESS;
}

View file

@ -0,0 +1,42 @@
#pragma once
#include <string>
#include <vector>
#include "Lib/ImGui/imgui.h"
#include <map>
#include <unordered_map>
enum class OverlayType {
TEXT, IMAGE, NOTIFICATION
};
struct Overlay {
OverlayType type;
const char* value;
float fadeTime;
float duration;
};
namespace Ship {
class GameOverlay {
public:
std::unordered_map<std::string, Overlay*> RegisteredOverlays;
std::unordered_map<std::string, ImFont*> Fonts;
std::string CurrentFont = "Default";
void Init();
void Draw();
void DrawSettings();
static float GetScreenWidth();
static float GetScreenHeight();
static float GetStringWidth(const char* text);
static ImVec2 CalculateTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
void TextDraw(float x, float y, bool shadow, ImVec4 color, const char* text, ...);
void TextDrawNotification(float duration, bool shadow, const char* fmt, ...);
private:
bool NeedsCleanup = false;
void CleanupNotifications();
void LoadFont(const std::string& name, const std::string& path, float fontSize);
};
static bool OverlayCommand(const std::vector<std::string>& args);
}

View file

@ -1,19 +1,22 @@
#include "GameSettings.h"
// Audio
#include <cstddef>
#include <PR/ultra64/types.h>
#include <PR/ultra64/sptask.h>
#include <PR/ultra64/pi.h>
#include <PR/ultra64/message.h>
#include <PR/ultra64/types.h>
#include "ConfigFile.h"
#include "Cvar.h"
#include "GlobalCtx2.h"
#include "SohImGuiImpl.h"
#include "stox.h"
#include "../../soh/include/z64audio.h"
#include <string>
#include "SohHooks.h"
#include "../../soh/soh/Enhancements/debugconsole.h"
#include "Window.h"
#include "Lib/Fast3D/gfx_rendering_api.h"
#define ABS(var) var < 0 ? -(var) : var
@ -22,155 +25,46 @@ using namespace Ship;
namespace Game {
bool DeSyncAudio = false;
SoHConfigType Settings;
const std::string ConfSection = DEBUG_SECTION;
const std::string AudioSection = AUDIO_SECTION;
const std::string ControllerSection = CONTROLLER_SECTION;
const std::string EnhancementSection = ENHANCEMENTS_SECTION;
const std::string CheatSection = CHEATS_SECTION;
void UpdateAudio() {
Audio_SetGameVolume(SEQ_BGM_MAIN, Settings.audio.music_main);
Audio_SetGameVolume(SEQ_BGM_SUB, Settings.audio.music_sub);
Audio_SetGameVolume(SEQ_FANFARE, Settings.audio.fanfare);
Audio_SetGameVolume(SEQ_SFX, Settings.audio.sfx);
Audio_SetGameVolume(SEQ_BGM_MAIN, CVar_GetFloat("gMainMusicVolume", 1));
Audio_SetGameVolume(SEQ_BGM_SUB, CVar_GetFloat("gSubMusicVolume", 1));
Audio_SetGameVolume(SEQ_FANFARE, CVar_GetFloat("gSFXMusicVolume", 1));
Audio_SetGameVolume(SEQ_SFX, CVar_GetFloat("gFanfareVolume", 1));
}
void LoadSettings() {
void LoadPadSettings() {
const std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
ConfigFile& Conf = *pConf;
// Debug
SohImGui::console->opened = stob(Conf[ConfSection]["console"]);
Settings.debug.menu_bar = stob(Conf[ConfSection]["menu_bar"]);
Settings.debug.soh = stob(Conf[ConfSection]["soh_debug"]);
for (const auto& [i, controllers] : Ship::Window::Controllers) {
for (const auto& controller : controllers) {
if (auto padConfSection = controller->GetPadConfSection()) {
}
}
}
}
Settings.debug.n64mode = stob(Conf[ConfSection]["n64_mode"]);
// Enhancements
Settings.enhancements.fast_text = stob(Conf[EnhancementSection]["fast_text"]);
CVar_SetS32(const_cast<char*>("gFastText"), Settings.enhancements.fast_text);
Settings.enhancements.disable_lod = stob(Conf[EnhancementSection]["disable_lod"]);
CVar_SetS32(const_cast<char*>("gDisableLOD"), Settings.enhancements.disable_lod);
Settings.enhancements.animated_pause_menu = stob(Conf[EnhancementSection]["animated_pause_menu"]);
CVar_SetS32(const_cast<char*>("gPauseLiveLink"), Settings.enhancements.animated_pause_menu);
Settings.enhancements.minimal_ui = stob(Conf[EnhancementSection]["minimal_ui"]);
CVar_SetS32(const_cast<char*>("gMinimalUI"), Settings.enhancements.minimal_ui);
// Audio
Settings.audio.master = Ship::stof(Conf[AudioSection]["master"]);
CVar_SetFloat(const_cast<char*>("gGameMasterVolume"), Settings.audio.master);
Settings.audio.music_main = Ship::stof(Conf[AudioSection]["music_main"]);
CVar_SetFloat(const_cast<char*>("gMainMusicVolume"), Settings.audio.music_main);
Settings.audio.music_sub = Ship::stof(Conf[AudioSection]["music_sub"]);
CVar_SetFloat(const_cast<char*>("gSubMusicVolume"), Settings.audio.music_sub);
Settings.audio.sfx = Ship::stof(Conf[AudioSection]["sfx"]);
CVar_SetFloat(const_cast<char*>("gSFXMusicVolume"), Settings.audio.sfx);
Settings.audio.fanfare = Ship::stof(Conf[AudioSection]["fanfare"]);
CVar_SetFloat(const_cast<char*>("gFanfareVolume"), Settings.audio.fanfare);
// Controllers
Settings.controller.gyro_sensitivity = Ship::stof(Conf[ControllerSection]["gyro_sensitivity"]);
CVar_SetFloat(const_cast<char*>("gGyroSensitivity"), Settings.controller.gyro_sensitivity);
Settings.controller.rumble_strength = Ship::stof(Conf[ControllerSection]["rumble_strength"]);
CVar_SetFloat(const_cast<char*>("gRumbleStrength"), Settings.controller.rumble_strength);
Settings.controller.input_scale = Ship::stof(Conf[ControllerSection]["input_scale"]);
CVar_SetFloat(const_cast<char*>("gInputScale"), Settings.controller.input_scale);
Settings.controller.input_enabled = stob(Conf[ControllerSection]["input_enabled"]);
CVar_SetS32(const_cast<char*>("gInputEnabled"), Settings.controller.input_enabled);
// Cheats
Settings.cheats.debug_mode = stob(Conf[CheatSection]["debug_mode"]);
CVar_SetS32(const_cast<char*>("gDebugEnabled"), Settings.cheats.debug_mode);
Settings.cheats.infinite_money = stob(Conf[CheatSection]["infinite_money"]);
CVar_SetS32(const_cast<char*>("gInfiniteMoney"), Settings.cheats.infinite_money);
Settings.cheats.infinite_health = stob(Conf[CheatSection]["infinite_health"]);
CVar_SetS32(const_cast<char*>("gInfiniteHealth"), Settings.cheats.infinite_health);
Settings.cheats.infinite_ammo = stob(Conf[CheatSection]["infinite_ammo"]);
CVar_SetS32(const_cast<char*>("gInfiniteAmmo"), Settings.cheats.infinite_ammo);
Settings.cheats.infinite_magic = stob(Conf[CheatSection]["infinite_magic"]);
CVar_SetS32(const_cast<char*>("gInfiniteMagic"), Settings.cheats.infinite_magic);
Settings.cheats.no_clip = stob(Conf[CheatSection]["no_clip"]);
CVar_SetS32(const_cast<char*>("gNoClip"), Settings.cheats.no_clip);
Settings.cheats.climb_everything = stob(Conf[CheatSection]["climb_everything"]);
CVar_SetS32(const_cast<char*>("gClimbEverything"), Settings.cheats.climb_everything);
Settings.cheats.moon_jump_on_l = stob(Conf[CheatSection]["moon_jump_on_l"]);
CVar_SetS32(const_cast<char*>("gMoonJumpOnL"), Settings.cheats.moon_jump_on_l);
Settings.cheats.super_tunic = stob(Conf[CheatSection]["super_tunic"]);
CVar_SetS32(const_cast<char*>("gSuperTunic"), Settings.cheats.super_tunic);
UpdateAudio();
void LoadSettings() {
DebugConsole_LoadCVars();
}
void SaveSettings() {
const std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
ConfigFile& Conf = *pConf;
// Debug
Conf[ConfSection]["console"] = std::to_string(SohImGui::console->opened);
Conf[ConfSection]["menu_bar"] = std::to_string(Settings.debug.menu_bar);
Conf[ConfSection]["soh_debug"] = std::to_string(Settings.debug.soh);
Conf[ConfSection]["n64_mode"] = std::to_string(Settings.debug.n64mode);
// Audio
Conf[AudioSection]["master"] = std::to_string(Settings.audio.master);
Conf[AudioSection]["music_main"] = std::to_string(Settings.audio.music_main);
Conf[AudioSection]["music_sub"] = std::to_string(Settings.audio.music_sub);
Conf[AudioSection]["sfx"] = std::to_string(Settings.audio.sfx);
Conf[AudioSection]["fanfare"] = std::to_string(Settings.audio.fanfare);
// Enhancements
Conf[EnhancementSection]["fast_text"] = std::to_string(Settings.enhancements.fast_text);
Conf[EnhancementSection]["disable_lod"] = std::to_string(Settings.enhancements.disable_lod);
Conf[EnhancementSection]["animated_pause_menu"] = std::to_string(Settings.enhancements.animated_pause_menu);
Conf[EnhancementSection]["minimal_ui"] = std::to_string(Settings.enhancements.minimal_ui);
// Controllers
Conf[ControllerSection]["gyro_sensitivity"] = std::to_string(Settings.controller.gyro_sensitivity);
Conf[ControllerSection]["rumble_strength"] = std::to_string(Settings.controller.rumble_strength);
Conf[ControllerSection]["input_scale"] = std::to_string(Settings.controller.input_scale);
Conf[ControllerSection]["input_enabled"] = std::to_string(Settings.controller.input_enabled);
// Cheats
Conf[CheatSection]["debug_mode"] = std::to_string(Settings.cheats.debug_mode);
Conf[CheatSection]["infinite_money"] = std::to_string(Settings.cheats.infinite_money);
Conf[CheatSection]["infinite_health"] = std::to_string(Settings.cheats.infinite_health);
Conf[CheatSection]["infinite_ammo"] = std::to_string(Settings.cheats.infinite_ammo);
Conf[CheatSection]["infinite_magic"] = std::to_string(Settings.cheats.infinite_magic);
Conf[CheatSection]["no_clip"] = std::to_string(Settings.cheats.no_clip);
Conf[CheatSection]["climb_everything"] = std::to_string(Settings.cheats.climb_everything);
Conf[CheatSection]["moon_jump_on_l"] = std::to_string(Settings.cheats.moon_jump_on_l);
Conf[CheatSection]["super_tunic"] = std::to_string(Settings.cheats.super_tunic);
Conf.Save();
DebugConsole_SaveCVars();
}
void InitSettings() {
ModInternal::registerHookListener({ AUDIO_INIT, [](HookEvent ev) {
UpdateAudio();
}});
ModInternal::registerHookListener({ GFX_INIT, [](HookEvent ev) {
gfx_get_current_rendering_api()->set_texture_filter((FilteringMode) CVar_GetS32("gTextureFilter", THREE_POINT));
SohImGui::console->opened = CVar_GetS32("gConsoleEnabled", 0);
UpdateAudio();
}});
}
void SetSeqPlayerVolume(SeqPlayers playerId, float volume) {
Audio_SetGameVolume(playerId, volume);
}
}
}

View file

@ -1,55 +1,5 @@
#pragma once
struct SoHConfigType {
// Debug
struct {
bool soh = false;
bool n64mode = false;
bool menu_bar = false;
bool soh_sink = true;
} debug;
// Audio
struct {
float master = 1.0f;
float music_main = 1.0f;
float fanfare = 1.0f;
float sfx = 1.0f;
float music_sub = 1.0f;
} audio;
// Enhancements
struct {
bool fast_text = false;
bool disable_lod = false;
bool animated_pause_menu = false;
bool minimal_ui = false;
} enhancements;
// Controller
struct {
float gyro_sensitivity = 1.0f;
float rumble_strength = 1.0f;
float input_scale = 1.0f;
float gyroDriftX = 0.0f;
float gyroDriftY = 0.0f;
bool input_enabled = false;
} controller;
// Cheats
struct {
bool debug_mode = false;
bool infinite_money = false;
bool infinite_health = false;
bool infinite_ammo = false;
bool infinite_magic = false;
bool no_clip = false;
bool climb_everything = false;
bool moon_jump_on_l = false;
bool super_tunic = false;
} cheats;
};
enum SeqPlayers {
/* 0 */ SEQ_BGM_MAIN,
/* 1 */ SEQ_FANFARE,
@ -58,16 +8,10 @@ enum SeqPlayers {
/* 4 */ SEQ_MAX
};
#define DEBUG_SECTION "DEBUG SETTINGS"
#define AUDIO_SECTION "AUDIO SETTINGS"
#define CONTROLLER_SECTION "CONTROLLER SECTION"
#define ENHANCEMENTS_SECTION "ENHANCEMENT SETTINGS"
#define CHEATS_SECTION "CHEATS SETTINGS"
namespace Game {
extern SoHConfigType Settings;
void InitSettings();
void LoadSettings();
void LoadPadSettings();
void SaveSettings();
void SetSeqPlayerVolume(SeqPlayers playerId, float volume);
}

View file

@ -1,3 +1,6 @@
#ifndef GAME_VERSION_H
#define GAME_VERSION_H
#pragma once
#define OOT_NTSC_10 0xEC7011B7
@ -17,4 +20,6 @@
#define OOT_PAL_GC_MQ_DBG 0x917D18F6
#define OOT_IQUE_TW 0x3D81FB3E
#define OOT_IQUE_CN 0xB1E1E07B
#define OOT_UNKNOWN 0xFFFFFFFF
#define OOT_UNKNOWN 0xFFFFFFFF
#endif

View file

@ -30,7 +30,7 @@ namespace Ship {
}
GlobalCtx2::GlobalCtx2(const std::string& Name) : Name(Name), MainPath(""), PatchesPath("") {
}
GlobalCtx2::~GlobalCtx2() {
@ -54,7 +54,11 @@ namespace Ship {
if (!ResMan->DidLoadSuccessfully())
{
#ifdef _WIN32
MessageBox(NULL, L"Main OTR file not found!", L"Uh oh", MB_OK);
#else
SPDLOG_ERROR("Main OTR file not found!");
#endif
exit(1);
}
INSTANCE = new ModManager(ResMan);

View file

@ -1,3 +1,6 @@
#ifndef GLOBAL_CTX_2
#define GLOBAL_CTX_2
#pragma once
#ifdef __cplusplus
@ -38,4 +41,6 @@ namespace Ship {
std::string PatchesPath;
};
}
#endif
#endif
#endif

View file

@ -53,4 +53,4 @@ namespace Ship {
std::string KeyboardController::GetBindingConfSection() {
return GetControllerType() + " CONTROLLER BINDING " + std::to_string(GetControllerNumber() + 1);
}
}
}

View file

@ -10,11 +10,16 @@ namespace Ship {
void ReadFromSource();
void WriteToSource(ControllerCallback* controller);
bool Connected() const { return true; }
bool CanRumble() const { return false; }
bool PressButton(int32_t dwScancode);
bool ReleaseButton(int32_t dwScancode);
void ReleaseAllButtons();
bool HasPadConf() const { return false; }
std::optional<std::string> GetPadConfSection() { return {}; }
protected:
std::string GetControllerType();
std::string GetConfSection();

View file

@ -170,6 +170,12 @@
#define G_TEXRECT_WIDE 0x37
#define G_FILLWIDERECT 0x38
/* GFX Effects */
// RDP Cmd
#define G_SETGRAYSCALE 0x39
#define G_SETINTENSITY 0x40
/*
* The following commands are the "generated" RDP commands; the user
* never sees them, the RSP microcode generates them.
@ -998,7 +1004,7 @@
#define G_DL_PUSH 0x00
#define G_DL_NOPUSH 0x01
#if _MSC_VER
#if defined(_MSC_VER) || defined(__GNUC__)
#define _LANGUAGE_C
#endif
@ -2821,6 +2827,14 @@ _DW({ \
_g->words.w1 = 0; \
}
#define gsSPGrayscale(pkt, state) \
{ \
Gfx *_g = (Gfx *)(pkt); \
\
_g->words.w0 = _SHIFTL(G_SETGRAYSCALE, 24, 8); \
_g->words.w1 = state; \
}
#ifdef F3DEX_GBI_2
/*
* One gSPGeometryMode(pkt,c,s) GBI is equal to these two GBIs.
@ -3118,7 +3132,7 @@ _DW({ \
#endif
*/
#ifdef _MSC_VER
#if defined(_MSC_VER)
#define CALL_2(A,B) A B
#define CALL_3(A,B,C) A B C
@ -3129,12 +3143,12 @@ _DW({ \
#define gsDPSetCombineMode(a, b) gsDPSetCombineLERP(a, b)
#endif
#if _MSC_VER
#if defined(_MSC_VER) || defined(__GNUC__)
#define CALL_2(A,B) A B
#define CALL_3(A,B,C) A B C
#define gsDPSetCombineMode(a, b) CALL_2(gsDPSetCombineLERP, (a, b))
//#define gsDPSetCombineMode(a, b) _SHIFTL(0, 24, 8), 0
// #define gsDPSetCombineMode(a, b) CALL_2(gsDPSetCombineLERP, (a, b))
// #define gsDPSetCombineMode(a, b) _SHIFTL(0, 24, 8), 0
#else
#define gsDPSetCombineMode(a, b) gsDPSetCombineLERP(a, b)
#endif
@ -3161,6 +3175,8 @@ _DW({ \
(_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | \
_SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)))
#define gsDPSetGrayscaleColor(pkt, r, g, b, lerp) \
DPRGBColor(pkt, G_SETINTENSITY, r, g, b, lerp)
#define gDPSetEnvColor(pkt, r, g, b, a) \
DPRGBColor(pkt, G_SETENVCOLOR, r,g,b,a)
#define gsDPSetEnvColor(r, g, b, a) \
@ -3177,7 +3193,6 @@ _DW({ \
gDPSetColor(pkt, G_SETFILLCOLOR, (d))
#define gsDPSetFillColor(d) \
gsDPSetColor(G_SETFILLCOLOR, (d))
#define gDPSetPrimDepth(pkt, z, dz) \
gDPSetColor(pkt, G_SETPRIMDEPTH, \
_SHIFTL(z, 16, 16) | _SHIFTL(dz, 0, 16))
@ -4560,6 +4575,20 @@ _DW({ \
_g2->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \
}
# define gsSPWideTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \
{{ \
(_SHIFTL(G_TEXRECT_WIDE, 24, 8) | _SHIFTL((xh), 0, 24)), \
_SHIFTL((yh), 0, 24), \
}}, \
{{ \
(_SHIFTL((tile), 24, 3) | _SHIFTL((xl), 0, 24)), \
_SHIFTL((yl), 0, 24), \
}}, \
{{ \
_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), \
_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) \
}}
/* like gSPTextureRectangle but accepts negative position arguments */
#define gSPScisTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \
_DW({ \

View file

@ -33,6 +33,7 @@
#define G_OFF (0)
#include <stdint.h>
#include "types.h"
#include "gbi.h"
#include "abi.h"

View file

@ -16,6 +16,7 @@ void gfx_cc_get_features(uint64_t shader_id0, uint32_t shader_id1, struct CCFeat
cc_features->opt_2cyc = (shader_id1 & SHADER_OPT_2CYC) != 0;
cc_features->opt_alpha_threshold = (shader_id1 & SHADER_OPT_ALPHA_THRESHOLD) != 0;
cc_features->opt_invisible = (shader_id1 & SHADER_OPT_INVISIBLE) != 0;
cc_features->opt_grayscale = (shader_id1 & SHADER_OPT_GRAYSCALE) != 0;
cc_features->clamp[0][0] = (shader_id1 & SHADER_OPT_TEXEL0_CLAMP_S);
cc_features->clamp[0][1] = (shader_id1 & SHADER_OPT_TEXEL0_CLAMP_T);

View file

@ -39,10 +39,11 @@ enum {
#define SHADER_OPT_2CYC (1 << 4)
#define SHADER_OPT_ALPHA_THRESHOLD (1 << 5)
#define SHADER_OPT_INVISIBLE (1 << 6)
#define SHADER_OPT_TEXEL0_CLAMP_S (1 << 7)
#define SHADER_OPT_TEXEL0_CLAMP_T (1 << 8)
#define SHADER_OPT_TEXEL1_CLAMP_S (1 << 9)
#define SHADER_OPT_TEXEL1_CLAMP_T (1 << 10)
#define SHADER_OPT_GRAYSCALE (1 << 7)
#define SHADER_OPT_TEXEL0_CLAMP_S (1 << 8)
#define SHADER_OPT_TEXEL0_CLAMP_T (1 << 9)
#define SHADER_OPT_TEXEL1_CLAMP_S (1 << 10)
#define SHADER_OPT_TEXEL1_CLAMP_T (1 << 11)
#define CC_SHADER_OPT_POS 56
struct CCFeatures {
@ -54,6 +55,7 @@ struct CCFeatures {
bool opt_2cyc;
bool opt_alpha_threshold;
bool opt_invisible;
bool opt_grayscale;
bool used_textures[2];
bool clamp[2][2];
int num_inputs;

View file

@ -15,11 +15,9 @@
#ifndef _LANGUAGE_C
#define _LANGUAGE_C
#endif
#include <PR/ultra64/gbi.h>
#include "PR/ultra64/gbi.h"
#include "gfx_cc.h"
#include "gfx_window_manager_api.h"
#include "gfx_rendering_api.h"
#include "gfx_direct3d_common.h"
#define DECLARE_GFX_DXGI_FUNCTIONS
@ -28,7 +26,9 @@
#include "gfx_screen_config.h"
#include "../../SohImGuiImpl.h"
#define THREE_POINT_FILTERING 0
#include "gfx_cc.h"
#include "gfx_rendering_api.h"
#include "gfx_pc.h"
#define DEBUG_D3D 0
using namespace Microsoft::WRL; // For ComPtr
@ -37,9 +37,8 @@ namespace {
struct PerFrameCB {
uint32_t noise_frame;
float noise_scale_x;
float noise_scale_y;
uint32_t padding;
float noise_scale;
uint32_t padding[2]; // constant buffers must be multiples of 16 bytes in size
};
struct PerDrawCB {
@ -51,12 +50,12 @@ struct PerDrawCB {
} textures[2];
};
struct CoordCB {
float x, y;
float padding[2]; // structure size must be multiple of 16
struct Coord {
int x, y;
};
struct TextureData {
ComPtr<ID3D11Texture2D> texture;
ComPtr<ID3D11ShaderResourceView> resource_view;
ComPtr<ID3D11SamplerState> sampler_state;
uint32_t width;
@ -64,10 +63,13 @@ struct TextureData {
bool linear_filtering;
};
struct FramebufferData {
struct Framebuffer {
ComPtr<ID3D11RenderTargetView> render_target_view;
ComPtr<ID3D11DepthStencilView> depth_stencil_view;
ComPtr<ID3D11ShaderResourceView> depth_stencil_srv;
uint32_t texture_id;
bool has_depth_buffer;
uint32_t msaa_level;
};
struct ShaderProgramD3D11 {
@ -86,39 +88,35 @@ struct ShaderProgramD3D11 {
static struct {
HMODULE d3d11_module;
PFN_D3D11_CREATE_DEVICE D3D11CreateDevice;
HMODULE d3dcompiler_module;
pD3DCompile D3DCompile;
D3D_FEATURE_LEVEL feature_level;
uint32_t msaa_num_quality_levels[D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT];
ComPtr<ID3D11Device> device;
ComPtr<IDXGISwapChain1> swap_chain;
ComPtr<ID3D11DeviceContext> context;
ComPtr<ID3D11RenderTargetView> backbuffer_view;
ComPtr<ID3D11DepthStencilView> depth_stencil_view;
ComPtr<ID3D11ShaderResourceView> depth_stencil_srv;
ComPtr<ID3D11RasterizerState> rasterizer_state;
ComPtr<ID3D11DepthStencilState> depth_stencil_state;
ComPtr<ID3D11Buffer> vertex_buffer;
ComPtr<ID3D11Buffer> per_frame_cb;
ComPtr<ID3D11Buffer> per_draw_cb;
ComPtr<ID3D11Texture2D> depth_stencil_texture;
ComPtr<ID3D11Texture2D> depth_stencil_copy_texture;
ComPtr<ID3D11Buffer> coord_buffer;
ComPtr<ID3D11ShaderResourceView> coord_buffer_srv;
ComPtr<ID3D11Buffer> depth_value_output_buffer;
ComPtr<ID3D11Buffer> depth_value_output_buffer_copy;
ComPtr<ID3D11UnorderedAccessView> depth_value_output_uav;
ComPtr<ID3D11SamplerState> depth_value_sampler;
ComPtr<ID3D11ComputeShader> compute_shader;
bool copied_depth_buffer;
ComPtr<ID3D11ComputeShader> compute_shader_msaa;
ComPtr<ID3DBlob> compute_shader_msaa_blob;
size_t coord_buffer_size;
#if DEBUG_D3D
ComPtr<ID3D11Debug> debug;
#endif
DXGI_SAMPLE_DESC sample_description;
PerFrameCB per_frame_cb_data;
PerDrawCB per_draw_cb_data;
@ -128,14 +126,16 @@ static struct {
int current_tile;
uint32_t current_texture_ids[2];
std::vector<FramebufferData> framebuffers;
std::vector<Framebuffer> framebuffers;
// Current state
struct ShaderProgramD3D11 *shader_program;
uint32_t current_width, current_height;
//uint32_t current_width, current_height;
uint32_t render_target_height;
int current_framebuffer;
FilteringMode current_filter_mode = NONE;
int8_t depth_test;
int8_t depth_mask;
@ -156,7 +156,9 @@ static struct {
static LARGE_INTEGER last_time, accumulated_time, frequency;
void create_depth_stencil_objects(uint32_t width, uint32_t height, ID3D11Texture2D **texture, ID3D11DepthStencilView **view, ID3D11ShaderResourceView **srv) {
int gfx_d3d11_create_framebuffer(void);
static void create_depth_stencil_objects(uint32_t width, uint32_t height, uint32_t msaa_count, ID3D11DepthStencilView **view, ID3D11ShaderResourceView **srv) {
D3D11_TEXTURE2D_DESC texture_desc;
texture_desc.Width = width;
texture_desc.Height = height;
@ -164,93 +166,42 @@ void create_depth_stencil_objects(uint32_t width, uint32_t height, ID3D11Texture
texture_desc.ArraySize = 1;
texture_desc.Format = d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ?
DXGI_FORMAT_R32_TYPELESS : DXGI_FORMAT_R24G8_TYPELESS;
texture_desc.SampleDesc.Count = 1;
texture_desc.SampleDesc.Count = msaa_count;
texture_desc.SampleDesc.Quality = 0;
texture_desc.Usage = D3D11_USAGE_DEFAULT;
texture_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | (srv != nullptr ? D3D11_BIND_SHADER_RESOURCE : 0);
texture_desc.CPUAccessFlags = 0;
texture_desc.MiscFlags = 0;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, texture));
ComPtr<ID3D11Texture2D> texture;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, texture.GetAddressOf()));
D3D11_DEPTH_STENCIL_VIEW_DESC view_desc;
view_desc.Format = d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ?
DXGI_FORMAT_D32_FLOAT : DXGI_FORMAT_D24_UNORM_S8_UINT;
view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
view_desc.Flags = 0;
view_desc.Texture2D.MipSlice = 0;
if (msaa_count > 1) {
view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
view_desc.Texture2DMS.UnusedField_NothingToDefine = 0;
} else {
view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
view_desc.Texture2D.MipSlice = 0;
}
ThrowIfFailed(d3d.device->CreateDepthStencilView(*texture, &view_desc, view));
ThrowIfFailed(d3d.device->CreateDepthStencilView(texture.Get(), &view_desc, view));
if (srv != nullptr) {
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
srv_desc.Format = d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ?
DXGI_FORMAT_R32_FLOAT : DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.ViewDimension = msaa_count > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MostDetailedMip = 0;
srv_desc.Texture2D.MipLevels = -1;
ThrowIfFailed(d3d.device->CreateShaderResourceView(*texture, &srv_desc, srv));
ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), &srv_desc, srv));
}
}
static void create_render_target_views(bool is_resize) {
DXGI_SWAP_CHAIN_DESC1 desc1;
if (is_resize) {
// Release previous stuff (if any)
d3d.backbuffer_view.Reset();
d3d.depth_stencil_texture.Reset();
d3d.depth_stencil_view.Reset();
d3d.depth_stencil_srv.Reset();
d3d.depth_stencil_copy_texture.Reset();
// Resize swap chain buffers
ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1));
ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags),
gfx_dxgi_get_h_wnd(), "Failed to resize IDXGISwapChain buffers.");
}
// Get new size
ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1));
// Create back buffer
ComPtr<ID3D11Texture2D> backbuffer_texture;
ThrowIfFailed(d3d.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *) backbuffer_texture.GetAddressOf()),
gfx_dxgi_get_h_wnd(), "Failed to get backbuffer from IDXGISwapChain.");
ThrowIfFailed(d3d.device->CreateRenderTargetView(backbuffer_texture.Get(), nullptr, d3d.backbuffer_view.GetAddressOf()),
gfx_dxgi_get_h_wnd(), "Failed to create render target view.");
// Create depth buffer
create_depth_stencil_objects(desc1.Width, desc1.Height, d3d.depth_stencil_texture.GetAddressOf(), d3d.depth_stencil_view.GetAddressOf(), d3d.depth_stencil_srv.GetAddressOf());
// Create texture that can be used to retrieve depth value
D3D11_TEXTURE2D_DESC depth_texture = {};
depth_texture.Width = desc1.Width;
depth_texture.Height = desc1.Height;
depth_texture.MipLevels = 1;
depth_texture.ArraySize = 1;
depth_texture.Format = DXGI_FORMAT_D32_FLOAT;
depth_texture.SampleDesc.Count = 1;
depth_texture.SampleDesc.Quality = 0;
depth_texture.Usage = D3D11_USAGE_STAGING;
depth_texture.BindFlags = 0;
depth_texture.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
depth_texture.MiscFlags = 0;
ThrowIfFailed(d3d.device->CreateTexture2D(&depth_texture, nullptr, d3d.depth_stencil_copy_texture.GetAddressOf()));
// Save resolution
d3d.current_width = desc1.Width;
d3d.current_height = desc1.Height;
}
static void gfx_d3d11_init(void) {
// Load d3d11.dll
d3d.d3d11_module = LoadLibraryW(L"d3d11.dll");
@ -300,11 +251,6 @@ static void gfx_d3d11_init(void) {
}
});
// Sample description to be used in back buffer and depth buffer
d3d.sample_description.Count = 1;
d3d.sample_description.Quality = 0;
// Create the swap chain
d3d.swap_chain = gfx_dxgi_create_swap_chain(d3d.device.Get());
@ -315,9 +261,19 @@ static void gfx_d3d11_init(void) {
gfx_dxgi_get_h_wnd(), "Failed to get ID3D11Debug device.");
#endif
// Create views
// Create the default framebuffer which represents the window
Framebuffer& fb = d3d.framebuffers[gfx_d3d11_create_framebuffer()];
create_render_target_views(false);
// Check the size of the window
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc;
ThrowIfFailed(d3d.swap_chain->GetDesc1(&swap_chain_desc));
d3d.textures[fb.texture_id].width = swap_chain_desc.Width;
d3d.textures[fb.texture_id].height = swap_chain_desc.Height;
fb.msaa_level = 1;
for (uint32_t sample_count = 1; sample_count <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; sample_count++) {
ThrowIfFailed(d3d.device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, sample_count, &d3d.msaa_num_quality_levels[sample_count - 1]));
}
// Create main vertex buffer
@ -364,61 +320,30 @@ static void gfx_d3d11_init(void) {
// Create compute shader that can be used to retrieve depth buffer values
D3D11_BUFFER_DESC coord_cb_desc;
coord_cb_desc.Usage = D3D11_USAGE_DYNAMIC;
coord_cb_desc.ByteWidth = sizeof(CoordCB);
coord_cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
coord_cb_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
coord_cb_desc.MiscFlags = 0;
coord_cb_desc.StructureByteStride = 0;
ThrowIfFailed(d3d.device->CreateBuffer(&coord_cb_desc, nullptr, d3d.coord_buffer.GetAddressOf()));
D3D11_SAMPLER_DESC sampler_desc = {};
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
sampler_desc.MinLOD = 0;
sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
ThrowIfFailed(d3d.device->CreateSamplerState(&sampler_desc, d3d.depth_value_sampler.GetAddressOf()));
D3D11_BUFFER_DESC output_buffer_desc;
output_buffer_desc.Usage = D3D11_USAGE_DEFAULT;
output_buffer_desc.ByteWidth = sizeof(float);
output_buffer_desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
output_buffer_desc.CPUAccessFlags = 0;
output_buffer_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
output_buffer_desc.StructureByteStride = sizeof(float);
ThrowIfFailed(d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer.GetAddressOf()));
D3D11_UNORDERED_ACCESS_VIEW_DESC output_buffer_uav_desc;
output_buffer_uav_desc.Format = DXGI_FORMAT_UNKNOWN;
output_buffer_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
output_buffer_uav_desc.Buffer.FirstElement = 0;
output_buffer_uav_desc.Buffer.NumElements = 1;
output_buffer_uav_desc.Buffer.Flags = 0;
ThrowIfFailed(d3d.device->CreateUnorderedAccessView(d3d.depth_value_output_buffer.Get(), &output_buffer_uav_desc, d3d.depth_value_output_uav.GetAddressOf()));
output_buffer_desc.Usage = D3D11_USAGE_STAGING;
output_buffer_desc.BindFlags = 0;
output_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
ThrowIfFailed(d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer_copy.GetAddressOf()));
const char* shader_source = R"(
sampler my_sampler : register(s0);
Texture2D<float> tex : register(t0);
cbuffer coordCB : register(b0) {
float2 coord;
}
StructuredBuffer<int2> coord : register(t1);
RWStructuredBuffer<float> output : register(u0);
[numthreads(1, 1, 1)]
void CSMain(uint3 DTid : SV_DispatchThreadID) {
output[0] = tex.SampleLevel(my_sampler, coord, 0);
output[DTid.x] = tex.Load(int3(coord[DTid.x], 0));
}
)";
const char* shader_source_msaa = R"(
sampler my_sampler : register(s0);
Texture2DMS<float, 2> tex : register(t0);
StructuredBuffer<int2> coord : register(t1);
RWStructuredBuffer<float> output : register(u0);
[numthreads(1, 1, 1)]
void CSMain(uint3 DTid : SV_DispatchThreadID) {
output[DTid.x] = tex.Load(coord[DTid.x], 0);
}
)";
#if DEBUG_D3D
UINT compile_flags = D3DCOMPILE_DEBUG;
#else
@ -426,7 +351,9 @@ void CSMain(uint3 DTid : SV_DispatchThreadID) {
#endif
ComPtr<ID3DBlob> cs, error_blob;
HRESULT hr = d3d.D3DCompile(shader_source, strlen(shader_source), nullptr, nullptr, nullptr, "CSMain", "cs_4_0", compile_flags, 0, cs.GetAddressOf(), error_blob.GetAddressOf());
HRESULT hr;
hr = d3d.D3DCompile(shader_source, strlen(shader_source), nullptr, nullptr, nullptr, "CSMain", "cs_4_0", compile_flags, 0, cs.GetAddressOf(), error_blob.GetAddressOf());
if (FAILED(hr)) {
char* err = (char*)error_blob->GetBufferPointer();
@ -436,6 +363,14 @@ void CSMain(uint3 DTid : SV_DispatchThreadID) {
ThrowIfFailed(d3d.device->CreateComputeShader(cs->GetBufferPointer(), cs->GetBufferSize(), nullptr, d3d.compute_shader.GetAddressOf()));
hr = d3d.D3DCompile(shader_source_msaa, strlen(shader_source_msaa), nullptr, nullptr, nullptr, "CSMain", "cs_4_1", compile_flags, 0, d3d.compute_shader_msaa_blob.GetAddressOf(), error_blob.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
char* err = (char*)error_blob->GetBufferPointer();
MessageBoxA(gfx_dxgi_get_h_wnd(), err, "Error", MB_OK | MB_ICONERROR);
throw hr;
}
// Create ImGui
SohImGui::WindowImpl window_impl;
@ -445,8 +380,8 @@ void CSMain(uint3 DTid : SV_DispatchThreadID) {
}
static bool gfx_d3d11_z_is_from_0_to_1(void) {
return true;
static struct GfxClipParameters gfx_d3d11_get_clip_parameters(void) {
return { true, false };
}
static void gfx_d3d11_unload_shader(struct ShaderProgram *old_prg) {
@ -463,7 +398,7 @@ static struct ShaderProgram *gfx_d3d11_create_and_load_new_shader(uint64_t shade
char buf[4096];
size_t len, num_floats;
gfx_direct3d_common_build_shader(buf, len, num_floats, cc_features, false, THREE_POINT_FILTERING);
gfx_direct3d_common_build_shader(buf, len, num_floats, cc_features, false, d3d.current_filter_mode == THREE_POINT);
ComPtr<ID3DBlob> vs, ps;
ComPtr<ID3DBlob> error_blob;
@ -514,6 +449,9 @@ static struct ShaderProgram *gfx_d3d11_create_and_load_new_shader(uint64_t shade
if (cc_features.opt_fog) {
ied[ied_index++] = { "FOG", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 };
}
if (cc_features.opt_grayscale) {
ied[ied_index++] = { "GRAYSCALE", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 };
}
for (unsigned int i = 0; i < cc_features.num_inputs; i++) {
DXGI_FORMAT format = cc_features.opt_alpha ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R32G32B32_FLOAT;
ied[ied_index++] = { "INPUT", i, format, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 };
@ -531,8 +469,8 @@ static struct ShaderProgram *gfx_d3d11_create_and_load_new_shader(uint64_t shade
blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; // We initially clear alpha to 1.0f and want to keep it at 1.0f
blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
} else {
@ -592,6 +530,10 @@ static D3D11_TEXTURE_ADDRESS_MODE gfx_cm_to_d3d11(uint32_t val) {
static void gfx_d3d11_upload_texture(const uint8_t *rgba32_buf, uint32_t width, uint32_t height) {
// Create texture
TextureData *texture_data = &d3d.textures[d3d.current_texture_ids[d3d.current_tile]];
texture_data->width = width;
texture_data->height = height;
D3D11_TEXTURE2D_DESC texture_desc;
ZeroMemory(&texture_desc, sizeof(D3D11_TEXTURE2D_DESC));
@ -612,32 +554,19 @@ static void gfx_d3d11_upload_texture(const uint8_t *rgba32_buf, uint32_t width,
resource_data.SysMemPitch = width * 4;
resource_data.SysMemSlicePitch = resource_data.SysMemPitch * height;
ComPtr<ID3D11Texture2D> texture;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, &resource_data, texture.GetAddressOf()));
TextureData *texture_data = &d3d.textures[d3d.current_texture_ids[d3d.current_tile]];
texture_data->width = width;
texture_data->height = height;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, &resource_data, texture_data->texture.ReleaseAndGetAddressOf()));
// Create shader resource view from texture
if (texture_data->resource_view.Get() != nullptr) {
// Free the previous texture in this slot
texture_data->resource_view.Reset();
}
ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), nullptr, texture_data->resource_view.GetAddressOf()));
ThrowIfFailed(d3d.device->CreateShaderResourceView(texture_data->texture.Get(), nullptr, texture_data->resource_view.ReleaseAndGetAddressOf()));
}
static void gfx_d3d11_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) {
D3D11_SAMPLER_DESC sampler_desc;
ZeroMemory(&sampler_desc, sizeof(D3D11_SAMPLER_DESC));
#if THREE_POINT_FILTERING
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
#else
sampler_desc.Filter = linear_filter ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT;
#endif
sampler_desc.Filter = linear_filter && d3d.current_filter_mode == LINEAR ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT;
sampler_desc.AddressU = gfx_cm_to_d3d11(cms);
sampler_desc.AddressV = gfx_cm_to_d3d11(cmt);
sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
@ -741,12 +670,12 @@ static void gfx_d3d11_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t
d3d.last_resource_views[i] = d3d.textures[d3d.current_texture_ids[i]].resource_view.Get();
d3d.context->PSSetShaderResources(i, 1, d3d.textures[d3d.current_texture_ids[i]].resource_view.GetAddressOf());
#if THREE_POINT_FILTERING
d3d.per_draw_cb_data.textures[i].width = d3d.textures[d3d.current_texture_ids[i]].width;
d3d.per_draw_cb_data.textures[i].height = d3d.textures[d3d.current_texture_ids[i]].height;
d3d.per_draw_cb_data.textures[i].linear_filtering = d3d.textures[d3d.current_texture_ids[i]].linear_filtering;
textures_changed = true;
#endif
if (d3d.current_filter_mode == THREE_POINT) {
d3d.per_draw_cb_data.textures[i].width = d3d.textures[d3d.current_texture_ids[i]].width;
d3d.per_draw_cb_data.textures[i].height = d3d.textures[d3d.current_texture_ids[i]].height;
d3d.per_draw_cb_data.textures[i].linear_filtering = d3d.textures[d3d.current_texture_ids[i]].linear_filtering;
textures_changed = true;
}
if (d3d.last_sampler_states[i].Get() != d3d.textures[d3d.current_texture_ids[i]].sampler_state.Get()) {
d3d.last_sampler_states[i] = d3d.textures[d3d.current_texture_ids[i]].sampler_state.Get();
@ -803,20 +732,10 @@ static void gfx_d3d11_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t
}
static void gfx_d3d11_on_resize(void) {
create_render_target_views(true);
//create_render_target_views(true);
}
static void gfx_d3d11_start_frame(void) {
// Set render targets
d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), d3d.depth_stencil_view.Get());
// Clear render targets
const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
d3d.context->ClearRenderTargetView(d3d.backbuffer_view.Get(), clearColor);
d3d.context->ClearDepthStencilView(d3d.depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
// Set per-frame constant buffer
d3d.per_frame_cb_data.noise_frame++;
@ -824,22 +743,9 @@ static void gfx_d3d11_start_frame(void) {
// No high values, as noise starts to look ugly
d3d.per_frame_cb_data.noise_frame = 0;
}
float aspect_ratio = (float) d3d.current_width / (float) d3d.current_height;
d3d.render_target_height = d3d.current_height;
d3d.per_frame_cb_data.noise_scale_x = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2
d3d.per_frame_cb_data.noise_scale_y = 120;
D3D11_MAPPED_SUBRESOURCE ms;
ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE));
d3d.context->Map(d3d.per_frame_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
memcpy(ms.pData, &d3d.per_frame_cb_data, sizeof(PerFrameCB));
d3d.context->Unmap(d3d.per_frame_cb.Get(), 0);
d3d.copied_depth_buffer = false;
}
static void gfx_d3d11_end_frame(void) {
SohImGui::Draw();
d3d.context->Flush();
}
@ -847,43 +753,13 @@ static void gfx_d3d11_finish_render(void) {
d3d.context->Flush();
}
void gfx_d3d11_resize_framebuffer(int fb, uint32_t width, uint32_t height) {
FramebufferData& fd = d3d.framebuffers[fb];
TextureData& td = d3d.textures[fd.texture_id];
ComPtr<ID3D11Texture2D> texture, depth_stencil_texture;
D3D11_TEXTURE2D_DESC texture_desc;
texture_desc.Width = width;
texture_desc.Height = height;
texture_desc.Usage = D3D11_USAGE_DEFAULT;
texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texture_desc.CPUAccessFlags = 0;
texture_desc.MiscFlags = 0;
texture_desc.ArraySize = 1;
texture_desc.MipLevels = 1;
texture_desc.SampleDesc.Count = 1;
texture_desc.SampleDesc.Quality = 0;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, texture.GetAddressOf()));
create_depth_stencil_objects(width, height, depth_stencil_texture.GetAddressOf(), fd.depth_stencil_view.ReleaseAndGetAddressOf(), nullptr);
ThrowIfFailed(d3d.device->CreateRenderTargetView(texture.Get(), nullptr, fd.render_target_view.ReleaseAndGetAddressOf()));
ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), nullptr, td.resource_view.ReleaseAndGetAddressOf()));
td.width = width;
td.height = height;
}
int gfx_d3d11_create_framebuffer(uint32_t width, uint32_t height) {
int gfx_d3d11_create_framebuffer(void) {
uint32_t texture_id = gfx_d3d11_new_texture();
TextureData& t = d3d.textures[texture_id];
t.width = width;
t.height = height;
size_t index = d3d.framebuffers.size();
d3d.framebuffers.resize(d3d.framebuffers.size() + 1);
FramebufferData& data = d3d.framebuffers.back();
Framebuffer& data = d3d.framebuffers.back();
data.texture_id = texture_id;
uint32_t tile = 0;
@ -892,24 +768,109 @@ int gfx_d3d11_create_framebuffer(uint32_t width, uint32_t height) {
gfx_d3d11_set_sampler_parameters(0, true, G_TX_WRAP, G_TX_WRAP);
d3d.current_texture_ids[tile] = saved;
gfx_d3d11_resize_framebuffer(index, width, height);
return (int)index;
}
void gfx_d3d11_set_framebuffer(int fb) {
d3d.render_target_height = d3d.textures[d3d.framebuffers[fb].texture_id].height;
static void gfx_d3d11_update_framebuffer_parameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth) {
Framebuffer& fb = d3d.framebuffers[fb_id];
TextureData& tex = d3d.textures[fb.texture_id];
d3d.context->OMSetRenderTargets(1, d3d.framebuffers[fb].render_target_view.GetAddressOf(), d3d.framebuffers[fb].depth_stencil_view.Get());
width = max(width, 1U);
height = max(height, 1U);
while (msaa_level > 1 && d3d.msaa_num_quality_levels[msaa_level - 1] == 0) {
--msaa_level;
}
const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
d3d.context->ClearRenderTargetView(d3d.framebuffers[fb].render_target_view.Get(), clearColor);
d3d.context->ClearDepthStencilView(d3d.framebuffers[fb].depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
bool diff = tex.width != width || tex.height != height || fb.msaa_level != msaa_level;
if (diff || (fb.render_target_view.Get() != nullptr) != render_target) {
if (fb_id != 0) {
D3D11_TEXTURE2D_DESC texture_desc;
texture_desc.Width = width;
texture_desc.Height = height;
texture_desc.Usage = D3D11_USAGE_DEFAULT;
texture_desc.BindFlags = (msaa_level <= 1 ? D3D11_BIND_SHADER_RESOURCE : 0) | (render_target ? D3D11_BIND_RENDER_TARGET : 0);
texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texture_desc.CPUAccessFlags = 0;
texture_desc.MiscFlags = 0;
texture_desc.ArraySize = 1;
texture_desc.MipLevels = 1;
texture_desc.SampleDesc.Count = msaa_level;
texture_desc.SampleDesc.Quality = 0;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, tex.texture.ReleaseAndGetAddressOf()));
if (msaa_level <= 1) {
ThrowIfFailed(d3d.device->CreateShaderResourceView(tex.texture.Get(), nullptr, tex.resource_view.ReleaseAndGetAddressOf()));
}
} else if (diff) {
DXGI_SWAP_CHAIN_DESC1 desc1;
ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1));
if (desc1.Width != width || desc1.Height != height) {
fb.render_target_view.Reset();
tex.texture.Reset();
ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags));
}
ThrowIfFailed(d3d.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)tex.texture.ReleaseAndGetAddressOf()));
}
if (render_target) {
ThrowIfFailed(d3d.device->CreateRenderTargetView(tex.texture.Get(), nullptr, fb.render_target_view.ReleaseAndGetAddressOf()));
}
tex.width = width;
tex.height = height;
}
if (has_depth_buffer && (diff || !fb.has_depth_buffer || (fb.depth_stencil_srv.Get() != nullptr) != can_extract_depth)) {
fb.depth_stencil_srv.Reset();
create_depth_stencil_objects(width, height, msaa_level, fb.depth_stencil_view.ReleaseAndGetAddressOf(), can_extract_depth ? fb.depth_stencil_srv.GetAddressOf() : nullptr);
}
if (!has_depth_buffer) {
fb.depth_stencil_view.Reset();
fb.depth_stencil_srv.Reset();
}
fb.has_depth_buffer = has_depth_buffer;
fb.msaa_level = msaa_level;
}
void gfx_d3d11_reset_framebuffer(void) {
d3d.render_target_height = d3d.current_height;
d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), d3d.depth_stencil_view.Get());
void gfx_d3d11_start_draw_to_framebuffer(int fb_id, float noise_scale) {
Framebuffer& fb = d3d.framebuffers[fb_id];
d3d.render_target_height = d3d.textures[fb.texture_id].height;
d3d.context->OMSetRenderTargets(1, fb.render_target_view.GetAddressOf(), fb.has_depth_buffer ? fb.depth_stencil_view.Get() : nullptr);
d3d.current_framebuffer = fb_id;
if (noise_scale != 0.0f) {
d3d.per_frame_cb_data.noise_scale = 1.0f / noise_scale;
}
D3D11_MAPPED_SUBRESOURCE ms;
ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE));
d3d.context->Map(d3d.per_frame_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
memcpy(ms.pData, &d3d.per_frame_cb_data, sizeof(PerFrameCB));
d3d.context->Unmap(d3d.per_frame_cb.Get(), 0);
}
void gfx_d3d11_clear_framebuffer(void) {
Framebuffer& fb = d3d.framebuffers[d3d.current_framebuffer];
const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
d3d.context->ClearRenderTargetView(fb.render_target_view.Get(), clearColor);
if (fb.has_depth_buffer) {
d3d.context->ClearDepthStencilView(fb.depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
}
}
void gfx_d3d11_resolve_msaa_color_buffer(int fb_id_target, int fb_id_source) {
Framebuffer& fb_dst = d3d.framebuffers[fb_id_target];
Framebuffer& fb_src = d3d.framebuffers[fb_id_source];
d3d.context->ResolveSubresource(d3d.textures[fb_dst.texture_id].texture.Get(), 0, d3d.textures[fb_src.texture_id].texture.Get(), 0, DXGI_FORMAT_R8G8B8A8_UNORM);
}
void *gfx_d3d11_get_framebuffer_texture_id(int fb_id) {
return (void *)d3d.textures[d3d.framebuffers[fb_id].texture_id].resource_view.Get();
}
void gfx_d3d11_select_texture_fb(int fbID) {
@ -917,72 +878,126 @@ void gfx_d3d11_select_texture_fb(int fbID) {
gfx_d3d11_select_texture(tile, d3d.framebuffers[fbID].texture_id);
}
uint16_t gfx_d3d11_get_pixel_depth(float x, float y) {
void gfx_d3d11_set_texture_filter(FilteringMode mode) {
d3d.current_filter_mode = mode;
gfx_texture_cache_clear();
}
FilteringMode gfx_d3d11_get_texture_filter(void) {
return d3d.current_filter_mode;
}
std::map<std::pair<float, float>, uint16_t> gfx_d3d11_get_pixel_depth(int fb_id, const std::set<std::pair<float, float>>& coordinates) {
Framebuffer& fb = d3d.framebuffers[fb_id];
TextureData& td = d3d.textures[fb.texture_id];
if (coordinates.size() > d3d.coord_buffer_size) {
d3d.coord_buffer.Reset();
d3d.coord_buffer_srv.Reset();
d3d.depth_value_output_buffer.Reset();
d3d.depth_value_output_uav.Reset();
d3d.depth_value_output_buffer_copy.Reset();
D3D11_BUFFER_DESC coord_buf_desc;
coord_buf_desc.Usage = D3D11_USAGE_DYNAMIC;
coord_buf_desc.ByteWidth = sizeof(Coord) * coordinates.size();
coord_buf_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
coord_buf_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
coord_buf_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
coord_buf_desc.StructureByteStride = sizeof(Coord);
ThrowIfFailed(d3d.device->CreateBuffer(&coord_buf_desc, nullptr, d3d.coord_buffer.GetAddressOf()));
D3D11_SHADER_RESOURCE_VIEW_DESC coord_buf_srv_desc;
coord_buf_srv_desc.Format = DXGI_FORMAT_UNKNOWN;
coord_buf_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
coord_buf_srv_desc.Buffer.FirstElement = 0;
coord_buf_srv_desc.Buffer.NumElements = coordinates.size();
ThrowIfFailed(d3d.device->CreateShaderResourceView(d3d.coord_buffer.Get(), &coord_buf_srv_desc, d3d.coord_buffer_srv.GetAddressOf()));
D3D11_BUFFER_DESC output_buffer_desc;
output_buffer_desc.Usage = D3D11_USAGE_DEFAULT;
output_buffer_desc.ByteWidth = sizeof(float) * coordinates.size();
output_buffer_desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
output_buffer_desc.CPUAccessFlags = 0;
output_buffer_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
output_buffer_desc.StructureByteStride = sizeof(float);
ThrowIfFailed(d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer.GetAddressOf()));
D3D11_UNORDERED_ACCESS_VIEW_DESC output_buffer_uav_desc;
output_buffer_uav_desc.Format = DXGI_FORMAT_UNKNOWN;
output_buffer_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
output_buffer_uav_desc.Buffer.FirstElement = 0;
output_buffer_uav_desc.Buffer.NumElements = coordinates.size();
output_buffer_uav_desc.Buffer.Flags = 0;
ThrowIfFailed(d3d.device->CreateUnorderedAccessView(d3d.depth_value_output_buffer.Get(), &output_buffer_uav_desc, d3d.depth_value_output_uav.GetAddressOf()));
output_buffer_desc.Usage = D3D11_USAGE_STAGING;
output_buffer_desc.BindFlags = 0;
output_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
ThrowIfFailed(d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer_copy.GetAddressOf()));
d3d.coord_buffer_size = coordinates.size();
}
D3D11_MAPPED_SUBRESOURCE ms;
if (fb.msaa_level > 1 && d3d.compute_shader_msaa.Get() == nullptr) {
ThrowIfFailed(d3d.device->CreateComputeShader(d3d.compute_shader_msaa_blob->GetBufferPointer(), d3d.compute_shader_msaa_blob->GetBufferSize(), nullptr, d3d.compute_shader_msaa.GetAddressOf()));
}
// ImGui overwrites these values, so we cannot set them once at init
d3d.context->CSSetShader(d3d.compute_shader.Get(), nullptr, 0);
d3d.context->CSSetConstantBuffers(0, 1, d3d.coord_buffer.GetAddressOf());
d3d.context->CSSetSamplers(0, 1, d3d.depth_value_sampler.GetAddressOf());
d3d.context->CSSetShader(fb.msaa_level > 1 ? d3d.compute_shader_msaa.Get() : d3d.compute_shader.Get(), nullptr, 0);
d3d.context->CSSetUnorderedAccessViews(0, 1, d3d.depth_value_output_uav.GetAddressOf(), nullptr);
ThrowIfFailed(d3d.context->Map(d3d.coord_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms));
CoordCB* coord_cb = (CoordCB*)ms.pData;
coord_cb->x = x / d3d.current_width;
// We invert y because the game assumes OpenGL coordinates (bottom-left corner is origin), while DX's origin is top-left corner
coord_cb->y = 1 - y / d3d.current_height;
Coord *coord_cb = (Coord *)ms.pData;
{
size_t i = 0;
for (const auto& coord : coordinates) {
coord_cb[i].x = coord.first;
// We invert y because the gfx_pc assumes OpenGL coordinates (bottom-left corner is origin), while DX's origin is top-left corner
coord_cb[i].y = td.height - 1 - coord.second;
++i;
}
}
d3d.context->Unmap(d3d.coord_buffer.Get(), 0);
// The depth stencil texture can only have one mapping at a time, so temporarily unbind from the OM
d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), nullptr);
d3d.context->CSSetShaderResources(0, 1, d3d.depth_stencil_srv.GetAddressOf());
// The depth stencil texture can only have one mapping at a time, so unbind from the OM
ID3D11RenderTargetView* null_arr1[1] = { nullptr };
d3d.context->OMSetRenderTargets(1, null_arr1, nullptr);
d3d.context->Dispatch(1, 1, 1);
ID3D11ShaderResourceView *srvs[] = { fb.depth_stencil_srv.Get(), d3d.coord_buffer_srv.Get() };
d3d.context->CSSetShaderResources(0, 2, srvs);
d3d.context->Dispatch(coordinates.size(), 1, 1);
d3d.context->CopyResource(d3d.depth_value_output_buffer_copy.Get(), d3d.depth_value_output_buffer.Get());
ThrowIfFailed(d3d.context->Map(d3d.depth_value_output_buffer_copy.Get(), 0, D3D11_MAP_READ, 0, &ms));
float res = *(float *)ms.pData;
d3d.context->Unmap(d3d.depth_value_output_buffer_copy.Get(), 0);
ID3D11ShaderResourceView *null_arr[1] = { nullptr };
d3d.context->CSSetShaderResources(0, 1, null_arr);
d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), d3d.depth_stencil_view.Get());
return res * 65532.0f;
}
uint16_t gfx_d3d11_get_pixel_depth_old(float x, float y) {
// This approach, compared to using a compute shader, might have better performance on nvidia cards
if (!d3d.copied_depth_buffer) {
d3d.context->CopyResource(d3d.depth_stencil_copy_texture.Get(), d3d.depth_stencil_texture.Get());
d3d.copied_depth_buffer = true;
}
D3D11_MAPPED_SUBRESOURCE mapping_desc;
d3d.context->Map(d3d.depth_stencil_copy_texture.Get(), 0, D3D11_MAP_READ, 0, &mapping_desc);
float res = 0;
if (mapping_desc.pData != nullptr) {
float *addr = (float *)mapping_desc.pData;
uint32_t num_pixels = mapping_desc.DepthPitch / sizeof(float);
uint32_t width = mapping_desc.RowPitch / sizeof(float);
uint32_t height = width == 0 ? 0 : num_pixels / width;
if (x >= 0 && x < width && y >= 0 && y < height) {
res = addr[width * (height - 1 - (int)y) + (int)x];
std::map<std::pair<float, float>, uint16_t> res;
{
size_t i = 0;
for (const auto& coord : coordinates) {
res.emplace(coord, ((float *)ms.pData)[i++] * 65532.0f);
}
}
d3d.context->Unmap(d3d.depth_stencil_copy_texture.Get(), 0);
return res * 65532.0f;
d3d.context->Unmap(d3d.depth_value_output_buffer_copy.Get(), 0);
ID3D11ShaderResourceView *null_arr[2] = { nullptr, nullptr };
d3d.context->CSSetShaderResources(0, 2, null_arr);
return res;
}
} // namespace
void* SohImGui::GetTextureByID(int id) {
ImTextureID gfx_d3d11_get_texture_by_id(int id) {
return d3d.textures[id].resource_view.Get();
}
struct GfxRenderingAPI gfx_direct3d11_api = {
gfx_d3d11_z_is_from_0_to_1,
gfx_d3d11_get_clip_parameters,
gfx_d3d11_unload_shader,
gfx_d3d11_load_shader,
gfx_d3d11_create_and_load_new_shader,
@ -993,7 +1008,6 @@ struct GfxRenderingAPI gfx_direct3d11_api = {
gfx_d3d11_upload_texture,
gfx_d3d11_set_sampler_parameters,
gfx_d3d11_set_depth_test_and_mask,
gfx_d3d11_get_pixel_depth,
gfx_d3d11_set_zmode_decal,
gfx_d3d11_set_viewport,
gfx_d3d11_set_scissor,
@ -1005,11 +1019,16 @@ struct GfxRenderingAPI gfx_direct3d11_api = {
gfx_d3d11_end_frame,
gfx_d3d11_finish_render,
gfx_d3d11_create_framebuffer,
gfx_d3d11_resize_framebuffer,
gfx_d3d11_set_framebuffer,
gfx_d3d11_reset_framebuffer,
gfx_d3d11_update_framebuffer_parameters,
gfx_d3d11_start_draw_to_framebuffer,
gfx_d3d11_clear_framebuffer,
gfx_d3d11_resolve_msaa_color_buffer,
gfx_d3d11_get_pixel_depth,
gfx_d3d11_get_framebuffer_texture_id,
gfx_d3d11_select_texture_fb,
gfx_d3d11_delete_texture
gfx_d3d11_delete_texture,
gfx_d3d11_set_texture_filter,
gfx_d3d11_get_texture_filter
};
#endif

View file

@ -134,13 +134,14 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
}
}
}
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, " float4 screenPos : TEXCOORD2;");
}
if (cc_features.opt_fog) {
append_line(buf, &len, " float4 fog : FOG;");
num_floats += 4;
}
if (cc_features.opt_grayscale) {
append_line(buf, &len, " float4 grayscale : GRAYSCALE;");
num_floats += 4;
}
for (int i = 0; i < cc_features.num_inputs; i++) {
len += sprintf(buf + len, " float%d input%d : INPUT%d;\r\n", cc_features.opt_alpha ? 4 : 3, i + 1, i);
num_floats += cc_features.opt_alpha ? 4 : 3;
@ -163,7 +164,7 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, "cbuffer PerFrameCB : register(b0) {");
append_line(buf, &len, " uint noise_frame;");
append_line(buf, &len, " float2 noise_scale;");
append_line(buf, &len, " float noise_scale;");
append_line(buf, &len, "}");
append_line(buf, &len, "float random(in float3 value) {");
@ -211,15 +212,15 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
if (cc_features.opt_fog) {
append_str(buf, &len, ", float4 fog : FOG");
}
if (cc_features.opt_grayscale) {
append_str(buf, &len, ", float4 grayscale : GRAYSCALE");
}
for (int i = 0; i < cc_features.num_inputs; i++) {
len += sprintf(buf + len, ", float%d input%d : INPUT%d", cc_features.opt_alpha ? 4 : 3, i + 1, i);
}
append_line(buf, &len, ") {");
append_line(buf, &len, " PSInput result;");
append_line(buf, &len, " result.position = position;");
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, " result.screenPos = position;");
}
for (int i = 0; i < 2; i++) {
if (cc_features.used_textures[i]) {
len += sprintf(buf + len, " result.uv%d = uv%d;\r\n", i, i);
@ -234,6 +235,9 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
if (cc_features.opt_fog) {
append_line(buf, &len, " result.fog = fog;");
}
if (cc_features.opt_grayscale) {
append_line(buf, &len, " result.grayscale = grayscale;");
}
for (int i = 0; i < cc_features.num_inputs; i++) {
len += sprintf(buf + len, " result.input%d = input%d;\r\n", i + 1, i + 1);
}
@ -244,7 +248,11 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
if (include_root_signature) {
append_line(buf, &len, "[RootSignature(RS)]");
}
append_line(buf, &len, "float4 PSMain(PSInput input) : SV_TARGET {");
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, "float4 PSMain(PSInput input, float4 screenSpace : SV_Position) : SV_TARGET {");
} else {
append_line(buf, &len, "float4 PSMain(PSInput input) : SV_TARGET {");
}
for (int i = 0; i < 2; i++) {
if (cc_features.used_textures[i]) {
len += sprintf(buf + len, " float2 tc%d = input.uv%d;\r\n", i, i);
@ -300,8 +308,14 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
}
}
if (cc_features.opt_grayscale) {
append_line(buf, &len, "float intensity = (texel.r + texel.g + texel.b) / 3.0;");
append_line(buf, &len, "float3 new_texel = input.grayscale.rgb * intensity;");
append_line(buf, &len, "texel.rgb = lerp(texel.rgb, new_texel, input.grayscale.a);");
}
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, " float2 coords = (input.screenPos.xy / input.screenPos.w) * noise_scale;");
append_line(buf, &len, " float2 coords = screenSpace.xy * noise_scale;");
append_line(buf, &len, " texel.a *= round(saturate(random(float3(floor(coords), noise_frame)) + texel.a - 0.5));");
}

View file

@ -10,6 +10,7 @@
#include <windows.h>
#include <wrl/client.h>
#include <dxgi1_3.h>
#include <dxgi1_4.h>
#include <versionhelpers.h>
#include <shellscalingapi.h>
@ -61,6 +62,7 @@ static struct {
RECT last_window_rect;
bool is_full_screen, last_maximized_state;
bool dxgi1_4;
ComPtr<IDXGIFactory2> factory;
ComPtr<IDXGISwapChain1> swap_chain;
HANDLE waitable_object;
@ -197,17 +199,6 @@ static void toggle_borderless_window_full_screen(bool enable, bool call_callback
}
}
static void gfx_dxgi_on_resize(void) {
if (dxgi.swap_chain.Get() != nullptr) {
gfx_get_current_rendering_api()->on_resize();
DXGI_SWAP_CHAIN_DESC1 desc1;
ThrowIfFailed(dxgi.swap_chain->GetDesc1(&desc1));
dxgi.current_width = desc1.Width;
dxgi.current_height = desc1.Height;
}
}
static void onkeydown(WPARAM w_param, LPARAM l_param) {
int key = ((l_param >> 16) & 0x1ff);
if (dxgi.on_key_down != nullptr) {
@ -227,7 +218,8 @@ static LRESULT CALLBACK gfx_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_par
SohImGui::Update(event_impl);
switch (message) {
case WM_SIZE:
gfx_dxgi_on_resize();
dxgi.current_width = (uint32_t)(l_param & 0xffff);
dxgi.current_height = (uint32_t)(l_param >> 16);
break;
case WM_DESTROY:
exit(0);
@ -573,6 +565,13 @@ void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*crea
ThrowIfFailed(dxgi.CreateDXGIFactory1(__uuidof(IDXGIFactory2), &dxgi.factory));
}
{
ComPtr<IDXGIFactory4> factory4;
if (dxgi.factory->QueryInterface(__uuidof(IDXGIFactory4), &factory4) == S_OK) {
dxgi.dxgi1_4 = true;
}
}
ComPtr<IDXGIAdapter1> adapter;
for (UINT i = 0; dxgi.factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++) {
DXGI_ADAPTER_DESC1 desc;
@ -604,7 +603,9 @@ ComPtr<IDXGISwapChain1> gfx_dxgi_create_swap_chain(IUnknown *device) {
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.Scaling = win8 ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // Apparently this was backported to Win 7 Platform Update
swap_chain_desc.SwapEffect = dxgi.dxgi1_4 ?
DXGI_SWAP_EFFECT_FLIP_DISCARD : // Introduced in DXGI 1.4 and Windows 10
DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // Apparently flip sequential was also backported to Win 7 Platform Update
swap_chain_desc.Flags = dxgi_13 ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0;
swap_chain_desc.SampleDesc.Count = 1;

View file

@ -150,29 +150,29 @@ static struct {
Display *dpy;
Window root;
Window win;
Atom atom_wm_state;
Atom atom_wm_state_fullscreen;
Atom atom_wm_delete_window;
bool is_fullscreen;
void (*on_fullscreen_changed)(bool is_now_fullscreen);
int keymap[256];
bool (*on_key_down)(int scancode);
bool (*on_key_up)(int scancode);
void (*on_all_keys_up)(void);
PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
PFNGLXSWAPBUFFERSMSCOMLPROC glXSwapBuffersMscOML;
PFNGLXWAITFORSBCOMLPROC glXWaitForSbcOML;
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
bool has_oml_sync_control;
uint64_t ust0;
int64_t last_msc;
@ -181,7 +181,7 @@ static struct {
uint64_t last_ust;
int64_t target_msc;
bool dropped_frame;
bool has_sgi_video_sync;
uint64_t last_sync_counter;
int64_t this_msc;
@ -220,7 +220,7 @@ static int64_t glXGetVideoSyncSGI_wrapper(void) {
static void init_keymap(void) {
XkbDescPtr desc = XkbGetMap(glx.dpy, 0, XkbUseCoreKbd);
XkbGetNames(glx.dpy, XkbKeyNamesMask, desc);
for (int i = desc->min_key_code; i <= desc->max_key_code && i < 256; i++) {
char name[XkbKeyNameLength + 1];
memcpy(name, desc->names->keys[i].name, XkbKeyNameLength);
@ -232,7 +232,7 @@ static void init_keymap(void) {
}
}
}
XkbFreeNames(desc, XkbKeyNamesMask, True);
XkbFreeKeyboard(desc, 0, True);
}
@ -265,7 +265,7 @@ static void gfx_glx_set_fullscreen_state(bool on, bool call_callback) {
return;
}
glx.is_fullscreen = on;
XEvent xev;
xev.xany.type = ClientMessage;
xev.xclient.message_type = glx.atom_wm_state;
@ -276,8 +276,8 @@ static void gfx_glx_set_fullscreen_state(bool on, bool call_callback) {
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 0;
XSendEvent(glx.dpy, glx.root, 0, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
gfx_glx_ShowHideMouse(on);
gfx_glx_show_cursor(on);
if (glx.on_fullscreen_changed != NULL && call_callback) {
glx.on_fullscreen_changed(on);
}
@ -303,7 +303,7 @@ static void gfx_glx_init(const char *game_name, bool start_in_fullscreen) {
// which means that glXSwapBuffers should be non-blocking,
// if we are sure to wait at least one vsync interval between calls.
setenv("__GL_MaxFramesAllowed", "2", true);
glx.dpy = XOpenDisplay(NULL);
if (glx.dpy == NULL) {
fprintf(stderr, "Cannot connect to X server\n");
@ -311,7 +311,7 @@ static void gfx_glx_init(const char *game_name, bool start_in_fullscreen) {
}
int screen = DefaultScreen(glx.dpy);
glx.root = RootWindow(glx.dpy, screen);
GLint att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
XVisualInfo *vi = glXChooseVisual(glx.dpy, 0, att);
if (vi == NULL) {
@ -323,7 +323,7 @@ static void gfx_glx_init(const char *game_name, bool start_in_fullscreen) {
swa.colormap = cmap;
swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | FocusChangeMask;
glx.win = XCreateWindow(glx.dpy, glx.root, 0, 0, DESIRED_SCREEN_WIDTH, DESIRED_SCREEN_HEIGHT, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
glx.atom_wm_state = XInternAtom(glx.dpy, "_NET_WM_STATE", False);
glx.atom_wm_state_fullscreen = XInternAtom(glx.dpy, "_NET_WM_STATE_FULLSCREEN", False);
glx.atom_wm_delete_window = XInternAtom(glx.dpy, "WM_DELETE_WINDOW", False);
@ -340,11 +340,11 @@ static void gfx_glx_init(const char *game_name, bool start_in_fullscreen) {
XStoreName(glx.dpy, glx.win, title);
GLXContext glc = glXCreateContext(glx.dpy, vi, NULL, GL_TRUE);
glXMakeCurrent(glx.dpy, glx.win, glc);
init_keymap();
const char *extensions = glXQueryExtensionsString(glx.dpy, screen);
if (gfx_glx_check_extension(extensions, "GLX_OML_sync_control")) {
glx.glXGetSyncValuesOML = (PFNGLXGETSYNCVALUESOMLPROC)glXGetProcAddressARB((const GLubyte *)"glXGetSyncValuesOML");
glx.glXSwapBuffersMscOML = (PFNGLXSWAPBUFFERSMSCOMLPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapBuffersMscOML");
@ -360,7 +360,7 @@ static void gfx_glx_init(const char *game_name, bool start_in_fullscreen) {
glx.glXGetVideoSyncSGI = (PFNGLXGETVIDEOSYNCSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXGetVideoSyncSGI");
glx.glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXWaitVideoSyncSGI");
}
int64_t ust, msc, sbc;
if (glx.glXGetSyncValuesOML != NULL && glx.glXGetSyncValuesOML(glx.dpy, glx.win, &ust, &msc, &sbc)) {
glx.has_oml_sync_control = true;
@ -439,7 +439,7 @@ static void gfx_glx_handle_events(void) {
}
}
}
if (xev.type == ClientMessage && xev.xclient.data.l[0] == glx.atom_wm_delete_window) {
if (xev.type == ClientMessage && (Atom)xev.xclient.data.l[0] == glx.atom_wm_delete_window) {
exit(0);
}
}
@ -451,19 +451,19 @@ static bool gfx_glx_start_frame(void) {
static void gfx_glx_swap_buffers_begin(void) {
glx.wanted_ust += FRAME_INTERVAL_US_NUMERATOR; // advance 1/30 seconds on JP/US or 1/25 seconds on EU
if (!glx.has_oml_sync_control && !glx.has_sgi_video_sync) {
glFlush();
uint64_t target = glx.wanted_ust / FRAME_INTERVAL_US_DENOMINATOR;
uint64_t now;
while (target > (now = (uint64_t)get_time() - glx.ust0)) {
struct timespec ts = {(target - now) / 1000000, ((target - now) % 1000000) * 1000};
struct timespec ts = {(time_t)((target - now) / 1000000), (time_t)(((target - now) % 1000000) * 1000)};
if (nanosleep(&ts, NULL) == 0) {
break;
}
}
if (target + 2 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR < now) {
if (target + 32 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR >= now) {
printf("Dropping frame\n");
@ -476,10 +476,10 @@ static void gfx_glx_swap_buffers_begin(void) {
}
glXSwapBuffers(glx.dpy, glx.win);
glx.dropped_frame = false;
return;
}
double vsyncs_to_wait = (int64_t)(glx.wanted_ust / FRAME_INTERVAL_US_DENOMINATOR - glx.last_ust) / (double)glx.vsync_interval;
if (vsyncs_to_wait <= 0) {
printf("Dropping frame\n");
@ -519,17 +519,17 @@ static void gfx_glx_swap_buffers_begin(void) {
vsyncs_to_wait = 2;
}
glx.target_msc = glx.last_msc + vsyncs_to_wait;
if (glx.has_oml_sync_control) {
glx.glXSwapBuffersMscOML(glx.dpy, glx.win, glx.target_msc, 0, 0);
} else if (glx.has_sgi_video_sync) {
glFlush(); // Try to submit pending work. Don't use glFinish since that busy loops on NVIDIA proprietary driver.
//uint64_t counter0;
uint64_t counter1, counter2;
//uint64_t before_wait = get_time();
counter1 = glXGetVideoSyncSGI_wrapper();
//counter0 = counter1;
//int waits = 0;
@ -537,17 +537,17 @@ static void gfx_glx_swap_buffers_begin(void) {
counter1 = glXWaitVideoSyncSGI_wrapper();
//++waits;
}
//uint64_t before = get_time();
glXSwapBuffers(glx.dpy, glx.win);
counter2 = glXGetVideoSyncSGI_wrapper();
while (counter2 < (uint64_t)glx.target_msc) {
counter2 = glXWaitVideoSyncSGI_wrapper();
}
uint64_t after = get_time();
//printf("%.3f %.3f %.3f\t%.3f\t%u %d %.2f %u %d\n", before_wait * 0.000060, before * 0.000060, after * 0.000060, (after - before) * 0.000060, counter0, counter2 - counter0, vsyncs_to_wait, (unsigned int)glx.target_msc, waits);
glx.this_msc = counter2;
glx.this_ust = after;
@ -558,7 +558,7 @@ static void gfx_glx_swap_buffers_end(void) {
if (glx.dropped_frame || (!glx.has_oml_sync_control && !glx.has_sgi_video_sync)) {
return;
}
int64_t ust, msc, sbc;
if (glx.has_oml_sync_control) {
if (!glx.glXWaitForSbcOML(glx.dpy, glx.win, 0, &ust, &msc, &sbc)) {
@ -600,6 +600,10 @@ static double gfx_glx_get_time(void) {
return 0.0;
}
static void gfx_glx_set_frame_divisor(int divisor) {
// TODO
}
struct GfxWindowManagerAPI gfx_glx = {
gfx_glx_init,
gfx_glx_set_keyboard_callbacks,
@ -612,7 +616,8 @@ struct GfxWindowManagerAPI gfx_glx = {
gfx_glx_start_frame,
gfx_glx_swap_buffers_begin,
gfx_glx_swap_buffers_end,
gfx_glx_get_time
gfx_glx_get_time,
gfx_glx_set_frame_divisor,
};
#endif

View file

@ -3,6 +3,6 @@
#include "gfx_window_manager_api.h"
struct GfxWindowManagerAPI gfx_glx;
extern struct GfxWindowManagerAPI gfx_glx;
#endif

View file

@ -29,8 +29,9 @@
#include "SDL_opengl.h"
#else
#include <SDL2/SDL.h>
#include <GL/glew.h>
#define GL_GLEXT_PROTOTYPES 1
#include <SDL2/SDL_opengles2.h>
// #include <SDL2/SDL_opengles2.h>
#endif
#include "gfx_cc.h"
@ -52,19 +53,34 @@ struct ShaderProgram {
uint8_t num_attribs;
bool used_noise;
GLint frame_count_location;
GLint window_height_location;
GLint noise_scale_location;
};
struct Framebuffer {
uint32_t width, height;
bool has_depth_buffer;
uint32_t msaa_level;
bool invert_y;
GLuint fbo, clrbuf, clrbuf_msaa, rbo;
};
static map<pair<uint64_t, uint32_t>, struct ShaderProgram> shader_program_pool;
static GLuint opengl_vbo;
static uint32_t frame_count;
static uint32_t current_height;
static map<int, pair<GLuint, GLuint>> fb2tex;
static bool current_depth_mask;
static bool gfx_opengl_z_is_from_0_to_1(void) {
return false;
static uint32_t frame_count;
static vector<Framebuffer> framebuffers;
static size_t current_framebuffer;
static float current_noise_scale;
static FilteringMode current_filter_mode = THREE_POINT;
GLuint pixel_depth_rb, pixel_depth_fb;
size_t pixel_depth_rb_size;
static struct GfxClipParameters gfx_opengl_get_clip_parameters(void) {
return { false, framebuffers[current_framebuffer].invert_y };
}
static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram *prg) {
@ -81,7 +97,7 @@ static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram *prg) {
static void gfx_opengl_set_uniforms(struct ShaderProgram *prg) {
if (prg->used_noise) {
glUniform1i(prg->frame_count_location, frame_count);
glUniform1i(prg->window_height_location, current_height);
glUniform1f(prg->noise_scale_location, current_noise_scale);
}
}
@ -163,6 +179,7 @@ static const char *shader_item_to_str(uint32_t item, bool with_alpha, bool only_
return "texel.a";
}
}
return "";
}
static void append_formula(char *buf, size_t *len, uint8_t c[2][4], bool do_single, bool do_multiply, bool do_mix, bool with_alpha, bool only_alpha, bool opt_alpha) {
@ -197,7 +214,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
gfx_cc_get_features(shader_id0, shader_id1, &cc_features);
char vs_buf[1024];
char fs_buf[1024];
char fs_buf[3000];
size_t vs_len = 0;
size_t fs_len = 0;
size_t num_floats = 4;
@ -224,6 +241,13 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
append_line(vs_buf, &vs_len, "varying vec4 vFog;");
num_floats += 4;
}
if (cc_features.opt_grayscale) {
append_line(vs_buf, &vs_len, "attribute vec4 aGrayscaleColor;");
append_line(vs_buf, &vs_len, "varying vec4 vGrayscaleColor;");
num_floats += 4;
}
for (int i = 0; i < cc_features.num_inputs; i++) {
vs_len += sprintf(vs_buf + vs_len, "attribute vec%d aInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
vs_len += sprintf(vs_buf + vs_len, "varying vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
@ -243,6 +267,9 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
if (cc_features.opt_fog) {
append_line(vs_buf, &vs_len, "vFog = aFog;");
}
if (cc_features.opt_grayscale) {
append_line(vs_buf, &vs_len, "vGrayscaleColor = aGrayscaleColor;");
}
for (int i = 0; i < cc_features.num_inputs; i++) {
vs_len += sprintf(vs_buf + vs_len, "vInput%d = aInput%d;\n", i + 1, i + 1);
}
@ -265,6 +292,9 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
if (cc_features.opt_fog) {
append_line(fs_buf, &fs_len, "varying vec4 vFog;");
}
if (cc_features.opt_grayscale) {
append_line(fs_buf, &fs_len, "varying vec4 vGrayscaleColor;");
}
for (int i = 0; i < cc_features.num_inputs; i++) {
fs_len += sprintf(fs_buf + fs_len, "varying vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
}
@ -277,7 +307,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(fs_buf, &fs_len, "uniform int frame_count;");
append_line(fs_buf, &fs_len, "uniform int window_height;");
append_line(fs_buf, &fs_len, "uniform float noise_scale;");
append_line(fs_buf, &fs_len, "float random(in vec3 value) {");
append_line(fs_buf, &fs_len, " float random = dot(sin(value), vec3(12.9898, 78.233, 37.719));");
@ -285,21 +315,42 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
append_line(fs_buf, &fs_len, "}");
}
if (current_filter_mode == THREE_POINT) {
append_line(fs_buf, &fs_len, "#define TEX_OFFSET(off) texture2D(tex, texCoord - (off)/texSize)");
append_line(fs_buf, &fs_len, "vec4 filter3point(in sampler2D tex, in vec2 texCoord, in vec2 texSize) {");
append_line(fs_buf, &fs_len, " vec2 offset = fract(texCoord*texSize - vec2(0.5));");
append_line(fs_buf, &fs_len, " offset -= step(1.0, offset.x + offset.y);");
append_line(fs_buf, &fs_len, " vec4 c0 = TEX_OFFSET(offset);");
append_line(fs_buf, &fs_len, " vec4 c1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y));");
append_line(fs_buf, &fs_len, " vec4 c2 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y)));");
append_line(fs_buf, &fs_len, " return c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0);");
append_line(fs_buf, &fs_len, "}");
append_line(fs_buf, &fs_len, "vec4 hookTexture2D(in sampler2D tex, in vec2 uv, in vec2 texSize) {");
append_line(fs_buf, &fs_len, " return filter3point(tex, uv, texSize);");
append_line(fs_buf, &fs_len, "}");
} else {
append_line(fs_buf, &fs_len, "vec4 hookTexture2D(in sampler2D tex, in vec2 uv, in vec2 texSize) {");
append_line(fs_buf, &fs_len, " return texture2D(tex, uv);");
append_line(fs_buf, &fs_len, "}");
}
append_line(fs_buf, &fs_len, "void main() {");
for (int i = 0; i < 2; i++) {
if (cc_features.used_textures[i]) {
bool s = cc_features.clamp[i][0], t = cc_features.clamp[i][1];
fs_len += sprintf(fs_buf + fs_len, "vec2 texSize%d = textureSize(uTex%d, 0);\n", i, i);
if (!s && !t) {
fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = texture2D(uTex%d, vTexCoord%d);\n", i, i, i);
fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = hookTexture2D(uTex%d, vTexCoord%d, texSize%d);\n", i, i, i, i);
} else {
fs_len += sprintf(fs_buf + fs_len, "vec2 texSize%d = textureSize(uTex%d, 0);\n", i, i);
if (s && t) {
fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = texture2D(uTex%d, clamp(vTexCoord%d, 0.5 / texSize%d, vec2(vTexClampS%d, vTexClampT%d)));\n", i, i, i, i, i, i);
fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = hookTexture2D(uTex%d, clamp(vTexCoord%d, 0.5 / texSize%d, vec2(vTexClampS%d, vTexClampT%d)), texSize%d);\n", i, i, i, i, i, i, i);
} else if (s) {
fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = texture2D(uTex%d, vec2(clamp(vTexCoord%d.s, 0.5 / texSize%d.s, vTexClampS%d), vTexCoord%d.t));\n", i, i, i, i, i, i);
fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = hookTexture2D(uTex%d, vec2(clamp(vTexCoord%d.s, 0.5 / texSize%d.s, vTexClampS%d), vTexCoord%d.t), texSize%d);\n", i, i, i, i, i, i, i);
} else {
fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = texture2D(uTex%d, vec2(vTexCoord%d.s, clamp(vTexCoord%d.t, 0.5 / texSize%d.t, vTexClampT%d)));\n", i, i, i, i, i, i);
fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = hookTexture2D(uTex%d, vec2(vTexCoord%d.s, clamp(vTexCoord%d.t, 0.5 / texSize%d.t, vTexClampT%d)), texSize%d);\n", i, i, i, i, i, i, i);
}
}
}
@ -338,7 +389,13 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
}
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(fs_buf, &fs_len, "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy * (240.0 / float(window_height))), float(frame_count))) + texel.a, 0.0, 1.0));");
append_line(fs_buf, &fs_len, "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy * noise_scale), float(frame_count))) + texel.a, 0.0, 1.0));");
}
if (cc_features.opt_grayscale) {
append_line(fs_buf, &fs_len, "float intensity = (texel.r + texel.g + texel.b) / 3.0;");
append_line(fs_buf, &fs_len, "vec3 new_texel = vGrayscaleColor.rgb * intensity;");
append_line(fs_buf, &fs_len, "texel.rgb = mix(texel.rgb, new_texel, vGrayscaleColor.a);");
}
if (cc_features.opt_alpha) {
@ -389,9 +446,9 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
GLint max_length = 0;
glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &max_length);
char error_log[1024];
//fprintf(stderr, "Fragment shader compilation failed\n");
fprintf(stderr, "Fragment shader compilation failed\n");
glGetShaderInfoLog(fragment_shader, max_length, &max_length, &error_log[0]);
//fprintf(stderr, "%s\n", &error_log[0]);
fprintf(stderr, "%s\n", &error_log[0]);
abort();
}
@ -432,6 +489,12 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
++cnt;
}
if (cc_features.opt_grayscale) {
prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aGrayscaleColor");
prg->attrib_sizes[cnt] = 4;
++cnt;
}
for (int i = 0; i < cc_features.num_inputs; i++) {
char name[16];
sprintf(name, "aInput%d", i + 1);
@ -460,7 +523,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
if (cc_features.opt_alpha && cc_features.opt_noise) {
prg->frame_count_location = glGetUniformLocation(shader_program, "frame_count");
prg->window_height_location = glGetUniformLocation(shader_program, "window_height");
prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale");
prg->used_noise = true;
} else {
prg->used_noise = false;
@ -496,7 +559,7 @@ static void gfx_opengl_select_texture(int tile, GLuint texture_id) {
}
static void gfx_opengl_upload_texture(const uint8_t *rgba32_buf, uint32_t width, uint32_t height) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf);
}
static uint32_t gfx_cm_to_opengl(uint32_t val) {
@ -510,12 +573,14 @@ static uint32_t gfx_cm_to_opengl(uint32_t val) {
case G_TX_NOMIRROR | G_TX_WRAP:
return GL_REPEAT;
}
return 0;
}
static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) {
const GLint filter = linear_filter && current_filter_mode == LINEAR ? GL_LINEAR : GL_NEAREST;
glActiveTexture(GL_TEXTURE0 + tile);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gfx_cm_to_opengl(cms));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gfx_cm_to_opengl(cmt));
}
@ -543,7 +608,6 @@ static void gfx_opengl_set_zmode_decal(bool zmode_decal) {
static void gfx_opengl_set_viewport(int x, int y, int width, int height) {
glViewport(x, y, width, height);
current_height = height;
}
static void gfx_opengl_set_scissor(int x, int y, int width, int height) {
@ -564,10 +628,6 @@ static void gfx_opengl_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_
glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris);
}
static unsigned int framebuffer;
static unsigned int textureColorbuffer;
static unsigned int rbo;
static void gfx_opengl_init(void) {
//#if FOR_WINDOWS
glewInit();
@ -576,143 +636,230 @@ static void gfx_opengl_init(void) {
glGenBuffers(1, &opengl_vbo);
glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo);
glGenFramebuffers(1, &framebuffer);
glGenTextures(1, &textureColorbuffer);
glGenRenderbuffers(1, &rbo);
SohUtils::saveEnvironmentVar("framebuffer", std::to_string(textureColorbuffer));
glEnable(GL_DEPTH_CLAMP);
glDepthFunc(GL_LEQUAL);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
framebuffers.resize(1); // for the default screen buffer
glGenRenderbuffers(1, &pixel_depth_rb);
glBindRenderbuffer(GL_RENDERBUFFER, pixel_depth_rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1, 1);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenFramebuffers(1, &pixel_depth_fb);
glBindFramebuffer(GL_FRAMEBUFFER, pixel_depth_fb);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, pixel_depth_rb);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
pixel_depth_rb_size = 1;
}
static void gfx_opengl_on_resize(void) {
}
static void gfx_opengl_start_frame(void) {
GLsizei framebuffer_width = gfx_current_dimensions.width;
GLsizei framebuffer_height = gfx_current_dimensions.height;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::shared_ptr<Ship::Window> wnd = Ship::GlobalCtx2::GetInstance()->GetWindow();
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, framebuffer_width, framebuffer_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, framebuffer_width, framebuffer_height); // use a single renderbuffer object for both a depth AND stencil buffer.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // now actually attach it
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
frame_count++;
glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_TRUE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
glEnable(GL_DEPTH_CLAMP);
current_depth_mask = true;
}
static void gfx_opengl_end_frame(void) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint last_program;
glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
glUseProgram(0);
SohImGui::Draw();
glUseProgram(last_program);
glFlush();
}
static void gfx_opengl_finish_render(void) {
}
static int gfx_opengl_create_framebuffer(uint32_t width, uint32_t height) {
GLuint textureColorbuffer;
glGenTextures(1, &textureColorbuffer);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
static int gfx_opengl_create_framebuffer() {
GLuint clrbuf;
glGenTextures(1, &clrbuf);
glBindTexture(GL_TEXTURE_2D, clrbuf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
GLuint clrbuf_msaa;
glGenRenderbuffers(1, &clrbuf_msaa);
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1, 1);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
size_t i = framebuffers.size();
framebuffers.resize(i + 1);
fb2tex[fbo] = make_pair(textureColorbuffer, rbo);
framebuffers[i].fbo = fbo;
framebuffers[i].clrbuf = clrbuf;
framebuffers[i].clrbuf_msaa = clrbuf_msaa;
framebuffers[i].rbo = rbo;
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
return fbo;
return i;
}
static void gfx_opengl_resize_framebuffer(int fb, uint32_t width, uint32_t height) {
glBindTexture(GL_TEXTURE_2D, fb2tex[fb].first);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth) {
Framebuffer& fb = framebuffers[fb_id];
glBindRenderbuffer(GL_RENDERBUFFER, fb2tex[fb].second);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
width = max(width, 1U);
height = max(height, 1U);
void gfx_opengl_set_framebuffer(int fb)
{
if (GLEW_ARB_clip_control || GLEW_VERSION_4_5) {
// Set origin to upper left corner, to match N64 and DX11
// If this function is not supported, the texture will be upside down :(
glClipControl(GL_UPPER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
if (fb_id != 0) {
if (fb.width != width || fb.height != height || fb.msaa_level != msaa_level) {
if (msaa_level <= 1) {
glBindTexture(GL_TEXTURE_2D, fb.clrbuf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.clrbuf, 0);
} else {
glBindRenderbuffer(GL_RENDERBUFFER, fb.clrbuf_msaa);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_level, GL_RGB8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fb.clrbuf_msaa);
}
}
if (has_depth_buffer && (fb.width != width || fb.height != height || fb.msaa_level != msaa_level || !fb.has_depth_buffer)) {
glBindRenderbuffer(GL_RENDERBUFFER, fb.rbo);
if (msaa_level <= 1) {
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
} else {
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_level, GL_DEPTH24_STENCIL8, width, height);
}
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
if (!fb.has_depth_buffer && has_depth_buffer) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.rbo);
} else if (fb.has_depth_buffer && !has_depth_buffer) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
}
}
glBindFramebuffer(GL_FRAMEBUFFER_EXT, fb);
fb.width = width;
fb.height = height;
fb.has_depth_buffer = has_depth_buffer;
fb.msaa_level = msaa_level;
fb.invert_y = opengl_invert_y;
}
void gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) {
Framebuffer& fb = framebuffers[fb_id];
if (noise_scale != 0.0f) {
current_noise_scale = 1.0f / noise_scale;
}
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
current_framebuffer = fb_id;
}
void gfx_opengl_clear_framebuffer() {
glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_TRUE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDepthMask(current_depth_mask ? GL_TRUE : GL_FALSE);
glEnable(GL_SCISSOR_TEST);
}
void gfx_opengl_reset_framebuffer(void)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, framebuffer);
if (GLEW_ARB_clip_control || GLEW_VERSION_4_5) {
glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
}
void gfx_opengl_resolve_msaa_color_buffer(int fb_id_target, int fb_id_source) {
Framebuffer& fb_dst = framebuffers[fb_id_target];
Framebuffer& fb_src = framebuffers[fb_id_source];
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_dst.fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_src.fbo);
glBlitFramebuffer(0, 0, fb_src.width, fb_src.height, 0, 0, fb_dst.width, fb_dst.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
}
void gfx_opengl_select_texture_fb(int fbID)
{
void *gfx_opengl_get_framebuffer_texture_id(int fb_id) {
return (void *)(uintptr_t)framebuffers[fb_id].clrbuf;
}
void gfx_opengl_select_texture_fb(int fb_id) {
//glDisable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, fb2tex[fbID].first);
glBindTexture(GL_TEXTURE_2D, framebuffers[fb_id].clrbuf);
}
static uint16_t gfx_opengl_get_pixel_depth(float x, float y) {
float depth;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return depth * 65532.0f;
static std::map<std::pair<float, float>, uint16_t> gfx_opengl_get_pixel_depth(int fb_id, const std::set<std::pair<float, float>>& coordinates) {
std::map<std::pair<float, float>, uint16_t> res;
Framebuffer& fb = framebuffers[fb_id];
if (coordinates.size() == 1) {
uint32_t depth_stencil_value;
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
int x = coordinates.begin()->first;
int y = coordinates.begin()->second;
glReadPixels(x, fb.invert_y ? fb.height - y : y, 1, 1, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, &depth_stencil_value);
res.emplace(*coordinates.begin(), (depth_stencil_value >> 18) << 2);
} else {
if (pixel_depth_rb_size < coordinates.size()) {
// Resizing a renderbuffer seems broken with Intel's driver, so recreate one instead.
glBindFramebuffer(GL_FRAMEBUFFER, pixel_depth_fb);
glDeleteRenderbuffers(1, &pixel_depth_rb);
glGenRenderbuffers(1, &pixel_depth_rb);
glBindRenderbuffer(GL_RENDERBUFFER, pixel_depth_rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, coordinates.size(), 1);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, pixel_depth_rb);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
pixel_depth_rb_size = coordinates.size();
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb.fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pixel_depth_fb);
glDisable(GL_SCISSOR_TEST); // needed for the blit operation
{
size_t i = 0;
for (const auto& coord : coordinates) {
int x = coord.first;
int y = coord.second;
if (fb.invert_y) {
y = fb.height - y;
}
glBlitFramebuffer(x, y, x + 1, y + 1, i, 0, i + 1, 1, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
++i;
}
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, pixel_depth_fb);
vector<uint32_t> depth_stencil_values(coordinates.size());
glReadPixels(0, 0, coordinates.size(), 1, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, depth_stencil_values.data());
{
size_t i = 0;
for (const auto& coord : coordinates) {
res.emplace(coord, (depth_stencil_values[i++] >> 18) << 2);
}
}
}
glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
return res;
}
void gfx_opengl_set_texture_filter(FilteringMode mode) {
current_filter_mode = mode;
gfx_texture_cache_clear();
}
FilteringMode gfx_opengl_get_texture_filter(void) {
return current_filter_mode;
}
struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_z_is_from_0_to_1,
gfx_opengl_get_clip_parameters,
gfx_opengl_unload_shader,
gfx_opengl_load_shader,
gfx_opengl_create_and_load_new_shader,
@ -723,7 +870,6 @@ struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_upload_texture,
gfx_opengl_set_sampler_parameters,
gfx_opengl_set_depth_test_and_mask,
gfx_opengl_get_pixel_depth,
gfx_opengl_set_zmode_decal,
gfx_opengl_set_viewport,
gfx_opengl_set_scissor,
@ -735,11 +881,16 @@ struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_end_frame,
gfx_opengl_finish_render,
gfx_opengl_create_framebuffer,
gfx_opengl_resize_framebuffer,
gfx_opengl_set_framebuffer,
gfx_opengl_reset_framebuffer,
gfx_opengl_update_framebuffer_parameters,
gfx_opengl_start_draw_to_framebuffer,
gfx_opengl_clear_framebuffer,
gfx_opengl_resolve_msaa_color_buffer,
gfx_opengl_get_pixel_depth,
gfx_opengl_get_framebuffer_texture_id,
gfx_opengl_select_texture_fb,
gfx_opengl_delete_texture
gfx_opengl_delete_texture,
gfx_opengl_set_texture_filter,
gfx_opengl_get_texture_filter
};
#endif

View file

@ -7,8 +7,10 @@
#include <stdio.h>
#include <map>
#include <set>
#include <unordered_map>
#include <vector>
#include <list>
#ifndef _LANGUAGE_C
#define _LANGUAGE_C
@ -27,10 +29,14 @@
#include "../../luslog.h"
#include "../StrHash64.h"
#include "../../SohImGuiImpl.h"
#include "../../Environment.h"
#include "../../GameVersions.h"
#include "../../ResourceMgr.h"
// OTRTODO: fix header files for these
extern "C" {
char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc);
const char* ResourceMgr_GetNameByCRC(uint64_t crc);
int32_t* ResourceMgr_LoadMtxByCRC(uint64_t crc);
Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc);
Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc);
@ -42,6 +48,8 @@ extern "C" {
using namespace std;
#define SEG_ADDR(seg, addr) (addr | (seg << 24) | 1)
#define SUPPORT_CHECK(x) assert(x)
// SCALE_M_N: upscale/downscale M-bit integer to N-bit
@ -71,10 +79,6 @@ struct RGBA {
uint8_t r, g, b, a;
};
struct XYWidthHeight {
uint16_t x, y, width, height;
};
struct LoadedVertex {
float x, y, z, w;
float u, v;
@ -84,7 +88,7 @@ struct LoadedVertex {
static struct {
TextureCacheMap map;
list<TextureCacheMap::iterator> lru;
list<TextureCacheMapIter> lru;
vector<uint32_t> free_texture_ids;
} gfx_texture_cache;
@ -130,14 +134,14 @@ static struct RDP {
const uint8_t *addr;
uint8_t siz;
uint32_t width;
char* otr_path;
const char* otr_path;
} texture_to_load;
struct {
const uint8_t *addr;
uint32_t size_bytes;
uint32_t full_image_line_size_bytes;
uint32_t line_size_bytes;
char* otr_path;
const char* otr_path;
} loaded_texture[2];
struct {
uint8_t fmt;
@ -156,9 +160,10 @@ static struct RDP {
uint32_t other_mode_l, other_mode_h;
uint64_t combine_mode;
bool grayscale;
uint8_t prim_lod_fraction;
struct RGBA env_color, prim_color, fog_color, fill_color;
struct RGBA env_color, prim_color, fog_color, fill_color, grayscale_color;
struct XYWidthHeight viewport, scissor;
bool viewport_or_scissor_changed;
void *z_buf_address;
@ -174,11 +179,23 @@ static struct RenderingState {
TextureCacheNode *textures[2];
} rendering_state;
struct GfxDimensions gfx_current_window_dimensions;
struct GfxDimensions gfx_current_dimensions;
static struct GfxDimensions gfx_prev_dimensions;
struct XYWidthHeight gfx_current_game_window_viewport;
static bool game_renders_to_framebuffer;
static int game_framebuffer;
static int game_framebuffer_msaa_resolved;
uint32_t gfx_msaa_level = 1;
static bool has_drawn_imgui_menu;
static bool dropped_frame;
static const std::unordered_map<Mtx *, MtxF> *current_mtx_replacements;
static float buf_vbo[MAX_BUFFERED * (32 * 3)]; // 3 vertices in a triangle and 32 floats per vtx
static size_t buf_vbo_len;
static size_t buf_vbo_num_tris;
@ -198,7 +215,10 @@ static bool fbActive = 0;
static map<int, FBInfo>::iterator active_fb;
static map<int, FBInfo> framebuffers;
#ifdef _MSC_VER
static set<pair<float, float>> get_pixel_depth_pending;
static map<pair<float, float>, uint16_t> get_pixel_depth_cached;
#ifdef _WIN32
// TODO: Properly implement for MSVC
static unsigned long get_time(void)
{
@ -436,15 +456,15 @@ static void gfx_generate_cc(struct ColorCombiner *comb, uint64_t cc_id) {
val = SHADER_COMBINED;
break;
}
// fallthrough for G_ACMUX_LOD_FRACTION
c[i][1][j] = G_CCMUX_LOD_FRACTION;
[[fallthrough]]; // for G_ACMUX_LOD_FRACTION
case G_ACMUX_1:
//case G_ACMUX_PRIM_LOD_FRAC: same numerical value
if (j != 2) {
val = SHADER_1;
break;
}
// fallthrough for G_ACMUX_PRIM_LOD_FRAC
[[fallthrough]]; // for G_ACMUX_PRIM_LOD_FRAC
case G_ACMUX_PRIMITIVE:
case G_ACMUX_SHADE:
case G_ACMUX_ENVIRONMENT:
@ -507,18 +527,18 @@ static bool gfx_texture_cache_lookup(int i, int tile) {
key = { orig_addr, { }, fmt, siz, palette_index };
}
auto it = gfx_texture_cache.map.find(key);
TextureCacheMap::iterator it = gfx_texture_cache.map.find(key);
if (it != gfx_texture_cache.map.end()) {
gfx_rapi->select_texture(i, it->second.texture_id);
*n = &*it;
gfx_texture_cache.lru.splice(gfx_texture_cache.lru.end(), gfx_texture_cache.lru, *(list<TextureCacheMap::iterator>::iterator*)&it->second.lru_location); // move to back
gfx_texture_cache.lru.splice(gfx_texture_cache.lru.end(), gfx_texture_cache.lru, it->second.lru_location); // move to back
return true;
}
if (gfx_texture_cache.map.size() >= TEXTURE_CACHE_MAX_SIZE) {
// Remove the texture that was least recently used
it = gfx_texture_cache.lru.front();
it = gfx_texture_cache.lru.front().it;
gfx_texture_cache.free_texture_ids.push_back(it->second.texture_id);
gfx_texture_cache.map.erase(it);
gfx_texture_cache.lru.pop_front();
@ -535,7 +555,7 @@ static bool gfx_texture_cache_lookup(int i, int tile) {
it = gfx_texture_cache.map.insert(make_pair(key, TextureCacheValue())).first;
TextureCacheNode* node = &*it;
node->second.texture_id = texture_id;
*(list<TextureCacheMap::iterator>::iterator*)&node->second.lru_location = gfx_texture_cache.lru.insert(gfx_texture_cache.lru.end(), it);
node->second.lru_location = gfx_texture_cache.lru.insert(gfx_texture_cache.lru.end(), { it });
gfx_rapi->select_texture(i, texture_id);
gfx_rapi->set_sampler_parameters(i, false, 0, 0);
@ -551,9 +571,9 @@ static void gfx_texture_cache_delete(const uint8_t* orig_addr)
bool again = false;
for (auto it = gfx_texture_cache.map.begin(bucket); it != gfx_texture_cache.map.end(bucket); ++it) {
if (it->first.texture_addr == orig_addr) {
gfx_texture_cache.lru.erase(*(list<TextureCacheMap::iterator>::iterator*)&it->second.lru_location);
gfx_texture_cache.lru.erase(it->second.lru_location);
gfx_texture_cache.free_texture_ids.push_back(it->second.texture_id);
gfx_texture_cache.map.erase(it);
gfx_texture_cache.map.erase(it->first);
again = true;
break;
}
@ -815,22 +835,6 @@ static void import_texture(int i, int tile) {
uint8_t siz = rdp.texture_tile[tile].siz;
uint32_t tmem_index = rdp.texture_tile[tile].tmem_index;
// OTRTODO: Move it to a function to be faster
// ModInternal::bindHook(LOOKUP_TEXTURE);
// ModInternal::initBindHook(8,
// HOOK_PARAMETER("gfx_api", gfx_get_current_rendering_api()),
// HOOK_PARAMETER("path", rdp.loaded_texture[tmem_index].otr_path),
// HOOK_PARAMETER("node", &rendering_state.textures[i]),
// HOOK_PARAMETER("fmt", &fmt),
// HOOK_PARAMETER("siz", &siz),
// HOOK_PARAMETER("tile", &i),
// HOOK_PARAMETER("palette", &rdp.texture_tile[tile].palette),
// HOOK_PARAMETER("addr", const_cast<uint8_t*>(rdp.loaded_texture[tmem_index].addr))
// );
//
// if (ModInternal::callBindHook(0))
// return;
if (gfx_texture_cache_lookup(i, tile))
{
return;
@ -916,20 +920,31 @@ static void gfx_matrix_mul(float res[4][4], const float a[4][4], const float b[4
static void gfx_sp_matrix(uint8_t parameters, const int32_t *addr) {
float matrix[4][4];
#ifndef GBI_FLOATS
// Original GBI where fixed point matrices are used
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j += 2) {
int32_t int_part = addr[i * 2 + j / 2];
uint32_t frac_part = addr[8 + i * 2 + j / 2];
matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f;
matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f;
if (auto it = current_mtx_replacements->find((Mtx *)addr); it != current_mtx_replacements->end()) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
float v = it->second.mf[i][j];
int as_int = (int)(v * 65536.0f);
matrix[i][j] = as_int * (1.0f / 65536.0f);
}
}
} else {
#ifndef GBI_FLOATS
// Original GBI where fixed point matrices are used
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j += 2) {
int32_t int_part = addr[i * 2 + j / 2];
uint32_t frac_part = addr[8 + i * 2 + j / 2];
matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f;
matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f;
}
}
}
#else
// For a modified GBI where fixed point values are replaced with floats
memcpy(matrix, addr, sizeof(matrix));
// For a modified GBI where fixed point values are replaced with floats
memcpy(matrix, addr, sizeof(matrix));
#endif
}
if (parameters & G_MTX_PROJECTION) {
if (parameters & G_MTX_LOAD) {
@ -1210,7 +1225,6 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
uint64_t cc_id = rdp.combine_mode;
//bool use_alpha = (rdp.other_mode_l & (3 << 18)) == G_BL_1MA || (rdp.other_mode_l & (3 << 16)) == G_BL_1MA;
bool use_alpha = (rdp.other_mode_l & (3 << 20)) == (G_BL_CLR_MEM << 20) && (rdp.other_mode_l & (3 << 16)) == (G_BL_1MA << 16);
bool use_fog = (rdp.other_mode_l >> 30) == G_BL_CLR_FOG;
bool texture_edge = (rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA;
@ -1218,6 +1232,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
bool use_2cyc = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_2CYCLE;
bool alpha_threshold = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_THRESHOLD;
bool invisible = (rdp.other_mode_l & (3 << 24)) == (G_BL_0 << 24) && (rdp.other_mode_l & (3 << 20)) == (G_BL_CLR_MEM << 20);
bool use_grayscale = rdp.grayscale;
if (texture_edge) {
use_alpha = true;
@ -1230,12 +1245,13 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
if (use_2cyc) cc_id |= (uint64_t)SHADER_OPT_2CYC << CC_SHADER_OPT_POS;
if (alpha_threshold) cc_id |= (uint64_t)SHADER_OPT_ALPHA_THRESHOLD << CC_SHADER_OPT_POS;
if (invisible) cc_id |= (uint64_t)SHADER_OPT_INVISIBLE << CC_SHADER_OPT_POS;
if (use_grayscale) cc_id |= (uint64_t)SHADER_OPT_GRAYSCALE << CC_SHADER_OPT_POS;
if (!use_alpha) {
cc_id &= ~((0xfff << 16) | ((uint64_t)0xfff << 44));
}
struct ColorCombiner* comb = gfx_lookup_or_create_color_combiner(cc_id);
ColorCombiner* comb = gfx_lookup_or_create_color_combiner(cc_id);
uint32_t tm = 0;
uint32_t tex_width[2], tex_height[2], tex_width2[2], tex_height2[2];
@ -1326,11 +1342,11 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
gfx_rapi->shader_get_info(prg, &num_inputs, used_textures);
bool z_is_from_0_to_1 = gfx_rapi->z_is_from_0_to_1();
struct GfxClipParameters clip_parameters = gfx_rapi->get_clip_parameters();
for (int i = 0; i < 3; i++) {
float z = v_arr[i]->z, w = v_arr[i]->w;
if (z_is_from_0_to_1) {
if (clip_parameters.z_is_from_0_to_1) {
z = (z + w) / 2.0f;
}
@ -1340,7 +1356,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
}
buf_vbo[buf_vbo_len++] = v_arr[i]->x;
buf_vbo[buf_vbo_len++] = v_arr[i]->y;
buf_vbo[buf_vbo_len++] = clip_parameters.invert_y ? -v_arr[i]->y : v_arr[i]->y;
buf_vbo[buf_vbo_len++] = z;
buf_vbo[buf_vbo_len++] = w;
@ -1397,6 +1413,13 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
buf_vbo[buf_vbo_len++] = v_arr[i]->color.a / 255.0f; // fog factor (not alpha)
}
if (use_grayscale) {
buf_vbo[buf_vbo_len++] = rdp.grayscale_color.r / 255.0f;
buf_vbo[buf_vbo_len++] = rdp.grayscale_color.g / 255.0f;
buf_vbo[buf_vbo_len++] = rdp.grayscale_color.b / 255.0f;
buf_vbo[buf_vbo_len++] = rdp.grayscale_color.a / 255.0f; // lerp interpolation factor (not alpha)
}
for (int j = 0; j < num_inputs; j++) {
struct RGBA* color = 0;
struct RGBA tmp;
@ -1491,6 +1514,27 @@ static void gfx_sp_geometry_mode(uint32_t clear, uint32_t set) {
rsp.geometry_mode |= set;
}
static void gfx_adjust_viewport_or_scissor(XYWidthHeight *area) {
if (!fbActive) {
area->width *= RATIO_X;
area->height *= RATIO_Y;
area->x *= RATIO_X;
area->y = SCREEN_HEIGHT - area->y;
area->y *= RATIO_Y;
if (!game_renders_to_framebuffer || (gfx_msaa_level > 1 && gfx_current_dimensions.width == gfx_current_game_window_viewport.width && gfx_current_dimensions.height == gfx_current_game_window_viewport.height)) {
area->x += gfx_current_game_window_viewport.x;
area->y += gfx_current_window_dimensions.height - (gfx_current_game_window_viewport.y + gfx_current_game_window_viewport.height);
}
} else {
area->width *= RATIO_Y;
area->height *= RATIO_Y;
area->x *= RATIO_Y;
area->y = active_fb->second.orig_height - area->y;
area->y *= RATIO_Y;
}
}
static void gfx_calc_and_set_viewport(const Vp_t *viewport) {
// 2 bits fraction
float width = 2.0f * viewport->vscale[0] / 4.0f;
@ -1498,25 +1542,13 @@ static void gfx_calc_and_set_viewport(const Vp_t *viewport) {
float x = (viewport->vtrans[0] / 4.0f) - width / 2.0f;
float y = ((viewport->vtrans[1] / 4.0f) + height / 2.0f);
if (!fbActive) {
width *= RATIO_X;
height *= RATIO_Y;
x *= RATIO_X;
y = SCREEN_HEIGHT - y;
y *= RATIO_Y;
} else {
width *= RATIO_Y;
height *= RATIO_Y;
x *= RATIO_Y;
y = active_fb->second.orig_height - y;
y *= RATIO_Y;
}
rdp.viewport.x = x;
rdp.viewport.y = y;
rdp.viewport.width = width;
rdp.viewport.height = height;
gfx_adjust_viewport_or_scissor(&rdp.viewport);
rdp.viewport_or_scissor_changed = true;
}
@ -1585,11 +1617,6 @@ static void gfx_sp_texture(uint16_t sc, uint16_t tc, uint8_t level, uint8_t tile
rdp.textures_changed[1] = true;
}
if (tile > 8)
{
int bp = 0;
}
rdp.first_tile_index = tile;
}
@ -1599,33 +1626,21 @@ static void gfx_dp_set_scissor(uint32_t mode, uint32_t ulx, uint32_t uly, uint32
float width = (lrx - ulx) / 4.0f;
float height = (lry - uly) / 4.0f;
if (!fbActive) {
x *= RATIO_X;
y = SCREEN_HEIGHT - y;
y *= RATIO_Y;
width *= RATIO_X;
height *= RATIO_Y;
} else {
width *= RATIO_Y;
height *= RATIO_Y;
x *= RATIO_Y;
y = active_fb->second.orig_height - y;
y *= RATIO_Y;
}
rdp.scissor.x = x;
rdp.scissor.y = y;
rdp.scissor.width = width;
rdp.scissor.height = height;
gfx_adjust_viewport_or_scissor(&rdp.scissor);
rdp.viewport_or_scissor_changed = true;
}
static void gfx_dp_set_texture_image(uint32_t format, uint32_t size, uint32_t width, const void* addr, char* otr_path) {
static void gfx_dp_set_texture_image(uint32_t format, uint32_t size, uint32_t width, const void* addr, const char* otr_path) {
rdp.texture_to_load.addr = (const uint8_t*)addr;
rdp.texture_to_load.siz = size;
rdp.texture_to_load.width = width;
if ( otr_path != nullptr && !strncmp(otr_path, "__OTR__", 7)) otr_path = otr_path + 7;
if (otr_path != nullptr && !strncmp(otr_path, "__OTR__", 7)) otr_path = otr_path + 7;
rdp.texture_to_load.otr_path = otr_path;
}
@ -1697,7 +1712,7 @@ static void gfx_dp_load_block(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t
SUPPORT_CHECK(ult == 0);
// The lrs field rather seems to be number of pixels to load
uint32_t word_size_shift;
uint32_t word_size_shift = 0;
switch (rdp.texture_to_load.siz) {
case G_IM_SIZ_4b:
word_size_shift = 0; // Or -1? It's unused in SM64 anyway.
@ -1725,7 +1740,7 @@ static void gfx_dp_load_block(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t
static void gfx_dp_load_tile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t lrt) {
SUPPORT_CHECK(tile == G_TX_LOADTILE);
uint32_t word_size_shift;
uint32_t word_size_shift = 0;
switch (rdp.texture_to_load.siz) {
case G_IM_SIZ_4b:
word_size_shift = 0;
@ -1805,6 +1820,13 @@ static inline uint32_t alpha_comb(uint32_t a, uint32_t b, uint32_t c, uint32_t d
return (a & 7) | ((b & 7) << 3) | ((c & 7) << 6) | ((d & 7) << 9);
}
static void gfx_dp_set_grayscale_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
rdp.grayscale_color.r = r;
rdp.grayscale_color.g = g;
rdp.grayscale_color.b = b;
rdp.grayscale_color.a = a;
}
static void gfx_dp_set_env_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
rdp.env_color.r = r;
rdp.env_color.g = g;
@ -1887,10 +1909,12 @@ static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lr
ur->w = 1.0f;
// The coordinates for texture rectangle shall bypass the viewport setting
struct XYWidthHeight default_viewport = { 0, 0, gfx_current_dimensions.width, gfx_current_dimensions.height };
struct XYWidthHeight default_viewport = { 0, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT };
struct XYWidthHeight viewport_saved = rdp.viewport;
uint32_t geometry_mode_saved = rsp.geometry_mode;
gfx_adjust_viewport_or_scissor(&default_viewport);
rdp.viewport = default_viewport;
rdp.viewport_or_scissor_changed = true;
rsp.geometry_mode = 0;
@ -2056,12 +2080,11 @@ static void gfx_s2dex_bg_copy(const uObjBg* bg) {
static inline void* seg_addr(uintptr_t w1)
{
// Segmented?
if (w1 >= 0xF0000000)
if (w1 & 1)
{
uint32_t segNum = (w1 >> 24);
segNum -= 0xF0;
uint32_t offset = w1 & 0x00FFFFFF;
uint32_t offset = w1 & 0x00FFFFFE;
//offset = 0; // Cursed Malon bug
if (segmentPointers[segNum] != 0)
@ -2078,7 +2101,7 @@ static inline void* seg_addr(uintptr_t w1)
#define C0(pos, width) ((cmd->words.w0 >> (pos)) & ((1U << width) - 1))
#define C1(pos, width) ((cmd->words.w1 >> (pos)) & ((1U << width) - 1))
int dListBP;
unsigned int dListBP;
int matrixBP;
uintptr_t clearMtx;
@ -2091,7 +2114,7 @@ static void gfx_run_dl(Gfx* cmd) {
//puts("dl");
int dummy = 0;
char dlName[128];
char fileName[128];
const char* fileName;
Gfx* dListStart = cmd;
uint64_t ourHash = -1;
@ -2145,8 +2168,16 @@ static void gfx_run_dl(Gfx* cmd) {
uintptr_t mtxAddr = cmd->words.w1;
// OTRTODO: Temp way of dealing with gMtxClear. Need something more elegant in the future...
if (mtxAddr == 0xF012DB20 || mtxAddr == 0xF012DB40)
mtxAddr = clearMtx;
uint32_t gameVersion = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->GetGameVersion();
if (gameVersion == OOT_PAL_GC) {
if (mtxAddr == SEG_ADDR(0, 0x0FBC20)) {
mtxAddr = clearMtx;
}
} else {
if (mtxAddr == SEG_ADDR(0, 0x12DB20) || mtxAddr == SEG_ADDR(0, 0x12DB40)) {
mtxAddr = clearMtx;
}
}
#ifdef F3DEX_GBI_2
gfx_sp_matrix(C0(0, 8) ^ G_MTX_PUSH, (const int32_t *) seg_addr(mtxAddr));
@ -2246,7 +2277,7 @@ static void gfx_run_dl(Gfx* cmd) {
cmd--;
if (ourHash != -1)
if (ourHash != (uint64_t)-1)
ResourceMgr_RegisterResourcePatch(ourHash, cmd - dListStart, cmd->words.w1);
cmd->words.w1 = (uintptr_t)vtx;
@ -2364,7 +2395,7 @@ static void gfx_run_dl(Gfx* cmd) {
case G_QUAD:
{
int bp = 0;
// fallthrough
[[fallthrough]];
}
#endif
#if defined(F3DEX_GBI) || defined(F3DLP_GBI)
@ -2394,11 +2425,11 @@ static void gfx_run_dl(Gfx* cmd) {
char* imgData = (char*)i;
if ((i & 0xF0000000) != 0xF0000000)
if ((i & 1) != 1)
if (ResourceMgr_OTRSigCheck(imgData) == 1)
i = (uintptr_t)ResourceMgr_LoadTexByName(imgData);
gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), (void*) i, imgData);
gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), (void*) i, imgData);
break;
}
case G_SETTIMG_OTR:
@ -2406,9 +2437,7 @@ static void gfx_run_dl(Gfx* cmd) {
uintptr_t addr = cmd->words.w1;
cmd++;
uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + (uint64_t)cmd->words.w1;
ResourceMgr_GetNameByCRC(hash, fileName);
fileName = ResourceMgr_GetNameByCRC(hash);
#if _DEBUG && 0
char* tex = ResourceMgr_LoadTexByCRC(hash);
ResourceMgr_GetNameByCRC(hash, fileName);
@ -2417,7 +2446,7 @@ static void gfx_run_dl(Gfx* cmd) {
char* tex = NULL;
#endif
if (addr != NULL)
if (addr != 0)
{
tex = (char*)addr;
}
@ -2431,7 +2460,7 @@ static void gfx_run_dl(Gfx* cmd) {
uintptr_t oldData = cmd->words.w1;
cmd->words.w1 = (uintptr_t)tex;
if (ourHash != -1)
if (ourHash != (uint64_t)-1)
ResourceMgr_RegisterResourcePatch(ourHash, cmd - dListStart, oldData);
cmd++;
@ -2449,24 +2478,24 @@ static void gfx_run_dl(Gfx* cmd) {
gfx_dp_set_texture_image(fmt, size, width, tex, fileName);
cmd++;
}
break;
}
case G_SETFB:
{
gfx_flush();
fbActive = 1;
active_fb = framebuffers.find(cmd->words.w1);
gfx_rapi->set_framebuffer(active_fb->first);
}
gfx_rapi->start_draw_to_framebuffer(active_fb->first, (float)active_fb->second.applied_height / active_fb->second.orig_height);
gfx_rapi->clear_framebuffer();
break;
}
case G_RESETFB:
{
gfx_flush();
fbActive = 0;
gfx_rapi->reset_framebuffer();
gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT);
break;
}
break;
case G_SETTIMG_FB:
{
gfx_flush();
@ -2476,8 +2505,13 @@ static void gfx_run_dl(Gfx* cmd) {
//if (texPtr != NULL)
//gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), texPtr);
break;
}
case G_SETGRAYSCALE:
{
rdp.grayscale = cmd->words.w1;
break;
}
break;
case G_LOADBLOCK:
gfx_dp_load_block(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12));
break;
@ -2505,6 +2539,9 @@ static void gfx_run_dl(Gfx* cmd) {
case G_SETFILLCOLOR:
gfx_dp_set_fill_color(cmd->words.w1);
break;
case G_SETINTENSITY:
gfx_dp_set_grayscale_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8));
break;
case G_SETCOMBINE:
gfx_dp_set_combine_mode(
color_comb(C0(20, 4), C1(28, 4), C0(15, 5), C1(15, 3)),
@ -2547,7 +2584,7 @@ static void gfx_run_dl(Gfx* cmd) {
gfx_dp_texture_rectangle(ulx, uly, lrx, lry, tile, uls, ult, dsdx, dtdy, opcode == G_TEXRECTFLIP);
break;
}
case G_TEXRECT_WIDE:
case G_TEXRECT_WIDE:
{
int32_t lrx, lry, tile, ulx, uly;
uint32_t uls, ult, dsdx, dtdy;
@ -2630,12 +2667,15 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi, co
gfx_rapi = rapi;
gfx_wapi->init(game_name, start_in_fullscreen);
gfx_rapi->init();
gfx_rapi->update_framebuffer_parameters(0, SCREEN_WIDTH, SCREEN_HEIGHT, 1, false, true, true, true);
gfx_current_dimensions.internal_mul = 1;
gfx_current_dimensions.width = SCREEN_WIDTH;
gfx_current_dimensions.height = SCREEN_HEIGHT;
game_framebuffer = gfx_rapi->create_framebuffer();
game_framebuffer_msaa_resolved = gfx_rapi->create_framebuffer();
for (int i = 0; i < 16; i++)
segmentPointers[i] = NULL;
segmentPointers[i] = 0;
// Used in the 120 star TAS
static uint32_t precomp_shaders[] = {
@ -2681,7 +2721,9 @@ struct GfxRenderingAPI *gfx_get_current_rendering_api(void) {
void gfx_start_frame(void) {
gfx_wapi->handle_events();
// gfx_wapi->get_dimensions(&gfx_current_dimensions.width, &gfx_current_dimensions.height);
gfx_wapi->get_dimensions(&gfx_current_window_dimensions.width, &gfx_current_window_dimensions.height);
SohImGui::DrawMainMenuAndCalculateGameSize();
has_drawn_imgui_menu = true;
if (gfx_current_dimensions.height == 0) {
// Avoid division by zero
gfx_current_dimensions.height = 1;
@ -2693,7 +2735,7 @@ void gfx_start_frame(void) {
uint32_t width = fb.second.orig_width, height = fb.second.orig_height;
gfx_adjust_width_height_for_scale(width, height);
if (width != fb.second.applied_width || height != fb.second.applied_height) {
gfx_rapi->resize_framebuffer(fb.first, width, height);
gfx_rapi->update_framebuffer_parameters(fb.first, width, height, 1, true, true, true, true);
fb.second.applied_width = width;
fb.second.applied_height = height;
}
@ -2701,28 +2743,81 @@ void gfx_start_frame(void) {
}
gfx_prev_dimensions = gfx_current_dimensions;
bool different_size = gfx_current_dimensions.width != gfx_current_game_window_viewport.width || gfx_current_dimensions.height != gfx_current_game_window_viewport.height;
if (different_size || gfx_msaa_level > 1) {
game_renders_to_framebuffer = true;
if (different_size) {
gfx_rapi->update_framebuffer_parameters(game_framebuffer, gfx_current_dimensions.width, gfx_current_dimensions.height, gfx_msaa_level, true, true, true, true);
} else {
// MSAA framebuffer needs to be resolved to an equally sized target when complete, which must therefore match the window size
gfx_rapi->update_framebuffer_parameters(game_framebuffer, gfx_current_window_dimensions.width, gfx_current_window_dimensions.height, gfx_msaa_level, false, true, true, true);
}
if (gfx_msaa_level > 1 && different_size) {
gfx_rapi->update_framebuffer_parameters(game_framebuffer_msaa_resolved, gfx_current_dimensions.width, gfx_current_dimensions.height, 1, false, false, false, false);
}
} else {
game_renders_to_framebuffer = false;
}
fbActive = 0;
}
void gfx_run(Gfx *commands) {
void gfx_run(Gfx *commands, const std::unordered_map<Mtx *, MtxF>& mtx_replacements) {
gfx_sp_reset();
//puts("New frame");
get_pixel_depth_pending.clear();
get_pixel_depth_cached.clear();
if (!gfx_wapi->start_frame()) {
dropped_frame = true;
if (has_drawn_imgui_menu) {
SohImGui::DrawFramebufferAndGameInput();
SohImGui::CancelFrame();
has_drawn_imgui_menu = false;
}
return;
}
dropped_frame = false;
if (!has_drawn_imgui_menu) {
SohImGui::DrawMainMenuAndCalculateGameSize();
}
current_mtx_replacements = &mtx_replacements;
double t0 = gfx_wapi->get_time();
gfx_rapi->update_framebuffer_parameters(0, gfx_current_window_dimensions.width, gfx_current_window_dimensions.height, 1, false, true, true, !game_renders_to_framebuffer);
gfx_rapi->start_frame();
gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT);
gfx_rapi->clear_framebuffer();
gfx_run_dl(commands);
gfx_flush();
SohUtils::saveEnvironmentVar("framebuffer", string());
if (game_renders_to_framebuffer) {
gfx_rapi->start_draw_to_framebuffer(0, 1);
gfx_rapi->clear_framebuffer();
if (gfx_msaa_level > 1) {
bool different_size = gfx_current_dimensions.width != gfx_current_game_window_viewport.width || gfx_current_dimensions.height != gfx_current_game_window_viewport.height;
if (different_size) {
gfx_rapi->resolve_msaa_color_buffer(game_framebuffer_msaa_resolved, game_framebuffer);
SohUtils::saveEnvironmentVar("framebuffer", std::to_string((uintptr_t)gfx_rapi->get_framebuffer_texture_id(game_framebuffer_msaa_resolved)));
} else {
gfx_rapi->resolve_msaa_color_buffer(0, game_framebuffer);
}
} else {
SohUtils::saveEnvironmentVar("framebuffer", std::to_string((uintptr_t)gfx_rapi->get_framebuffer_texture_id(game_framebuffer)));
}
}
SohImGui::DrawFramebufferAndGameInput();
SohImGui::Render();
double t1 = gfx_wapi->get_time();
//printf("Process %f %f\n", t1, t1 - t0);
gfx_rapi->end_frame();
gfx_wapi->swap_buffers_begin();
has_drawn_imgui_menu = false;
}
void gfx_end_frame(void) {
@ -2739,21 +2834,47 @@ void gfx_set_framedivisor(int divisor) {
int gfx_create_framebuffer(uint32_t width, uint32_t height) {
uint32_t orig_width = width, orig_height = height;
gfx_adjust_width_height_for_scale(width, height);
int fb = gfx_rapi->create_framebuffer(width, height);
int fb = gfx_rapi->create_framebuffer();
gfx_rapi->update_framebuffer_parameters(fb, width, height, 1, true, true, true, true);
framebuffers[fb] = { orig_width, orig_height, width, height };
return fb;
}
void gfx_set_framebuffer(int fb)
{
gfx_rapi->set_framebuffer(fb);
void gfx_set_framebuffer(int fb, float noise_scale) {
gfx_rapi->start_draw_to_framebuffer(fb, noise_scale);
gfx_rapi->clear_framebuffer();
}
void gfx_reset_framebuffer()
{
gfx_rapi->reset_framebuffer();
void gfx_reset_framebuffer() {
gfx_rapi->start_draw_to_framebuffer(0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT);
}
static void adjust_pixel_depth_coordinates(float& x, float& y) {
x = x * RATIO_Y - (SCREEN_WIDTH * RATIO_Y - gfx_current_dimensions.width) / 2;
y *= RATIO_Y;
if (!game_renders_to_framebuffer || (gfx_msaa_level > 1 && gfx_current_dimensions.width == gfx_current_game_window_viewport.width && gfx_current_dimensions.height == gfx_current_game_window_viewport.height)) {
x += gfx_current_game_window_viewport.x;
y += gfx_current_window_dimensions.height - (gfx_current_game_window_viewport.y + gfx_current_game_window_viewport.height);
}
}
void gfx_get_pixel_depth_prepare(float x, float y) {
adjust_pixel_depth_coordinates(x, y);
get_pixel_depth_pending.emplace(x, y);
}
uint16_t gfx_get_pixel_depth(float x, float y) {
return gfx_rapi->get_pixel_depth(x * RATIO_Y - (SCREEN_WIDTH * RATIO_Y - gfx_current_dimensions.width) / 2, y * RATIO_Y);
adjust_pixel_depth_coordinates(x, y);
if (auto it = get_pixel_depth_cached.find(make_pair(x, y)); it != get_pixel_depth_cached.end()) {
return it->second;
}
get_pixel_depth_pending.emplace(x, y);
map<pair<float, float>, uint16_t> res = gfx_rapi->get_pixel_depth(game_renders_to_framebuffer ? game_framebuffer : 0, get_pixel_depth_pending);
get_pixel_depth_cached.merge(res);
get_pixel_depth_pending.clear();
return get_pixel_depth_cached.find(make_pair(x, y))->second;
}

View file

@ -4,12 +4,19 @@
#include <stdbool.h>
#include <stdint.h>
#include <unordered_map>
#include <list>
#include "U64/PR/ultra64/types.h"
struct GfxRenderingAPI;
struct GfxWindowManagerAPI;
struct GfxDimensions
{
struct XYWidthHeight {
int16_t x, y;
uint32_t width, height;
};
struct GfxDimensions {
uint32_t internal_mul;
uint32_t width, height;
float aspect_ratio;
@ -22,7 +29,7 @@ struct TextureCacheKey {
uint8_t palette_index;
bool operator==(const TextureCacheKey&) const noexcept = default;
struct Hasher {
size_t operator()(const TextureCacheKey& key) const noexcept {
uintptr_t addr = (uintptr_t)key.texture_addr;
@ -39,30 +46,31 @@ struct TextureCacheValue {
uint8_t cms, cmt;
bool linear_filter;
// Old versions of libstdc++ fail to compile this
#ifdef _MSC_VER
std::list<TextureCacheMap::iterator>::iterator lru_location;
#else
std::list<int>::iterator lru_location;
#endif
std::list<struct TextureCacheMapIter>::iterator lru_location;
};
struct TextureCacheMapIter {
TextureCacheMap::iterator it;
};
#ifdef __cplusplus
extern "C" {
#endif
extern struct GfxDimensions gfx_current_dimensions;
extern struct GfxDimensions gfx_current_window_dimensions; // The dimensions of the window
extern struct GfxDimensions gfx_current_dimensions; // The dimensions of the draw area the game draws to, before scaling (if applicable)
extern struct XYWidthHeight gfx_current_game_window_viewport; // The area of the window the game is drawn to, (0, 0) is top-left corner
extern uint32_t gfx_msaa_level;
}
void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name, bool start_in_fullscreen);
struct GfxRenderingAPI* gfx_get_current_rendering_api(void);
void gfx_start_frame(void);
void gfx_run(Gfx* commands);
void gfx_run(Gfx* commands, const std::unordered_map<Mtx*, MtxF>& mtx_replacements);
void gfx_end_frame(void);
void gfx_set_framedivisor(int);
void gfx_texture_cache_clear();
int gfx_create_framebuffer(uint32_t width, uint32_t height);
extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height);
void gfx_get_pixel_depth_prepare(float x, float y);
uint16_t gfx_get_pixel_depth(float x, float y);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -5,10 +5,24 @@
#include <stdint.h>
#include <stdbool.h>
#include <map>
#include <set>
struct ShaderProgram;
struct GfxClipParameters {
bool z_is_from_0_to_1;
bool invert_y;
};
enum FilteringMode {
THREE_POINT,
LINEAR,
NONE
};
struct GfxRenderingAPI {
bool (*z_is_from_0_to_1)(void);
struct GfxClipParameters (*get_clip_parameters)(void);
void (*unload_shader)(struct ShaderProgram *old_prg);
void (*load_shader)(struct ShaderProgram *new_prg);
struct ShaderProgram *(*create_and_load_new_shader)(uint64_t shader_id0, uint32_t shader_id1);
@ -19,7 +33,6 @@ struct GfxRenderingAPI {
void (*upload_texture)(const uint8_t *rgba32_buf, uint32_t width, uint32_t height);
void (*set_sampler_parameters)(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt);
void (*set_depth_test_and_mask)(bool depth_test, bool z_upd);
uint16_t (*get_pixel_depth)(float x, float y);
void (*set_zmode_decal)(bool zmode_decal);
void (*set_viewport)(int x, int y, int width, int height);
void (*set_scissor)(int x, int y, int width, int height);
@ -30,12 +43,17 @@ struct GfxRenderingAPI {
void (*start_frame)(void);
void (*end_frame)(void);
void (*finish_render)(void);
int (*create_framebuffer)(uint32_t width, uint32_t height);
void (*resize_framebuffer)(int fb, uint32_t width, uint32_t height);
void (*set_framebuffer)(int fb);
void (*reset_framebuffer)();
void (*select_texture_fb)(int fbID);
int (*create_framebuffer)();
void (*update_framebuffer_parameters)(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth);
void (*start_draw_to_framebuffer)(int fb_id, float noise_scale);
void (*clear_framebuffer)(void);
void (*resolve_msaa_color_buffer)(int fb_id_target, int fb_id_source);
std::map<std::pair<float, float>, uint16_t> (*get_pixel_depth)(int fb_id, const std::set<std::pair<float, float>>& coordinates);
void *(*get_framebuffer_texture_id)(int fb_id);
void (*select_texture_fb)(int fb_id);
void (*delete_texture)(uint32_t texID);
void (*set_texture_filter)(FilteringMode mode);
FilteringMode(*get_texture_filter)(void);
};
#endif

View file

@ -1,6 +1,6 @@
#include <stdio.h>
#if !defined(__linux__) && defined(ENABLE_OPENGL)
#if defined(ENABLE_OPENGL)
#ifdef __MINGW32__
#define FOR_WINDOWS 1
@ -23,7 +23,9 @@
#include "gfx_window_manager_api.h"
#include "gfx_screen_config.h"
#ifdef _WIN32
#include <WTypesbase.h>
#endif
#include <time.h>
#define GFX_API_NAME "SDL2 - OpenGL"
@ -41,7 +43,7 @@ static bool (*on_key_up_callback)(int scancode);
static void (*on_all_keys_up_callback)(void);
const SDL_Scancode windows_scancode_table[] =
{
{
/* 0 1 2 3 4 5 6 7 */
/* 8 9 A B C D E F */
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, /* 0 */
@ -117,7 +119,9 @@ static void set_fullscreen(bool on, bool call_callback) {
}
static uint64_t previous_time;
#ifndef __linux__
static HANDLE timer;
#endif
static int frameDivisor = 1;
@ -131,7 +135,9 @@ static void gfx_sdl_init(const char *game_name, bool start_in_fullscreen) {
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#ifndef __linux
timer = CreateWaitableTimer(nullptr, false, nullptr);
#endif
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
@ -188,7 +194,7 @@ static void gfx_sdl_set_keyboard_callbacks(bool (*on_key_down)(int scancode), bo
}
static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) {
while (1)
while (1)
{
run_one_game_iter();
}

View file

@ -502,7 +502,6 @@ namespace ImGui
IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text
IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape
IMGUI_API void ImageRotated(ImTextureID tex_id, ImVec2 center, ImVec2 size, float angle);
IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0));
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding
IMGUI_API bool Checkbox(const char* label, bool* v);

View file

@ -1007,33 +1007,6 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
return held;
}
void ImGui::ImageRotated(ImTextureID tex_id, ImVec2 center, ImVec2 size, float angle) {
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImRect bb(window->DC.CursorPos + ImVec2(size.x / 2, size.y / 2), window->DC.CursorPos + size);
ImVec2 pos[4] =
{
center + bb.Min + ImVec2(-size.x * 0.5f, -size.y * 0.5f),
center + bb.Min + ImVec2(+size.x * 0.5f, -size.y * 0.5f),
center + bb.Min + ImVec2(+size.x * 0.5f, +size.y * 0.5f),
center + bb.Min + ImVec2(-size.x * 0.5f, +size.y * 0.5f)
};
ImVec2 uvs[4] =
{
ImVec2(0.0f, 1.0f),
ImVec2(1.0f, 1.0f),
ImVec2(1.0f, 0.0f),
ImVec2(0.0f, 0.0f)
};
draw_list->AddImageQuad(tex_id, pos[0], pos[1], pos[2], pos[3], uvs[0], uvs[1], uvs[2], uvs[3], IM_COL32_WHITE);
}
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiWindow* window = GetCurrentWindow();

View file

@ -86,14 +86,8 @@
#include <stdint.h>
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define u64 uint64_t
#define unint uint32_t
#define INITIAL_CRC64 0xffffffffffffffffULL
extern uint64_t update_crc64(const void* buf, unint len, u64 crc);
extern u64 crc64(const void* buf, unint len);
extern u64 CRC64(const char* t);
extern uint64_t update_crc64(const void* buf, uint32_t len, uint64_t crc);
extern uint64_t crc64(const void* buf, uint32_t len);
extern uint64_t CRC64(const char* t);

View file

@ -10,6 +10,7 @@
#include <spdlog/details/synchronous_factory.h>
#include "SohImGuiImpl.h"
#include "GameSettings.h"
#include "Cvar.h"
#include <chrono>
#include <mutex>
#include <string>
@ -45,8 +46,8 @@ protected:
}
formatted.push_back('\0');
const char *msg_output = formatted.data();
if (Game::Settings.debug.soh_sink && SohImGui::console->opened)
SohImGui::console->Append("SoH Logging", priority, msg_output);
if (CVar_GetS32("gSinkEnabled", 0) && SohImGui::console->opened)
SohImGui::console->Append("SoH Logging", priority, "%s", msg_output);
}
void flush_() override {}
@ -66,6 +67,8 @@ private:
return Priority::ERROR_LVL;
case spdlog::level::critical:
return Priority::ERROR_LVL;
default:
break;
}
return Priority::LOG_LVL;
}

View file

@ -39,7 +39,7 @@ namespace Ship
Vertex* vtxData = new Vertex[numVerts];
uint32_t* indicesData = new uint32_t[numPolys];
if (vertices != NULL)
if (vertices != 0)
{
reader->Seek(headerStart + vertices, SeekOffsetType::Start);
@ -47,7 +47,7 @@ namespace Ship
vtxData[i].pos = reader->ReadVec3f();
}
if (normals != NULL)
if (normals != 0)
{
reader->Seek(headerStart + normals, SeekOffsetType::Start);
@ -55,7 +55,7 @@ namespace Ship
vtxData[i].normal = reader->ReadVec3f();
}
if (vertexColors != NULL)
if (vertexColors != 0)
{
reader->Seek(headerStart + vertexColors, SeekOffsetType::Start);
@ -63,7 +63,7 @@ namespace Ship
vtxData[i].color = reader->ReadColor3b();
}
if (uvCoords != NULL)
if (uvCoords != 0)
{
reader->Seek(headerStart + uvCoords, SeekOffsetType::Start);
@ -71,7 +71,7 @@ namespace Ship
vtxData[i].uv = reader->ReadVec2f();
}
if (boneWeights != NULL)
if (boneWeights != 0)
{
reader->Seek(headerStart + boneWeights, SeekOffsetType::Start);
@ -81,7 +81,7 @@ namespace Ship
mdl->boneWeights[i] = reader->ReadVec2f();
}
if (faces != NULL)
if (faces != 0)
{
reader->Seek(headerStart + faces, SeekOffsetType::Start);
reader->Read((char*)indicesData, numPolys * sizeof(uint32_t));

View file

@ -0,0 +1,173 @@
#if defined(__linux__) || defined(__BSD__)
#include "PulseAudioPlayer.h"
#include <spdlog/spdlog.h>
namespace Ship
{
static void pas_context_state_cb(pa_context *c, void *userdata) {
switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED:
*(bool*)userdata = true;
break;
default:
break;
}
}
static void pas_stream_state_cb(pa_stream *s, void *userdata) {
switch (pa_stream_get_state(s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
*(bool*)userdata = true;
break;
default:
break;
}
}
static void pas_stream_write_cb(pa_stream* s, size_t length, void* userdata) {
}
static void pas_update_complete(pa_stream* stream, int success, void* userdata) {
*(bool*)userdata = true;
}
static void pas_write_complete(void* userdata) {
*(bool*)userdata = true;
}
bool PulseAudioPlayer::Init()
{
bool done = false;
const pa_buffer_attr* applied_attr = nullptr;
// Create mainloop
m_MainLoop = pa_mainloop_new();
if (m_MainLoop == NULL) {
return false;
}
// Create context and connect
m_Context = pa_context_new(pa_mainloop_get_api(m_MainLoop), "Ocarina of Time");
if (m_Context == NULL) {
goto fail;
}
pa_context_set_state_callback(m_Context, pas_context_state_cb, &done);
if (pa_context_connect(m_Context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
goto fail;
}
while (!done) {
pa_mainloop_iterate(m_MainLoop, true, NULL);
}
pa_context_set_state_callback(m_Context, NULL, NULL);
if (pa_context_get_state(m_Context) != PA_CONTEXT_READY) {
goto fail;
}
// Create stream
pa_sample_spec ss;
ss.format = PA_SAMPLE_S16LE;
ss.rate = 32000;
ss.channels = 2;
pa_buffer_attr attr;
attr.maxlength = (1600 + 544 + 528 + 1600) * 4;
attr.tlength = (528*2 + 544) * 4;
attr.prebuf = 1500 * 4;
attr.minreq = 161 * 4;
attr.fragsize = (uint32_t)-1;
m_Stream = pa_stream_new(m_Context, "zelda", &ss, NULL);
if (m_Stream == NULL) {
goto fail;
}
done = false;
pa_stream_set_state_callback(m_Stream, pas_stream_state_cb, &done);
pa_stream_set_write_callback(m_Stream, pas_stream_write_cb, NULL);
if (pa_stream_connect_playback(m_Stream, NULL, &attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) {
goto fail;
}
while (!done) {
pa_mainloop_iterate(m_MainLoop, true, NULL);
}
pa_stream_set_state_callback(m_Stream, NULL, NULL);
if (pa_stream_get_state(m_Stream) != PA_STREAM_READY) {
goto fail;
}
applied_attr = pa_stream_get_buffer_attr(m_Stream);
SPDLOG_TRACE("maxlength: {}\ntlength: {}\nprebuf: {}\nminreq: {}\nfragsize: {}\n",
applied_attr->maxlength, applied_attr->tlength, applied_attr->prebuf, applied_attr->minreq, applied_attr->fragsize);
m_Attr = *applied_attr;
return true;
fail:
if (m_Stream != NULL) {
pa_stream_unref(m_Stream);
m_Stream = NULL;
}
if (m_Context != NULL) {
pa_context_disconnect(m_Context);
pa_context_unref(m_Context);
m_Context = NULL;
}
if (m_MainLoop != NULL) {
pa_mainloop_free(m_MainLoop);
m_MainLoop = NULL;
}
return false;
}
int PulseAudioPlayer::Buffered()
{
if (m_Stream == NULL) {
return 0;
}
bool done = false;
pa_stream_update_timing_info(m_Stream, pas_update_complete, &done);
while (!done) {
pa_mainloop_iterate(m_MainLoop, true, NULL);
}
const pa_timing_info *info = pa_stream_get_timing_info(m_Stream);
if (info == NULL) {
SPDLOG_ERROR("pa_stream_get_timing_info failed, state is %d\n", pa_stream_get_state(m_Stream));
}
return (info->write_index - info->read_index) / 4;
}
int PulseAudioPlayer::GetDesiredBuffered()
{
// return 1100;
return 1680;
}
void PulseAudioPlayer::Play(const uint8_t* buff, uint32_t len)
{
size_t ws = m_Attr.maxlength - Buffered() * 4;
if (ws < len) {
len = ws;
}
if (pa_stream_write_ext_free(m_Stream, buff, len, pas_write_complete, &m_WriteComplete, 0LL, PA_SEEK_RELATIVE) < 0) {
SPDLOG_ERROR("pa_stream_write failed");
return;
}
while (!m_WriteComplete) {
pa_mainloop_iterate(m_MainLoop, true, NULL);
}
m_WriteComplete = false;
}
}
#endif

View file

@ -0,0 +1,26 @@
#pragma once
#if defined(__linux__) || defined(__BSD__)
#include "AudioPlayer.h"
#include <pulse/pulseaudio.h>
namespace Ship {
class PulseAudioPlayer : public AudioPlayer {
public:
PulseAudioPlayer() {}
bool Init() override;
int Buffered() override;
int GetDesiredBuffered() override;
void Play(const uint8_t* buff, uint32_t len) override;
private:
pa_context* m_Context = nullptr;
pa_stream* m_Stream = nullptr;
pa_mainloop* m_MainLoop = nullptr;
bool m_WriteComplete = false;
pa_buffer_attr m_Attr = {0};
};
}
#endif

View file

@ -2,8 +2,8 @@
#include "DisplayList.h"
#include "ResourceMgr.h"
#include "Utils/BinaryReader.h"
#include "lib/tinyxml2/tinyxml2.h"
#include "lib/Fast3D/U64/PR/ultra64/gbi.h"
#include "Lib/tinyxml2/tinyxml2.h"
#include "Lib/Fast3D/U64/PR/ultra64/gbi.h"
namespace Ship
{
@ -25,7 +25,7 @@ namespace Ship
void ResourceFile::WriteFileBinary(BinaryWriter* writer, Resource* res)
{
}
void ResourceFile::WriteFileXML(tinyxml2::XMLElement* writer, Resource* res)
@ -35,17 +35,20 @@ namespace Ship
Resource::~Resource()
{
free(cachedGameAsset);
free(cachedGameAsset);
cachedGameAsset = nullptr;
for (int i = 0; i < patches.size(); i++)
for (size_t i = 0; i < patches.size(); i++)
{
std::string hashStr = resMgr->HashToString(patches[i].crc);
auto resShared = resMgr->GetCachedFile(hashStr);
const std::string* hashStr = resMgr->HashToString(patches[i].crc);
if (hashStr == nullptr)
continue;
auto resShared = resMgr->GetCachedFile(hashStr->c_str());
if (resShared != nullptr)
{
auto res = (Ship::DisplayList*)resShared.get();
Gfx* gfx = (Gfx*)&res->instructions[patches[i].index];
gfx->words.w1 = patches[i].origData;
}

View file

@ -6,7 +6,7 @@
#include "GlobalCtx2.h"
#include "StrHash.h"
#include "File.h"
#include "lib/tinyxml2/tinyxml2.h"
#include "Lib/tinyxml2/tinyxml2.h"
namespace Ship
{
@ -101,10 +101,10 @@ namespace Ship
class ResourcePromise {
public:
std::shared_ptr<Resource> Resource;
std::shared_ptr<File> File;
std::condition_variable ResourceLoadNotifier;
std::mutex ResourceLoadMutex;
std::shared_ptr<Resource> resource;
std::shared_ptr<File> file;
std::condition_variable resourceLoadNotifier;
std::mutex resourceLoadMutex;
bool bHasResourceLoaded = false;
};
}

View file

@ -43,7 +43,7 @@ namespace Ship {
const std::lock_guard<std::mutex> ResLock(ResourceLoadMutex);
bIsRunning = false;
}
FileLoadNotifier.notify_all();
ResourceLoadNotifier.notify_all();
FileLoadThread->join();
@ -89,7 +89,7 @@ namespace Ship {
OTR->LoadFile(ToLoad->path, true, ToLoad);
//Lock.lock();
if (!ToLoad->bHasLoadError)
FileCache[ToLoad->path] = ToLoad->bIsLoaded && !ToLoad->bHasLoadError ? ToLoad : nullptr;
@ -124,15 +124,15 @@ namespace Ship {
// Wait for the underlying File to complete loading
{
std::unique_lock<std::mutex> FileLock(ToLoad->File->FileLoadMutex);
while (!ToLoad->File->bIsLoaded && !ToLoad->File->bHasLoadError) {
ToLoad->File->FileLoadNotifier.wait(FileLock);
std::unique_lock<std::mutex> FileLock(ToLoad->file->FileLoadMutex);
while (!ToLoad->file->bIsLoaded && !ToLoad->file->bHasLoadError) {
ToLoad->file->FileLoadNotifier.wait(FileLock);
}
}
if (!ToLoad->File->bHasLoadError)
if (!ToLoad->file->bHasLoadError)
{
auto UnmanagedRes = ResourceLoader::LoadResource(ToLoad->File);
auto UnmanagedRes = ResourceLoader::LoadResource(ToLoad->file);
if (UnmanagedRes != nullptr)
{
@ -140,13 +140,13 @@ namespace Ship {
auto Res = std::shared_ptr<Resource>(UnmanagedRes);
if (Res != nullptr) {
std::unique_lock<std::mutex> Lock(ToLoad->ResourceLoadMutex);
std::unique_lock<std::mutex> Lock(ToLoad->resourceLoadMutex);
ToLoad->bHasResourceLoaded = true;
ToLoad->Resource = Res;
ToLoad->resource = Res;
ResourceCache[Res->file->path] = Res;
SPDLOG_DEBUG("Loaded Resource {} on ResourceMgr thread", ToLoad->File->path);
SPDLOG_DEBUG("Loaded Resource {} on ResourceMgr thread", ToLoad->file->path);
// Disabled for now because it can cause random crashes
//FileCache[Res->File->path] = nullptr;
@ -155,9 +155,9 @@ namespace Ship {
}
else {
ToLoad->bHasResourceLoaded = false;
ToLoad->Resource = nullptr;
ToLoad->resource = nullptr;
SPDLOG_ERROR("Resource load FAILED {} on ResourceMgr thread", ToLoad->File->path);
SPDLOG_ERROR("Resource load FAILED {} on ResourceMgr thread", ToLoad->file->path);
}
//ResLock.lock();
@ -167,10 +167,10 @@ namespace Ship {
else
{
ToLoad->bHasResourceLoaded = false;
ToLoad->Resource = nullptr;
ToLoad->resource = nullptr;
}
ToLoad->ResourceLoadNotifier.notify_all();
ToLoad->resourceLoadNotifier.notify_all();
}
SPDLOG_INFO("Resource Manager LoadResourceThread ended");
@ -215,48 +215,53 @@ namespace Ship {
return ToLoad;
}
std::shared_ptr<Ship::Resource> ResourceMgr::GetCachedFile(std::string FilePath) {
std::shared_ptr<Ship::Resource> ResourceMgr::GetCachedFile(const char* FilePath) const {
auto resCacheFind = ResourceCache.find(FilePath);
if (resCacheFind != ResourceCache.end())
if (resCacheFind != ResourceCache.end() &&
resCacheFind->second.use_count() > 0)
{
return resCacheFind->second;
}
else
return nullptr;
}
std::shared_ptr<Resource> ResourceMgr::LoadResource(std::string FilePath) {
auto Promise = LoadResourceAsync(FilePath);
std::shared_ptr<Resource> ResourceMgr::LoadResource(const char* FilePath) {
auto Res = LoadResourceAsync(FilePath);
if (std::holds_alternative<std::shared_ptr<Resource>>(Res))
return std::get<std::shared_ptr<Resource>>(Res);
auto& Promise = std::get<std::shared_ptr<ResourcePromise>>(Res);
if (!Promise->bHasResourceLoaded)
{
std::unique_lock<std::mutex> Lock(Promise->ResourceLoadMutex);
std::unique_lock<std::mutex> Lock(Promise->resourceLoadMutex);
while (!Promise->bHasResourceLoaded) {
Promise->ResourceLoadNotifier.wait(Lock);
Promise->resourceLoadNotifier.wait(Lock);
}
}
return Promise->Resource;
return Promise->resource;
}
std::shared_ptr<ResourcePromise> ResourceMgr::LoadResourceAsync(std::string FilePath) {
StringHelper::ReplaceOriginal(FilePath, "/", "\\");
if (StringHelper::StartsWith(FilePath, "__OTR__"))
FilePath = StringHelper::Split(FilePath, "__OTR__")[1];
std::shared_ptr<ResourcePromise> Promise = std::make_shared<ResourcePromise>();
std::variant<std::shared_ptr<Resource>, std::shared_ptr<ResourcePromise>> ResourceMgr::LoadResourceAsync(const char* FilePath) {
if (FilePath[0] == '_' && FilePath[1] == '_' && FilePath[2] == 'O' && FilePath[3] == 'T' && FilePath[4] == 'R' && FilePath[5] == '_' && FilePath[6] == '_')
FilePath += 7;
const std::lock_guard<std::mutex> ResLock(ResourceLoadMutex);
auto resCacheFind = ResourceCache.find(FilePath);
if (resCacheFind == ResourceCache.end() || resCacheFind->second->isDirty/* || !FileData->bIsLoaded*/) {
if (resCacheFind == ResourceCache.end()) {
SPDLOG_TRACE("Cache miss on Resource load: {}", FilePath.c_str());
SPDLOG_TRACE("Cache miss on Resource load: {}", FilePath);
}
std::shared_ptr<ResourcePromise> Promise = std::make_shared<ResourcePromise>();
std::shared_ptr<File> FileData = LoadFile(FilePath);
Promise->File = FileData;
Promise->file = FileData;
if (Promise->File->bHasLoadError)
if (Promise->file->bHasLoadError)
{
Promise->bHasResourceLoaded = true;
}
@ -266,12 +271,13 @@ namespace Ship {
ResourceLoadQueue.push(Promise);
ResourceLoadNotifier.notify_all();
}
} else {
Promise->bHasResourceLoaded = true;
Promise->Resource = resCacheFind->second;
}
return Promise;
return Promise;
}
else
{
return resCacheFind->second;
}
}
std::shared_ptr<std::vector<std::shared_ptr<ResourcePromise>>> ResourceMgr::CacheDirectoryAsync(std::string SearchMask) {
@ -279,10 +285,15 @@ namespace Ship {
auto fileList = OTR->ListFiles(SearchMask);
for (DWORD i = 0; i < fileList.size(); i++) {
auto file = LoadResourceAsync(fileList.operator[](i).cFileName);
if (file != nullptr) {
loadedList->push_back(file);
auto resource = LoadResourceAsync(fileList.operator[](i).cFileName);
if (std::holds_alternative<std::shared_ptr<Resource>>(resource))
{
auto promise = std::make_shared<ResourcePromise>();
promise->bHasResourceLoaded = true;
promise->resource = std::get<std::shared_ptr<Resource>>(resource);
resource = promise;
}
loadedList->push_back(std::get<std::shared_ptr<ResourcePromise>>(resource));
}
return loadedList;
@ -292,37 +303,37 @@ namespace Ship {
auto PromiseList = CacheDirectoryAsync(SearchMask);
auto LoadedList = std::make_shared<std::vector<std::shared_ptr<Resource>>>();
for (int32_t i = 0; i < PromiseList->size(); i++) {
for (size_t i = 0; i < PromiseList->size(); i++) {
auto Promise = PromiseList->at(i);
std::unique_lock<std::mutex> Lock(Promise->ResourceLoadMutex);
std::unique_lock<std::mutex> Lock(Promise->resourceLoadMutex);
while (!Promise->bHasResourceLoaded) {
Promise->ResourceLoadNotifier.wait(Lock);
Promise->resourceLoadNotifier.wait(Lock);
}
LoadedList->push_back(Promise->Resource);
LoadedList->push_back(Promise->resource);
}
return LoadedList;
}
std::shared_ptr<std::vector<std::shared_ptr<Resource>>> ResourceMgr::DirtyDirectory(std::string SearchMask)
std::shared_ptr<std::vector<std::shared_ptr<Resource>>> ResourceMgr::DirtyDirectory(std::string SearchMask)
{
auto PromiseList = CacheDirectoryAsync(SearchMask);
auto LoadedList = std::make_shared<std::vector<std::shared_ptr<Resource>>>();
for (int32_t i = 0; i < PromiseList->size(); i++) {
for (size_t i = 0; i < PromiseList->size(); i++) {
auto Promise = PromiseList->at(i);
std::unique_lock<std::mutex> Lock(Promise->ResourceLoadMutex);
std::unique_lock<std::mutex> Lock(Promise->resourceLoadMutex);
while (!Promise->bHasResourceLoaded) {
Promise->ResourceLoadNotifier.wait(Lock);
Promise->resourceLoadNotifier.wait(Lock);
}
if (Promise->Resource != nullptr)
Promise->Resource->isDirty = true;
if (Promise->resource != nullptr)
Promise->resource->isDirty = true;
LoadedList->push_back(Promise->Resource);
LoadedList->push_back(Promise->resource);
}
return LoadedList;
@ -332,7 +343,7 @@ namespace Ship {
ResourceCache.clear();
}
std::string ResourceMgr::HashToString(uint64_t Hash) {
const std::string* ResourceMgr::HashToString(uint64_t Hash) const {
return OTR->HashToString(Hash);
}
}

View file

@ -4,6 +4,7 @@
#include <string>
#include <thread>
#include <queue>
#include <variant>
#include "Resource.h"
#include "GlobalCtx2.h"
@ -25,17 +26,18 @@ namespace Ship
std::shared_ptr<Archive> GetArchive() { return OTR; }
std::shared_ptr<GlobalCtx2> GetContext() { return Context.lock(); }
std::string HashToString(uint64_t Hash);
const std::string* HashToString(uint64_t Hash) const;
void InvalidateResourceCache();
uint32_t GetGameVersion();
void SetGameVersion(uint32_t newGameVersion);
std::shared_ptr<File> LoadFileAsync(std::string FilePath);
std::shared_ptr<File> LoadFile(std::string FilePath);
std::shared_ptr<Ship::Resource> GetCachedFile(std::string FilePath);
std::shared_ptr<Resource> LoadResource(std::string FilePath);
std::shared_ptr<ResourcePromise> LoadResourceAsync(std::string FilePath);
std::shared_ptr<Ship::Resource> GetCachedFile(const char* FilePath) const;
std::shared_ptr<Resource> LoadResource(const char* FilePath);
std::shared_ptr<Resource> LoadResource(const std::string& FilePath) { return LoadResource(FilePath.c_str()); }
std::variant<std::shared_ptr<Resource>, std::shared_ptr<ResourcePromise>> LoadResourceAsync(const char* FilePath);
std::shared_ptr<std::vector<std::shared_ptr<Resource>>> CacheDirectory(std::string SearchMask);
std::shared_ptr<std::vector<std::shared_ptr<ResourcePromise>>> CacheDirectoryAsync(std::string SearchMask);
std::shared_ptr<std::vector<std::shared_ptr<Resource>>> DirtyDirectory(std::string SearchMask);
@ -48,8 +50,9 @@ namespace Ship
private:
std::weak_ptr<GlobalCtx2> Context;
volatile bool bIsRunning;
std::map<std::string, std::shared_ptr<File>> FileCache;
std::map<std::string, std::shared_ptr<Resource>> ResourceCache;
std::map<std::string, std::shared_ptr<Resource>, std::less<>> ResourceCache;
std::queue<std::shared_ptr<File>> FileLoadQueue;
std::queue<std::shared_ptr<ResourcePromise>> ResourceLoadQueue;
std::shared_ptr<Archive> OTR;
@ -59,7 +62,6 @@ namespace Ship
std::mutex ResourceLoadMutex;
std::condition_variable FileLoadNotifier;
std::condition_variable ResourceLoadNotifier;
volatile bool bIsRunning;
uint32_t gameVersion;
};
}

View file

@ -5,6 +5,8 @@
#include "spdlog/spdlog.h"
#include "stox.h"
#include "Window.h"
#include "Cvar.h"
#include <Utils/StringHelper.h>
extern "C" uint8_t __osMaxControllers;
@ -59,30 +61,36 @@ namespace Ship {
if (Conf[ConfSection]["GUID"].compare("") == 0 || Conf[ConfSection]["GUID"].compare(INVALID_SDL_CONTROLLER_GUID) == 0 || Conf[ConfSection]["GUID"].compare(NewGuid) == 0) {
auto NewCont = SDL_GameControllerOpen(i);
if (SDL_GameControllerHasSensor(NewCont, SDL_SENSOR_GYRO))
{
SDL_GameControllerSetSensorEnabled(NewCont, SDL_SENSOR_GYRO, SDL_TRUE);
}
// We failed to load the controller. Go to next.
if (NewCont == nullptr) {
SPDLOG_ERROR("SDL Controller failed to open: ({})", SDL_GetError());
continue;
}
if (SDL_GameControllerHasSensor(NewCont, SDL_SENSOR_GYRO))
{
SDL_GameControllerSetSensorEnabled(NewCont, SDL_SENSOR_GYRO, SDL_TRUE);
}
guid = NewGuid;
Cont = NewCont;
std::string BindingConfSection = GetBindingConfSection();
std::shared_ptr<ConfigFile> pBindingConf = GlobalCtx2::GetInstance()->GetConfig();
ConfigFile& BindingConf = *pBindingConf.get();
std::string PadConfSection = *GetPadConfSection();
std::shared_ptr<ConfigFile> config = GlobalCtx2::GetInstance()->GetConfig();
if (!BindingConf.has(BindingConfSection)) {
if (!config->has(BindingConfSection)) {
CreateDefaultBinding();
}
if (!config->has(PadConfSection)) {
CreateDefaultPadConf();
}
LoadBinding();
LoadAxisThresholds();
// Update per-controller settings in ImGui menu after opening controller.
Game::LoadPadSettings();
break;
}
@ -93,6 +101,9 @@ namespace Ship {
}
bool SDLController::Close() {
if (CanRumble()) {
SDL_GameControllerRumble(Cont, 0, 0, 0);
}
if (Cont != nullptr) {
SDL_GameControllerClose(Cont);
}
@ -178,37 +189,44 @@ namespace Ship {
if (SDL_GameControllerHasSensor(Cont, SDL_SENSOR_GYRO))
{
size_t contNumber = GetControllerNumber();
float gyroData[3];
SDL_GameControllerGetSensorData(Cont, SDL_SENSOR_GYRO, gyroData, 3);
const char* contName = SDL_GameControllerName(Cont);
const int isSpecialController = !strcmp("PS5 Controller", contName);
const float gyroSensitivity = Game::Settings.controller.gyro_sensitivity;
float gyro_drift_x = CVar_GetFloat(StringHelper::Sprintf("gCont%i_GyroDriftX", contNumber).c_str(), 0.0f);
float gyro_drift_y = CVar_GetFloat(StringHelper::Sprintf("gCont%i_GyroDriftY", contNumber).c_str(), 0.0f);
const float gyro_sensitivity = CVar_GetFloat(StringHelper::Sprintf("gCont%i_GyroSensitivity", contNumber).c_str(), 1.0f);
if (Game::Settings.controller.gyroDriftX == 0) {
Game::Settings.controller.gyroDriftX = gyroData[0];
if (gyro_drift_x == 0) {
gyro_drift_x = gyroData[0];
}
if (Game::Settings.controller.gyroDriftY == 0) {
if (gyro_drift_y == 0) {
if (isSpecialController == 1) {
Game::Settings.controller.gyroDriftY = gyroData[2];
gyro_drift_y = gyroData[2];
}
else {
Game::Settings.controller.gyroDriftY = gyroData[1];
gyro_drift_y = gyroData[1];
}
}
CVar_SetFloat(StringHelper::Sprintf("gCont%i_GyroDriftX", contNumber).c_str(), gyro_drift_x);
CVar_SetFloat(StringHelper::Sprintf("gCont%i_GyroDriftY", contNumber).c_str(), gyro_drift_y);
if (isSpecialController == 1) {
wGyroX = gyroData[0] - Game::Settings.controller.gyroDriftX;
wGyroY = -gyroData[2] - Game::Settings.controller.gyroDriftY;
wGyroX = gyroData[0] - gyro_drift_x;
wGyroY = -gyroData[2] - gyro_drift_y;
}
else {
wGyroX = gyroData[0] - Game::Settings.controller.gyroDriftX;
wGyroY = gyroData[1] - Game::Settings.controller.gyroDriftY;
wGyroX = gyroData[0] - gyro_drift_x;
wGyroY = gyroData[1] - gyro_drift_y;
}
wGyroX *= gyroSensitivity;
wGyroY *= gyroSensitivity;
wGyroX *= gyro_sensitivity;
wGyroY *= gyro_sensitivity;
}
for (int32_t i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++) {
@ -329,9 +347,12 @@ namespace Ship {
void SDLController::WriteToSource(ControllerCallback* controller)
{
if (SDL_GameControllerHasRumble(Cont)) {
if (CanRumble()) {
if (controller->rumble > 0) {
SDL_GameControllerRumble(Cont, 0xFFFF * Game::Settings.controller.rumble_strength, 0xFFFF * Game::Settings.controller.rumble_strength, 1);
float rumble_strength = CVar_GetFloat(StringHelper::Sprintf("gCont%i_RumbleStrength", GetControllerNumber()).c_str(), 1.0f);
SDL_GameControllerRumble(Cont, 0xFFFF * rumble_strength, 0xFFFF * rumble_strength, 0);
} else {
SDL_GameControllerRumble(Cont, 0, 0, 0);
}
}
@ -391,6 +412,14 @@ namespace Ship {
Conf.Save();
}
void SDLController::CreateDefaultPadConf() {
std::string ConfSection = *GetPadConfSection();
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
ConfigFile& Conf = *pConf.get();
Conf.Save();
}
void SDLController::SetButtonMapping(const std::string& szButtonName, int32_t dwScancode) {
if (guid.compare(INVALID_SDL_CONTROLLER_GUID)) {
return;
@ -410,4 +439,8 @@ namespace Ship {
std::string SDLController::GetBindingConfSection() {
return GetControllerType() + " CONTROLLER BINDING " + guid;
}
std::optional<std::string> SDLController::GetPadConfSection() {
return GetControllerType() + " CONTROLLER PAD " + guid;
}
}

View file

@ -12,20 +12,31 @@ namespace Ship {
void ReadFromSource();
void WriteToSource(ControllerCallback* controller);
bool Connected() const { return Cont != nullptr; }
bool CanRumble() const {
#if SDL_COMPILEDVERSION >= SDL_VERSIONNUM(2,0,18)
return SDL_GameControllerHasRumble(Cont);
#endif
return true;
}
std::string GetGuid() { return guid; };
bool HasPadConf() const { return true; }
std::optional<std::string> GetPadConfSection();
protected:
std::string GetControllerType();
void SetButtonMapping(const std::string& szButtonName, int32_t dwScancode);
std::string GetConfSection();
std::string GetBindingConfSection();
void CreateDefaultBinding();
void CreateDefaultPadConf();
static bool IsGuidInUse(const std::string& guid);
private:
std::string guid;
SDL_GameController* Cont;
std::string guid;
std::map<int32_t, int16_t> ThresholdMapping;
void LoadAxisThresholds();

View file

@ -137,8 +137,8 @@ namespace Ship
y = 0;
z = 0;
unk_06 = 0;
opa;
xlu;
// opa;
// xlu;
}
SetMesh::SetMesh(BinaryReader* reader) : SceneCommand(reader)
@ -398,18 +398,18 @@ namespace Ship
LightInfo light = LightInfo();
light.type = reader->ReadUByte();
light.x = reader->ReadInt16();
light.y = reader->ReadInt16();
light.y = reader->ReadInt16();
light.z = reader->ReadInt16();
light.r = reader->ReadUByte();
light.g = reader->ReadUByte();
light.b = reader->ReadUByte();
light.drawGlow = reader->ReadUByte();
light.radius = reader->ReadInt16();
lights.push_back(light);
}
}

View file

@ -15,7 +15,7 @@ namespace Ship
limb->skinVtxCnt = reader->ReadUInt16();
uint32_t skinCnt = reader->ReadUInt32();
for (int i = 0; i < skinCnt; i++)
for (size_t i = 0; i < skinCnt; i++)
{
Struct_800A598C struc;

View file

@ -16,7 +16,7 @@ std::map<ImGuiKey, std::string> BindingToggle;
static bool HelpCommand(const std::vector<std::string>&) {
INFO("SoH Commands:");
for(const auto& cmd : SohImGui::console->Commands) {
INFO((" - " + cmd.first).c_str());
INFO("%s", (" - " + cmd.first).c_str());
}
return CMD_SUCCESS;
}
@ -35,7 +35,7 @@ std::string toLowerCase(std::string in) {
static bool BindCommand(const std::vector<std::string>& args) {
if(args.size() > 2) {
const ImGuiIO* io = &ImGui::GetIO();;
for (int k = 0; k < std::size(io->KeysData); k++) {
for (size_t k = 0; k < std::size(io->KeysData); k++) {
std::string key(ImGui::GetKeyName(k));
if(toLowerCase(args[1]) == toLowerCase(key)) {
@ -55,7 +55,7 @@ static bool BindCommand(const std::vector<std::string>& args) {
static bool BindToggleCommand(const std::vector<std::string>& args) {
if (args.size() > 2) {
const ImGuiIO* io = &ImGui::GetIO();;
for (int k = 0; k < std::size(io->KeysData); k++) {
for (size_t k = 0; k < std::size(io->KeysData); k++) {
std::string key(ImGui::GetKeyName(k));
if (toLowerCase(args[1]) == toLowerCase(key)) {
@ -92,7 +92,7 @@ void Console::Update() {
}
for (auto [key, var] : BindingToggle) {
if (ImGui::IsKeyPressed(key)) {
CVar* cvar = CVar_GetVar(const_cast<char*>(var.c_str()));
CVar* cvar = CVar_Get(var.c_str());
Dispatch("set " + var + " " + std::to_string(cvar == nullptr ? 0 : !static_cast<bool>(cvar->value.valueS32)));
}
}
@ -106,7 +106,7 @@ void Console::Draw() {
if (!this->opened) return;
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
ImGui::Begin("Console", &this->opened);
ImGui::Begin("Console", nullptr, ImGuiWindowFlags_NoFocusOnAppearing);
const ImVec2 pos = ImGui::GetWindowPos();
const ImVec2 size = ImGui::GetWindowSize();
@ -177,8 +177,10 @@ void Console::Draw() {
for (const auto& filter : priority_filters) {
const bool is_selected = (filter == std::string(this->level_filter));
if (ImGui::Selectable(filter.c_str(), is_selected))
{
this->level_filter = filter;
if (is_selected) ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
@ -194,7 +196,7 @@ void Console::Draw() {
if (ImGui::BeginTable("History", 1)) {
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
if (this->selectedId < this->Log.size() - 1) ++this->selectedId;
if (this->selectedId < (int)this->Log.size() - 1) ++this->selectedId;
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
if (this->selectedId > 0) --this->selectedId;
@ -226,7 +228,7 @@ void Console::Draw() {
ImGui::EndChild();
// Renders input textfield
constexpr ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackEdit |
constexpr ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackEdit |
ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
ImGui::PushItemWidth(-1);
if(ImGui::InputTextWithHint("CMDInput", ">", this->InputBuffer, MAX_BUFFER_SIZE, flags, &Console::CallbackStub, this)) {
@ -317,7 +319,7 @@ int Console::CallbackStub(ImGuiInputTextCallbackData* data) {
return 0;
}
void Console::Append(const std::string& channel, Priority priority, const char* fmt, ...) IM_FMTARGS(4) {
void Console::Append(const std::string& channel, Priority priority, const char* fmt, ...) {
char buf[1024];
va_list args;
va_start(args, fmt);

View file

@ -7,10 +7,10 @@
#include "Lib/ImGui/imgui.h"
#define LOG(msg, ...) SohImGui::console->Append("Main", Priority::LOG_LVL, msg, __VA_ARGS__)
#define INFO(msg, ...) SohImGui::console->Append("Main", Priority::INFO_LVL, msg, __VA_ARGS__)
#define WARNING(msg, ...) SohImGui::console->Append("Main", Priority::WARNING_LVL, msg, __VA_ARGS__)
#define ERROR(msg, ...) SohImGui::console->Append("Main", Priority::ERROR_LVL, msg, __VA_ARGS__)
#define LOG(msg, ...) SohImGui::console->Append("Main", Priority::LOG_LVL, msg, ##__VA_ARGS__)
#define INFO(msg, ...) SohImGui::console->Append("Main", Priority::INFO_LVL, msg, ##__VA_ARGS__)
#define WARNING(msg, ...) SohImGui::console->Append("Main", Priority::WARNING_LVL, msg, ##__VA_ARGS__)
#define ERROR(msg, ...) SohImGui::console->Append("Main", Priority::ERROR_LVL, msg, ##__VA_ARGS__)
#define CMD_SUCCESS true
#define CMD_FAILED false
#define MAX_BUFFER_SIZE 255
@ -75,7 +75,7 @@ public:
void Init();
void Update();
void Draw();
void Append(const std::string& channel, Priority priority, const char* fmt, ...);
void Append(const std::string& channel, Priority priority, const char* fmt, ...) IM_FMTARGS(4);
void Dispatch(const std::string& line);
static int CallbackStub(ImGuiInputTextCallbackData* data);
};

View file

@ -24,7 +24,7 @@ namespace ModInternal {
bool handleHook(std::shared_ptr<HookCall> call) {
std::string hookName = std::string(call->name);
for (int l = 0; l < listeners[hookName].size(); l++) {
for (size_t l = 0; l < listeners[hookName].size(); l++) {
(listeners[hookName][l])(call);
}
return call->cancelled;

View file

@ -47,6 +47,7 @@ struct HookParameter {
#include <functional>
#include <string>
#include <map>
#include <memory>
struct HookCall {
std::string name;

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,7 @@
#pragma once
#include "GameOverlay.h"
#include "Lib/ImGui/imgui.h"
#include "SohConsole.h"
struct GameAsset {
@ -47,11 +49,35 @@ namespace SohImGui {
} sdl;
} EventImpl;
extern WindowImpl impl;
using WindowDrawFunc = void(*)(bool& enabled);
typedef struct {
bool enabled;
WindowDrawFunc drawFunc;
} CustomWindow;
extern Console* console;
extern Ship::GameOverlay* overlay;
extern bool needs_save;
void Init(WindowImpl window_impl);
void Update(EventImpl event);
void Draw(void);
void EnhancementRadioButton(std::string text, std::string cvarName, int value);
void EnhancementCheckbox(std::string text, std::string cvarName);
void EnhancementSliderInt(std::string text, std::string id, std::string cvarName, int min, int max, std::string format);
void EnhancementSliderFloat(std::string text, std::string id, std::string cvarName, float min, float max, std::string format, float defaultValue);
void DrawMainMenuAndCalculateGameSize(void);
void DrawFramebufferAndGameInput(void);
void Render(void);
void CancelFrame(void);
void ShowCursor(bool hide, Dialogues w);
void BindCmd(const std::string& cmd, CommandEntry entry);
void* GetTextureByID(int id);
void AddWindow(const std::string& category, const std::string& name, WindowDrawFunc drawFunc);
void LoadResource(const std::string& name, const std::string& path, const ImVec4& tint = ImVec4(1, 1, 1, 1));
ImTextureID GetTextureByID(int id);
ImTextureID GetTextureByName(const std::string& name);
}

View file

@ -43,7 +43,7 @@ namespace Ship {
if (raw_path == nullptr) return;
const auto api = BIND_PTR("gfx_api", GfxRenderingAPI*);
const auto path = normalize(raw_path) + ".png";
const auto path = std::string(raw_path) + ".png";
const auto node = BIND_PTR("node", TextureCacheNode**);
const auto fmt = BIND_VAR("fmt", uint32_t*);
const auto siz = BIND_VAR("siz", uint32_t*);

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