Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/PATCHES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ each site), so they also work on other builds whose code matches.
| **Bug Fix - Texture Corruption Fix** | Fixes corrupted textures by giving the game a larger video arena (more video memory) | Applied whenever the render resolution is **Native**; a resolution patch replaces it | cuesta4 |
| **Skip Any Video With X Button** | Lets you skip any cinematic with Cross | **Game** page, on by default | cuesta4 |
| **Skip Intro** | Skips the logos at start | **Game** page, off by default | cuesta4 |
| **Resolution Patch - <size>** (21 sizes, from 480p to 5120 × 2160) | Sets the render resolution and the video arena; the ultrawide sizes also widen the field of view (Hor+) | **Render resolution** (launcher **Performance** page, or the in-game menu); one at a time | kvicken + cuesta4 (480p, 720p, 1440p, 1800p, 4K); SpartanPort (900p and the ultrawide, 16:10 and 4:3 sizes) |
| **Resolution Patch - <size>** (25 sizes, from 480p to 8K, with 11520 × 2160 for three screens side by side) | Sets the render resolution and the video arena; the ultrawide sizes also widen the field of view (Hor+) | **Render resolution** (launcher **Performance** page, or the in-game menu); one at a time | kvicken + cuesta4 (480p, 720p, 1440p, 1800p, 4K); SpartanPort (900p and the ultrawide, 48:9, 16:10 and 4:3 sizes) |
| **Frame Rate Patch - 240 FPS** | Raises the engine's frame rate limit and corrects the swing energy so it matches the original 60 Hz simulation | Applied on every start; the chosen **Engine frame rate** (60, 100, 120, 144, 180 or 240) is then written live | kvicken, SpartanPort |
| Frame Rate Patch - 120 FPS / 144 FPS / 100 FPS | The same patch with a limit of 120, 144 or 100 | Not used by the launcher any more; kept for other uses of the file | kvicken / kvicken, SpartanPort |

Expand Down
4 changes: 2 additions & 2 deletions gpu/shim/spartanport_graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ inline constexpr std::array<int, 8> FpsLimits = {30, 60, 100, 120, 144, 180, 240
/// Game patch suffixes ("Resolution Patch - <name>"); "native" is no resolution patch.
/// The ones after 900p are the Ultrawide mod's sizes (#202) and the 16:10 and 4:3 ones, built in as beta
/// (scripts/graphics_options.py).
inline constexpr std::array<std::string_view, 25> Resolutions = {
inline constexpr std::array<std::string_view, 26> Resolutions = {
"native", "480p", "720p", "900p", "1440p", "1800p", "4K",
"2560x1080", "3440x1440", "3840x1600", "5120x1440", "1920x1200", "2560x1600", "1280x800",
"1440x900", "1360x768",
"1280x960", "1440x1080", "1920x1440", "2048x1536", "2560x1920",
"5120x2160", "2880x1800", "5120x2880", "8K"};
"5120x2160", "2880x1800", "5120x2880", "11520x2160", "8K"};
/// The resolutions to choose from: the built-in ones, then those of the enabled mods' patch files
/// (SPARTAN_EXTRA_RESOLUTIONS, ";"-separated sizes such as "3440x1440"; run.py sets it at every start).
inline std::vector<std::string> ResolutionNames() {
Expand Down
2 changes: 1 addition & 1 deletion gpu/shim/spartanport_overlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ void PerformanceTab() {
"1360 × 768 · 16:9",
"1280 × 960 · 4:3", "1440 × 1080 · 4:3 (beta)", "1920 × 1440 · 4:3",
"2048 × 1536 · 4:3 (beta)", "2560 × 1920 · 4:3 (beta)", "5120 × 2160 · 21:9",
"2880 × 1800 · 16:10 (beta)", "5120 × 2880 · 16:9 (beta)",
"2880 × 1800 · 16:10 (beta)", "5120 × 2880 · 16:9 (beta)", "11520 × 2160 · 48:9 (beta)",
"8K · 7680 × 4320 · 16:9 (beta)"};
static_assert(std::size(builtin_labels) == G::Resolutions.size());
const std::vector<std::string> resolution_names = G::ResolutionNames();
Expand Down
8 changes: 4 additions & 4 deletions gpu/shim/spartanport_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -806,9 +806,9 @@ extern "C" uint64_t spartanportgpu_native_setting(unsigned function) {
const auto names = SpartanGraphics::ResolutionNames();
const auto aspect = [](const std::string& name) {
const auto [w, h] = SpartanGraphics::ResolutionSize(name);
const char* labels[] = {"16:9", "16:10", "21:9", "32:9", "4:3"};
const double ratios[] = {16.0/9, 1.6, 2.37, 32.0/9, 4.0/3};
for (unsigned i = 0; i < 5; ++i) if (std::abs(double(w)/h - ratios[i]) < 0.05)
const char* labels[] = {"16:9", "16:10", "21:9", "32:9", "48:9", "4:3"};
const double ratios[] = {16.0/9, 1.6, 2.37, 32.0/9, 48.0/9, 4.0/3};
for (unsigned i = 0; i < 6; ++i) if (std::abs(double(w)/h - ratios[i]) < 0.05)
return std::string(labels[i]);
const int divisor = std::gcd(w, h);
return std::to_string(w/divisor) + ":" + std::to_string(h/divisor);
Expand All @@ -827,7 +827,7 @@ extern "C" uint64_t spartanportgpu_native_setting(unsigned function) {
};
if (index <= 1) {
std::vector<std::string> aspects;
for (const char* a : {"16:9", "16:10", "21:9", "32:9", "4:3"}) aspects.emplace_back(a);
for (const char* a : {"16:9", "16:10", "21:9", "32:9", "48:9", "4:3"}) aspects.emplace_back(a);
for (const auto& name : names) {
const auto a = aspect(name);
if (std::find(aspects.begin(), aspects.end(), a) == aspects.end()) aspects.push_back(a);
Expand Down
11 changes: 10 additions & 1 deletion launcher-rust/src/core/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ pub static RESOLUTIONS: &[&str] = &[
"5120x2160",
"2880x1800",
"5120x2880",
"11520x2160",
"8K",
];

Expand Down Expand Up @@ -341,6 +342,7 @@ const ASPECTS: &[(&str, f64)] = &[
("16:10", 16.0 / 10.0),
("21:9", 2.37),
("32:9", 32.0 / 9.0),
("48:9", 48.0 / 9.0),
("4:3", 4.0 / 3.0),
];

Expand Down Expand Up @@ -517,6 +519,12 @@ mod tests {
assert_eq!(aspect_of("2560x1080").as_deref(), Some("21:9"));
assert_eq!(aspect_of("bogus"), None);
assert_eq!(aspect_of("1000x800").as_deref(), Some("5:4"));
// #719: three 4K screens side by side.
assert_eq!(aspect_of("11520x2160").as_deref(), Some("48:9"));
assert_eq!(
resolutions_for("48:9", &strings(RESOLUTIONS)),
["11520x2160"]
);
}

#[test]
Expand Down Expand Up @@ -577,7 +585,7 @@ mod tests {
}
let names: HashSet<_> = RESOLUTIONS.iter().collect();
assert_eq!(names.len(), RESOLUTIONS.len());
assert_eq!(RESOLUTIONS.len(), 25);
assert_eq!(RESOLUTIONS.len(), 26);
}

#[test]
Expand Down Expand Up @@ -609,6 +617,7 @@ mod tests {
"2560x1920",
"2880x1800",
"5120x2880",
"11520x2160",
"8K"
]
);
Expand Down
47 changes: 47 additions & 0 deletions patches/CUSA01623.xml
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,53 @@
<Target Name="second3" Address="0x005c9941" Line="28"/>
<Target Name="back3" Address="0x005cc0f7" Pattern="e8 ?? ?? ?? ?? c5 fa 5e 85 2c ff ff ff c5 f9 70 c0 00" At="13"/>
</Metadata>
<Metadata Title="SpartanPort" Name="Resolution Patch - 11520x2160" Note="Triple-screen 11520x2160 (48:9, three 4K screens side by side, 5.333:1), Hor+ (#719). Sets the game output resolution and the display aspect ratio, widens the horizontal field of view so the 16:9 vertical view is kept (no zoom), uses single-sample scene buffers to leave room for textures and sets the Video Arena to 3.98 GiB, the largest the game's 32-bit field can hold. Requires setting DMEM to 6144 MB or more. Only leave one resolution patch enabled; do not enable it with Bug Fix - Texture Corruption Fix. With the upscaler off it needs more than 12 GB of video memory, as 8K does; with FSR or another upscaler on it renders smaller and fits. Not tested on three screens yet: please report how it looks." Author="SpartanPort ultrawide" PatchVer="1.0" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
<PatchList>
<Line Type="bytes" Address="0x005f0b3b" Value="002d0000" Pattern="48 8d 75 a0 89 c7 e8 ?? ?? ?? ?? 41 c7 45 0c 80 07 00 00" At="15"/>
<Line Type="bytes" Address="0x005f0b43" Value="70080000" Pattern="41 c7 45 10 38 04 00 00 66 41 c7 45 14 80 07 66 41 c7 45 16 38 04" At="4"/>
<Line Type="bytes" Address="0x005f0b4c" Value="002d" Pattern="41 c7 45 10 38 04 00 00 66 41 c7 45 14 80 07 66 41 c7 45 16 38 04" At="13"/>
<Line Type="bytes" Address="0x005f0b53" Value="7008" Pattern="41 c7 45 10 38 04 00 00 66 41 c7 45 14 80 07 66 41 c7 45 16 38 04" At="20"/>
<Line Type="bytes" Address="0x005643f3" Value="70080000" Pattern="c7 04 24 38 04 00 00 48 8d 3d ?? ?? ?? ?? 41 b9 80 07 00 00" At="3"/>
<Line Type="bytes" Address="0x00564400" Value="002d0000" Pattern="41 b9 80 07 00 00 4c 89 f9 4d 89 e0 e8 ?? ?? ?? ??" At="2"/>
<Line Type="bytes" Address="0x005ff769" Value="002d0000" Pattern="bf 00 01 00 00 b9 80 07 00 00 41 b8 38 04 00 00" At="6"/>
<Line Type="bytes" Address="0x005ff76f" Value="70080000" Pattern="bf 00 01 00 00 b9 80 07 00 00 41 b8 38 04 00 00" At="12"/>
<Line Type="bytes" Address="0x007c46b4" Value="002d0000" Pattern="ff ff ff ff 01 00 00 00 80 07 00 00 38 04 00 00 0a e0 f2 00" At="8"/>
<Line Type="bytes" Address="0x007c46b8" Value="70080000" Pattern="01 00 00 00 80 07 00 00 38 04 00 00 0a e0 f2 00 0a e0 f2 00" At="8"/>
<Line Type="bytes" Address="0x0056bb82" Value="70080000" Pattern="e8 ?? ?? ?? ?? 4c 89 24 24 ba 01 00 00 00 41 b8 38 04 00 00" At="16"/>
<Line Type="bytes" Address="0x0060953d" Value="70080000" Pattern="83 39 00 74 19 48 8b 78 38 ba 38 04 00 00 48 89 de" At="10"/>
<Line Type="bytes" Address="0x007f2a3c" Value="80160000" Pattern="00 00 00 00 0a e0 f2 00 c0 03 00 00 1c 02 00 00 01 00 00 00" At="8"/>
<Line Type="bytes" Address="0x007f2a40" Value="38040000" Pattern="0a e0 f2 00 c0 03 00 00 1c 02 00 00 01 00 00 00 00 00 00 00" At="8"/>
<Line Type="bytes" Address="0x007cd918" Value="000000ff" Pattern="00 00 00 43 00 00 80 3f 00 00 00 50 00 00 00 00 00 04 00 00" At="8"/>
<Line Type="bytes" Address="0x005f0b57" Value="abaaaa40abaaaa40" Pattern="48 b8 39 8e e3 3f 39 8e e3 3f 49 89 45 00 b9 00 00 01 00" At="2"/>
<Line Type="bytes" Address="0x005c03c1" Value="00004040" Pattern="c3 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 55 48 89 e5 41 57 41 56 41 55 41 54 53 48 81 ec 18 01 00 00" At="1"/>
<Line Type="bytes" Address="0x005cbeff" Value="e9{rel:first0:0}0f1f00" Pattern="c4 c1 78 29 47 10 c4 c1 78 29 07 c5 fa 10 89 d8 00 00 00" At="11"/>
<Line Type="bytes" Address="0x005c0c31" Value="c5fa1089d8000000e9{rel:second0:0}" Pattern="0f 47 c1 c3 66 90 c3 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 c3" At="7"/>
<Line Type="bytes" Address="0x005c0c41" Value="c5f2590d{rel:K:0}e9{rel:back0:0}" Pattern="c3 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 00 90 66 66 66 66" At="1"/>
<Line Type="bytes" Address="0x005cbc69" Value="e9{rel:first1:0}0f1f00" Pattern="f6 80 91 00 00 00 40 74 51 c5 fa 10 80 d8 00 00 00" At="9"/>
<Line Type="bytes" Address="0x005c3311" Value="c5fa1080d8000000e9{rel:second1:0}" Pattern="c3 0f 1f 44 00 00 c3 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 c3" At="7"/>
<Line Type="bytes" Address="0x005c3321" Value="c5fa5905{rel:K:0}e9{rel:back1:0}" Pattern="c3 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 8b 57 08" At="1"/>
<Line Type="bytes" Address="0x005cc989" Value="e9{rel:first2:0}0f1f00" Pattern="74 4c c5 fa 10 80 d8 00 00 00 c4 c1 7a 5e 0c 24" At="2"/>
<Line Type="bytes" Address="0x005c3d41" Value="c5fa1080d8000000e9{rel:second2:0}" Pattern="eb 1b 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 ff c0" At="2"/>
<Line Type="bytes" Address="0x005c62e1" Value="c5fa5905{rel:K:0}e9{rel:back2:0}" Pattern="5b 41 5e 41 5f 5d e9 ?? ?? ?? ?? 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 55 48 89 e5 41 57 41 56 53 50 41 89 f6 48 89 fb" At="11"/>
<Line Type="bytes" Address="0x005cc0ef" Value="e9{rel:first3:0}0f1f00" Pattern="e8 ?? ?? ?? ?? c5 fa 5e 85 2c ff ff ff c5 f9 70 c0 00" At="5"/>
<Line Type="bytes" Address="0x005c6341" Value="c5fa5e852cffffffe9{rel:second3:0}" Pattern="c3 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 00 00 00 00 31 c0" At="1"/>
<Line Type="bytes" Address="0x005c9941" Value="c5fa5905{rel:K:0}e9{rel:back3:0}" Pattern="eb 12 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 4d 8b 3f" At="2"/>
<Line Type="bytes" Address="0x0055a876" Value="9090" Pattern="48 8d 05 ?? ?? ?? ?? 83 38 00 74 1e 41 c7 44 24 7c 00 00 00 00" At="10"/>
</PatchList>
<Target Name="K" Address="0x005c03c1" Line="16"/>
<Target Name="first0" Address="0x005c0c31" Line="18"/>
<Target Name="second0" Address="0x005c0c41" Line="19"/>
<Target Name="back0" Address="0x005cbf07" Pattern="c4 c1 78 29 47 10 c4 c1 78 29 07 c5 fa 10 89 d8 00 00 00" At="19"/>
<Target Name="first1" Address="0x005c3311" Line="21"/>
<Target Name="second1" Address="0x005c3321" Line="22"/>
<Target Name="back1" Address="0x005cbc71" Pattern="f6 80 91 00 00 00 40 74 51 c5 fa 10 80 d8 00 00 00" At="17"/>
<Target Name="first2" Address="0x005c3d41" Line="24"/>
<Target Name="second2" Address="0x005c62e1" Line="25"/>
<Target Name="back2" Address="0x005cc991" Pattern="74 4c c5 fa 10 80 d8 00 00 00 c4 c1 7a 5e 0c 24" At="10"/>
<Target Name="first3" Address="0x005c6341" Line="27"/>
<Target Name="second3" Address="0x005c9941" Line="28"/>
<Target Name="back3" Address="0x005cc0f7" Pattern="e8 ?? ?? ?? ?? c5 fa 5e 85 2c ff ff ff c5 f9 70 c0 00" At="13"/>
</Metadata>
<Metadata Title="SpartanPort" Name="Resolution Patch - 2048x1536" Note="2048x1536 (1.333:1). Sets the game output resolution and the display aspect ratio; the horizontal field of view is the game's own, so this taller picture shows more at the top and bottom and sets the Video Arena to 2.75 GiB. Requires setting DMEM to 3072 MB or more. Only leave one resolution patch enabled; do not enable it with Bug Fix - Texture Corruption Fix. Not tested in game yet: please report how it looks." Author="SpartanPort ultrawide" PatchVer="1.0" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
<PatchList>
<Line Type="bytes" Address="0x005f0b3b" Value="00080000" Pattern="48 8d 75 a0 89 c7 e8 ?? ?? ?? ?? 41 c7 45 0c 80 07 00 00" At="15"/>
Expand Down
4 changes: 2 additions & 2 deletions scripts/graphics_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
'2560x1080', '3440x1440', '3840x1600', '5120x1440', '1920x1200', '2560x1600', '1280x800',
'1440x900', '1360x768',
'1280x960', '1440x1080', '1920x1440', '2048x1536', '2560x1920',
'5120x2160', '2880x1800', '5120x2880', '8K')
'5120x2160', '2880x1800', '5120x2880', '11520x2160', '8K')
# The ultrawide, 16:10 and 4:3 sizes of the Ultrawide mod (#202), built in: beta until more screens confirm
# them, except the ones tested in game (the maintainer's own tests and players' reports in issues #180, #505
# and #519): 2560x1080, 1440x1080, 2048x1536, 2560x1920 and 8K are still beta.
Expand All @@ -24,7 +24,7 @@
BETA_RESOLUTIONS = tuple(r for r in RESOLUTIONS[7:] if r not in TESTED_RESOLUTIONS)
SIZES = {'native': (1920, 1080), '480p': (854, 480), '720p': (1280, 720), '900p': (1600, 900),
'1440p': (2560, 1440), '1800p': (3200, 1800), '4K': (3840, 2160), '8K': (7680, 4320)}
ASPECTS = (('16:9', 16 / 9), ('16:10', 16 / 10), ('21:9', 2.37), ('32:9', 32 / 9), ('4:3', 4 / 3))
ASPECTS = (('16:9', 16 / 9), ('16:10', 16 / 10), ('21:9', 2.37), ('32:9', 32 / 9), ('48:9', 48 / 9), ('4:3', 4 / 3))
ENGINE_FPS = (60, 100, 120, 144, 180, 240)
# The Steam Deck's DMI names: LCD (Jupiter) and OLED (Galileo), both with a 1280 × 800 screen (#855).
STEAM_DECK_PRODUCTS = ('Jupiter', 'Galileo')
Expand Down
5 changes: 3 additions & 2 deletions tests/test_graphics_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,9 @@ def test_aspect_of_a_resolution(self):
self.assertEqual(g.aspect_of('1280x1024'), '5:4') # a mod's size outside the named ones

def test_aspects_in_a_fixed_order_with_the_others_last(self):
self.assertEqual(g.aspects(g.RESOLUTIONS), ['16:9', '16:10', '21:9', '32:9', '4:3'])
self.assertEqual(g.aspects(g.resolutions(('1280x1024',))), ['16:9', '16:10', '21:9', '32:9', '4:3', '5:4'])
self.assertEqual(g.aspects(g.RESOLUTIONS), ['16:9', '16:10', '21:9', '32:9', '48:9', '4:3'])
self.assertEqual(g.resolutions_for('48:9', g.RESOLUTIONS), ['11520x2160']) # #719
self.assertEqual(g.aspects(g.resolutions(('1280x1024',))), ['16:9', '16:10', '21:9', '32:9', '48:9', '4:3', '5:4'])

def test_resolutions_of_an_aspect_smallest_first_native_on_top(self):
self.assertEqual(g.resolutions_for('16:9', g.RESOLUTIONS),
Expand Down
8 changes: 5 additions & 3 deletions tests/test_graphics_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,12 @@ int main() {
assert(G::ParseMonitor(std::to_string(std::numeric_limits<int>::max() + 1LL)) == G::PrimaryMonitor);
assert(G::ParseMonitor("99999999999999999999") == G::PrimaryMonitor);
assert(G::ParseFpsLimit(240) == 240 && G::ParseFpsLimit(144) == 144 && G::ParseFpsLimit(180) == 180 && G::ParseFpsLimit(100) == 100 && G::ParseFpsLimit(75) == 0);
assert(G::ParseResolution("900p") == 3 && G::ParseResolution("1440p") == 4 && G::ParseResolution("8K") == 24);
assert(G::ParseResolution("900p") == 3 && G::ParseResolution("1440p") == 4 && G::ParseResolution("8K") == 25);
// #964, #1038: 2880x1800 (16:10) and 5120x2880 (5K, 16:9).
assert(G::ParseResolution("2880x1800") == 22 && G::ParseResolution("5120x2880") == 23);
assert(G::ResolutionSize("5120x2880") == std::make_pair(5120, 2880));
// #719: 11520x2160 (48:9, three 4K screens).
assert(G::ParseResolution("11520x2160") == 24 && G::ResolutionSize("11520x2160") == std::make_pair(11520, 2160));
// #238: 900p (1600x900) sits between 720p and 1440p in the built-in list.
assert(G::Resolutions[3] == "900p" && G::Resolutions[2] == "720p" && G::Resolutions[4] == "1440p");
// The window's starting size (window.cpp): built-in names, a mod's size, native otherwise.
Expand Down Expand Up @@ -346,13 +348,13 @@ int main() {
// ones and keep their names through a load and a save; repeated or built-in names are not added twice.
SetEnvironment("SPARTAN_EXTRA_RESOLUTIONS", "3840x1080;2880x1200;;2880x1200;4K;3440x1440");
const auto names = G::ResolutionNames();
assert(names.size() == 27 && names[24] == "8K" && names[25] == "3840x1080" && names[26] == "2880x1200");
assert(names.size() == 28 && names[25] == "8K" && names[26] == "3840x1080" && names[27] == "2880x1200");
{
std::ofstream out(path);
out << "render_resolution=2880x1200\n";
}
SpartanSettings::Load();
assert(v.render_resolution == 26);
assert(v.render_resolution == 27);
SpartanSettings::Save();
assert(Read(path).find("render_resolution=2880x1200\n") != std::string::npos);
SetEnvironment("SPARTAN_EXTRA_RESOLUTIONS", ""); // the mod is off: its resolution is unknown, so native
Expand Down
Loading