diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index ca038a2c..91df7491 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -26,7 +26,7 @@ #define DISABLED 0 #define DEACTIVATED_GPU_FREQ 2000 #define GPU_MIN_MIN_VOLT 480000 -#define CPU_MAX_MAX_VOLT 1235000 +#define CPU_MAX_MAX_VOLT 1200000 namespace ams::ldr::hoc { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp index f562c54a..ec75c8d4 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp @@ -148,7 +148,7 @@ namespace ams::ldr::hoc::pcv { { C.commonEmcMemVolt, 912'500, 1350'000 }, // Official burst vmax for the RAMs is 1500mV { C.eristaCpuMaxVolt, 1000, 1260 }, { GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000 }, - { C.marikoCpuMaxVolt, 1000, 1235 }, + { C.marikoCpuMaxVolt, 1000, 1200 }, { C.marikoEmcMaxClock, 1600'000, 3500'000 }, { C.marikoEmcVddqVolt, 250'000, 700'000 }, { eristaCpuDvfsMaxFreq, 1785'000, 2295'000 }, diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index 45f78307..148baf08 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -202,11 +202,11 @@ namespace ams::ldr::hoc::pcv { { }, }; - constexpr u32 CpuVoltOfficial = 1235; + constexpr u32 CpuVoltOfficial = 1227; constexpr u32 CpuVminOfficial = 825; - constexpr u32 CpuVoltL4T = 1235'000; + constexpr u32 CpuVoltL4T = 1257'000; static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 }; static const u32 cpuVoltDvfsOffsets[] = { 5, 6, 7, 8, 9 }; @@ -231,7 +231,6 @@ namespace ams::ldr::hoc::pcv { static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 }; static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "invalid gpuVoltageThermalPattern size"); - /* GPU Max Clock asm Pattern: * * MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11) diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp index 716975cb..2496a20b 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -652,9 +652,8 @@ namespace ams::ldr::hoc::pcv::mariko { // Copy unmodified 1600000 table to tmp std::memcpy(reinterpret_cast(tmp), reinterpret_cast(table_max), sizeof(MarikoMtcTable)); - // Adjust max freq mtc timing parameters with reference to 1331200 table - /* TODO: Implement mariko */ - + + /* Adjust timings properly according to the new frequency. */ MemMtcTableAutoAdjust(table_max); MemMtcPllmbDivisor(table_max); @@ -683,6 +682,7 @@ namespace ams::ldr::hoc::pcv::mariko { #define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000), + /* TODO: More fine tuned values? */ if (C.marikoEmcMaxClock < 1862400) { std::memcpy(new_start, default_end, sizeof(emc_dvb_dvfs_table_t)); } else if (C.marikoEmcMaxClock < 2131200) { diff --git a/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp index e26af172..d39f9835 100644 --- a/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp +++ b/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp @@ -437,6 +437,12 @@ void MiscGui::listUI() ); std::vector dvfsOffset = { + NamedValue("-80 mV", 0xFFFFFFB0), + NamedValue("-75 mV", 0xFFFFFFB5), + NamedValue("-70 mV", 0xFFFFFFBA), + NamedValue("-65 mV", 0xFFFFFFBF), + NamedValue("-60 mV", 0xFFFFFFC4), + NamedValue("-55 mV", 0xFFFFFFC9), NamedValue("-50 mV", 0xFFFFFFCE), NamedValue("-45 mV", 0xFFFFFFD3), NamedValue("-40 mV", 0xFFFFFFD8), @@ -445,7 +451,7 @@ void MiscGui::listUI() NamedValue("-20 mV", 0xFFFFFFEC), NamedValue("-10 mV", 0xFFFFFFF6), NamedValue(" -5 mV", 0xFFFFFFFB), - NamedValue("Disabled", 0), + NamedValue("Disabled", 0), NamedValue(" +5 mV", 5), NamedValue("+10 mV", 10), NamedValue("+15 mV", 15), @@ -991,7 +997,7 @@ protected: addConfigButton( KipConfigValue_marikoCpuMaxVolt, "CPU Max Voltage", - ValueRange(1000, 1235, 5, "mV", 1), + ValueRange(1000, 1200, 5, "mV", 1), "CPU Max Voltage", &mCpuVoltThresholds, {}, @@ -1588,4 +1594,4 @@ void MiscGui::refresh() { } } } -} \ No newline at end of file +} diff --git a/dist/README.md b/dist/README.md deleted file mode 100644 index 243a969e..00000000 --- a/dist/README.md +++ /dev/null @@ -1,168 +0,0 @@ - -
- -logo - ---- - -![License: GPL-2.0](https://img.shields.io/badge/GPL--2.0-red?style=for-the-badge) -![Nintendo Switch](https://img.shields.io/badge/Nintendo_Switch-E60012?style=for-the-badge\&logo=nintendo-switch\&logoColor=white) -[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge\&logo=discord\&logoColor=white)](https://dsc.gg/horizonoc) -![VSCode](https://img.shields.io/badge/VSCode-0078D4?style=for-the-badge\&logo=visual%20studio%20code\&logoColor=white) -![Made with Notepad++](assets/np++.png?raw=true) -![C++](https://img.shields.io/badge/C%2B%2B-00599C?style=for-the-badge\&logo=c%2B%2B\&logoColor=white) -![Downloads](https://img.shields.io/github/downloads/souldbminersmwc/Horizon-OC/total.svg?style=for-the-badge) - ---- - -
- -## ⚠️ Disclaimer - -> **THIS TOOL CAN BE DANGEROUS IF MISUSED. PROCEED WITH CAUTION.** -> Due to the design of Horizon OS, **overclocking RAM can cause NAND OR SD CORRUPTION.** -> Ensure you have a **full NAND, PROINFO, EMUMMC and SD backup** before proceeding. - ---- - -## About - -**Horizon OC** is an open-source overclocking tool for Nintendo Switch consoles running **Atmosphere custom firmware**. -It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration tools. - ---- - -## Features - -* **CPU:** Up to 1963MHz (Mariko) / 1785MHz (Erista) -* **GPU:** Up to 1075MHz (Mariko) / 921MHz (Erista) -* **RAM:** Up to 1866MHz (Mariko) / 1600MHz (Erista) -* Over/undervolting support -* Built-in configurator -* Compatible with most homebrew - -> It is reccomended to read the [guide](https://rentry.co/howtoget60fps) before proceeding, as this can help you get a *significant* performance boost over the default settings, often times with less power draw and heat output - ---- - -## Installation - -1. Ensure you have the latest versions of - - * [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere) - * [Ultrahand Overlay](https://github.com/ppkantorski/Ultrahand-Overlay) -2. Download and extract the **Horizon OC Package** to the root of your SD card. -3. If using **Hekate**, edit `hekate_ipl.ini` to include: - - ``` - kip1=atmosphere/kips/hoc.kip - ``` - - *(No changes needed if using fusee.)* - ---- - -## Configuration - -1. Open the Horizon OC Overlay -2. Open the settings menu -3. Adjust your overclocking settings as desired. A helpful guide can be found [here.](https://rentry.co/mariko#oc-settings-for-horizon-oc) -4. Click **Save KIP Settings** to apply your configuration. - ---- - -## Building from Source - -Refer to COMPILATION.md - ---- -## Clock table - -### MEM clocks -* 3200 → max on mariko, JEDEC. -* 2933 → JEDEC. -* 2666 → JEDEC. -* 2400 → max on erista, JEDEC. -* 2133 → mariko safe max (4266 Modules), JEDEC. -* 1996 → JEDEC. -* 1866 → mariko safe max (3733 Modules), JEDEC. -* 1600 → official docked, boost mode, erista safe max, JEDEC. -* 1331 → official handheld, JEDEC. -* 1065 -* 800 -* 665 - -### CPU clocks -* 2601 → mariko absolute max, very dangerous -* 2499 -* 2397 → mariko safe max with UV (low speedo) -* 2295 -* 2193 -* 2091 -* 1963 → mariko no UV max clock -* 1887 -* 1785 → erista no UV max clock, boost mode -* 1683 -* 1581 -* 1428 -* 1326 -* 1224 → sdev oc -* 1122 -* 1020 → official docked & handheld -* 918 -* 816 -* 714 -* 612 → sleep mode - -**Notes:** -1. On Erista, CPU in handheld is capped to 1581MHz - -### GPU clocks -* 1536 → absolute max clock on mariko. very dangerous -* 1459 -* 1382 -* 1305 -* 1267 → NVIDIA T214 rating -* 1228 → mariko HiOPT safe clock -* 1152 → mariko SLT max clock -* 1075 → mariko no UV max clock. absolute max clock on erista. very dangerous -* 998 → NVIDIA T210 rating -* 960 (erista only) → erista slt/hiopt safe max clock -* 921 → erista no UV max clock -* 844 -* 768 → official docked -* 691 -* 614 -* 537 -* 460 → max handheld -* 384 → official handheld -* 307 → official handheld -* 230 -* 153 -* 76 → boost mode - -**Notes:** -1. GPU overclock is capped at 460MHz on erista in handheld -2. On Mariko, cap with No uv is 614MHz, with SLT it is 691MHz and with HiOPT it's 768MHz -3. Clocks higher than 768MHz on erista need the official charger is plugged in. -4. On Mariko, cap with No uv is 844MHz, with SLT it is 921MHz and with HiOPT it's 998MHz - ---- - -## Credits -* **Lightos's Cat** - Cat - -* **Souldbminer** – hoc-clk and loader development -* **Lightos** – loader patches development -* **SciresM** - Atmosphere CFW -* **CTCaer** - L4T, Hekate, perfect ram timings -* **KazushiMe** – Switch OC Suite -* **hanai3bi (meha)** – Switch OC Suite, EOS, sys-clk-eos -* **NaGaa95** – L4T-OC-kernel -* **B3711 (halop)** – EOS -* **sys-clk team (m4xw, p-sam, natinusala)** – sys-clk -* **b0rd2death** – Ultrahand sys-clk & Status Monitor fork -* **MasaGratoR and ZachyCatGames** - General help -* **MasaGratoR** - Status Monitor & Display Refresh Rate Driver -* **Dom, Samybigio, Arcdelta, Miki, Happy, Flopsider, Winnerboi77, Blaise, Alvise, TDRR, agjeococh, frost, letum00 and Xenshen** - Testing -* **Samybigio2011** - Italian translations diff --git a/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp b/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp index 8c1f6160..0aacb6d9 100644 Binary files a/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp and b/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp differ diff --git a/dist/atmosphere/kips/hoc.kip b/dist/atmosphere/kips/hoc.kip index 75f3c2b9..d44d7708 100644 Binary files a/dist/atmosphere/kips/hoc.kip and b/dist/atmosphere/kips/hoc.kip differ diff --git a/dist/switch/.overlays/Horizon-OC-Monitor.ovl b/dist/switch/.overlays/Horizon-OC-Monitor.ovl index d98630e3..8aa39405 100644 Binary files a/dist/switch/.overlays/Horizon-OC-Monitor.ovl and b/dist/switch/.overlays/Horizon-OC-Monitor.ovl differ diff --git a/dist/switch/.overlays/horizon-oc-overlay.ovl b/dist/switch/.overlays/horizon-oc-overlay.ovl index ad21a4a7..22a26fb5 100644 Binary files a/dist/switch/.overlays/horizon-oc-overlay.ovl and b/dist/switch/.overlays/horizon-oc-overlay.ovl differ diff --git a/docs/configurator.html b/docs/configurator.html deleted file mode 100644 index eecd56c7..00000000 --- a/docs/configurator.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - Horizon OC Configurator - - - - - - - - - - - -
-

Configurator

-

Configure frequencies and voltages to suit your hardware and preferences.

- -
- -
- - - -
- - -
- -

Upload loader.kip here

-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- - -
- Built with ❤️. All trademarks and logos are the property of their respective owners. -
- - - - - - diff --git a/docs/configurator.js b/docs/configurator.js deleted file mode 100644 index 409cec66..00000000 --- a/docs/configurator.js +++ /dev/null @@ -1,683 +0,0 @@ -/* configurator.js - Clean, readable reconstruction of the original minified configurator - - Keeps original behavior (tables, parsing, localStorage, GitHub release fetching) - - Uses modern async/await and DOMContentLoaded guarding -*/ - -const CUST_REV_ADV = 11; - -const CustPlatform = Object.freeze({ - Undefined: 0, - Erista: 1, - Mariko: 2, - All: 3 -}); - -class CustEntry { - constructor(id, name, platform, size, desc = [], defval = 0, range = [0, 1e6], step = 1, zeroable = true) { - this.id = id; - this.name = name; - this.platform = platform; - this.size = size; - this.desc = desc; - this.defval = defval; - this.step = step; - this.zeroable = zeroable; - this.min = range[0]; - this.max = range[1]; - this.value = this.defval; - } - - validate() { - const err = new ErrorToolTip(this.id).clear(); - if (Number.isNaN(this.value) || this.value === undefined) { - err.setMsg("Invalid value: Not a number").show(); - return false; - } - if (!this.zeroable && this.value === 0) { - err.setMsg("Zero is not allowed for this entry").show(); - return false; - } - if (this.value < this.min || this.value > this.max) { - err.setMsg(`Expected range: [${this.min}, ${this.max}], got ${this.value}.`).show(); - return false; - } - if ((this.value % this.step) !== 0) { - err.setMsg(`${this.value} % ${this.step} ≠ 0`).show(); - return false; - } - return true; - } - - getInputElement() { - return document.getElementById(this.id); - } - - updateValueFromElement() { - const el = this.getInputElement(); - this.value = Number(el?.value); - } - - isAvailableFor(platform) { - return platform === CustPlatform.Undefined || this.platform === platform || this.platform === CustPlatform.All; - } - - createElement(containerId = "config-list-basic") { - let input = this.getInputElement(); - if (!input) { - const wrapper = document.createElement("div"); - wrapper.classList.add("grid", "cust-element", "mb-3"); - - input = document.createElement("input"); - input.min = String(this.zeroable ? 0 : this.min); - input.max = String(this.max); - input.id = this.id; - input.type = "number"; - input.step = String(this.step); - input.classList.add("text-black", "w-36"); - - const label = document.createElement("label"); - label.setAttribute("for", this.id); - label.innerHTML = `${this.name}`; - label.classList.add("block", "mb-1"); - - const desc = document.createElement("blockquote"); - desc.innerHTML = "
    " + (this.desc || []).map(d => `
  • ${d}
  • `).join("") + "
"; - desc.setAttribute("for", this.id); - desc.classList.add("text-sm", "text-gray-300"); - - wrapper.appendChild(label); - wrapper.appendChild(input); - wrapper.appendChild(desc); - - const container = document.getElementById(containerId); - if (container) container.appendChild(wrapper); - new ErrorToolTip(this.id).addChangeListener(); - } - input.value = String(this.value); - } - - setElementValue() { - const el = this.getInputElement(); - if (el) el.value = String(this.value); - } - - setElementDefaultValue() { - const el = this.getInputElement(); - if (el) el.value = String(this.defval); - } - - removeElement() { - const el = this.getInputElement(); - if (el && el.parentElement && el.parentElement.parentElement) el.parentElement.parentElement.remove(); - } - - showElement() { - const el = this.getInputElement(); - if (el && el.parentElement && el.parentElement.parentElement) el.parentElement.parentElement.style.removeProperty("display"); - } - - hideElement() { - const el = this.getInputElement(); - if (el && el.parentElement && el.parentElement.parentElement) el.parentElement.parentElement.style.setProperty("display", "none"); - } -} - -class AdvEntry extends CustEntry { - createElement() { - super.createElement("config-list-advanced"); - } -} - -class GpuEntry extends CustEntry { - constructor(id, name, platform = CustPlatform.Mariko, size = 4, desc = ["range: 610 ≤ x ≤ 1000"], defval = 610, range = [610, 1000], step = 5, zeroable = false) { - super(id, name, platform, size, desc, defval, range, step, zeroable); - } - - createElement() { - super.createElement("config-list-gpu"); - } -} - -/* === Tables (reconstructed from original) === - Note: Numeric defaults preserved where feasible. -*/ -const CustTable = [ - new CustEntry("mtcConf", "DRAM Timing", CustPlatform.All, 4, - [ - "0: AUTO_ADJ_ALL: Auto adjust mtc table with LPDDR4 3733 Mbps specs, 8Gb density. Change timing with Advanced Config (Default)", - "1: CUSTOM_ADJ_ALL: Adjust only non-zero preset timings in Advanced Config", - "2: NO_ADJ_ALL: Use 1600 mtc table without adjusting (Timing becomes tighter if you raise dram clock)." - ], 0, [0, 2], 1), - new CustEntry("commonCpuBoostClock", "Boost Clock in kHz", CustPlatform.All, 4, - ["System default: 1785000", "Boost clock will be applied when applications request Boost Mode via performance configuration."], - 1785000, [1020000, 3000000], 1, false), - new CustEntry("commonEmcMemVolt", "EMC Vdd2 Voltage in uV", CustPlatform.All, 4, - ["Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.", "Erista Default: 1125000", "Mariko Default: 1100000", "Official lpddr4(x) range: 1060mV~1175mV (1100mV nominal)", "OCS need high voltage unlike l4t because of not scaling mtc table well. However it is recommended to stay within official limits", "Not enabled by default"], - 0, [1100000, 1250000], 12500), - new CustEntry("eristaCpuMaxVolt", "Erista CPU Max Voltage in mV", CustPlatform.Erista, 4, - ["Acceptable range: 1120 ≤ x ≤ 1300", "L4T Default: 1235"], 1235, [1120, 1300], 1), - new CustEntry("eristaEmcMaxClock", "Erista RAM Max Clock in kHz", CustPlatform.Erista, 4, - ["Values should be ≥ 1600000, and divided evenly by 3200.", "Recommended Clocks: 1862400, 2131200 (JEDEC)", "WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM"], 1862400, [1600000, 2131200], 3200), - new CustEntry("marikoCpuMaxVolt", "Mariko CPU Max Voltage in mV", CustPlatform.Mariko, 4, - ["System default: 1120", "Acceptable range: 1120 ≤ x ≤ 1300", "Changing this value affects cpu voltage calculation"], 1235, [1120, 1300], 5), - new CustEntry("marikoEmcMaxClock", "Mariko RAM Max Clock in kHz", CustPlatform.Mariko, 4, - ["Values should be ≥ 1600000, and divided evenly by 3200.", "Recommended Clocks: 1862400, 2131200, 2400000 (JEDEC)", "Some clocks above 2400Mhz might not boot, because OCS doesn't scale table very well", "WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM."], - 1996800, [1600000, 2502400], 3200), - new CustEntry("marikoEmcVddqVolt", "EMC Vddq (Mariko Only) Voltage in uV", CustPlatform.Mariko, 4, - ["Acceptable range: 550000 ≤ x ≤ 650000", "Value should be divided evenly by 5000", "Default: 600000", "Official lpddr4(x) range: 570mV~650mV (600mV nominal)", "Not enabled by default."], - 0, [550000, 650000], 5000), - new CustEntry("marikoCpuUV", "Enable Mariko CPU Undervolt", CustPlatform.Mariko, 4, - ["Reduce CPU power draw", "0 : Default Table", "1 : Undervolt Level 1 (SLT - CPU speedo < 1650)", "2 : Undervolt Level 1 (SLT - CPU speedo >= 1650)"], 0, [0, 2], 1), - new CustEntry("marikoGpuUV", "Enable Mariko GPU Undervolt", CustPlatform.Mariko, 4, - ["Reduce GPU power draw", "Your GPU might not withstand undervolt, and can hang your console, or crash games", "Undervolting too much will drop GPU performance even if it seems stable", "GPU voltages are dynamic and will change with temperature and gpu speedo", "0 : Default Table", "1 : Undervolt Level 1 (SLT: Aggressive)", "2 : Undervolt Level 2 (HiOPT: Drastic)", "3 : Custom static GPU Voltage Table (Use Gpu Configuration below)"], - 0, [0, 3], 1), - new CustEntry("commonGpuVoltOffset", "GPU Volt Offset", CustPlatform.All, 4, - ["Negative Voltage offset value for gpu dynamic voltage calculation", "For example, value of 10 will decrease 10mV gpu volt from all frequencies", "Default gpu vmin: Erista - 812.5mV / Mariko - 610mV", "Acceptable range: 0 ~ 100"], - 0, [0, 100], 1) -]; - -const AdvTable = [ - new AdvEntry("marikoEmcDvbShift", "Step up Mariko EMC DVB Table", CustPlatform.Mariko, 4, - ["Each number adds 25mV to SoC voltage", "Helps with stability at higher memory clock", "Acceptable range : 0~9"], 0, [0, 9], 1), - new AdvEntry("ramTimingPresetOne", "Primary RAM Timing Preset", CustPlatform.Mariko, 4, - ["WARNING: Unstable timings can corrupt your nand", "Select Timing Preset for both AUTO_ADJ and CUSTOM_ADJ", "Values are : tRCD - tRP - tRAS (tRC = tRP + tRAS)", "0 : Do Not Adjust (2400Mhz: 12 - 12 - 28) (CUST_ADJ only)", "1 : 18 - 18 - 42 (Default timing)", "2 : 17 - 17 - 39", "3 : 16 - 16 - 36", "4 : 15 - 15 - 34", "5 : 14 - 14 - 32", "6 : 13 - 13 - 30"], - 1, [0, 6], 1), - new AdvEntry("ramTimingPresetTwo", "Secondary RAM Timing Preset", CustPlatform.Mariko, 4, - ["WARNING: Unstable timings can corrupt your nand", "Secondary Timing Preset for both AUTO_ADJ and CUSTOM_ADJ", "Values are : tRRD - tFAW", "0 : Do Not Adjust (2400Mhz: 6.6 - 26.6) (CUST_ADJ only)", "1 : 10 - 40 (Default timing) (3733 specs)", "2 : 7.5 - 30 (4266 specs)", "3 : 6 - 24", "4 : 4 - 16", "5 : 3 - 12"], - 1, [0, 5], 1), - new AdvEntry("ramTimingPresetThree", "Secondary RAM Timing Preset", CustPlatform.Mariko, 4, - ["WARNING: Unstable timings can corrupt your nand", "Secondary Timing Preset for both AUTO_ADJ and CUSTOM_ADJ", "Values are : tWR - tRTP", "0 : Do Not Adjust (2400Mhz: ?? - 5) (CUST_ADJ only)", "1 : 18 - 7.5 (Default timing)", "2 : 15 - 7.5", "3 : 15 - 6", "4 : 12 - 6", "5 : 12 - 4", "6 : 8 - 4"], - 1, [0, 6], 1), - new AdvEntry("ramTimingPresetFour", "Secondary RAM Timing Preset", CustPlatform.Mariko, 4, - ["WARNING: Unstable timings can corrupt your nand", "Secondary Timing Preset for both AUTO_ADJ and CUSTOM_ADJ", "Values are : tRFC", "0 : Do Not Adjust (2400Mhz: 93.3) (CUST_ADJ only)", "1 : 140 (Default timing)", "2 : 120", "3 : 100", "4 : 80", "5 : 70", "6 : 60"], - 1, [0, 6], 1), - new AdvEntry("ramTimingPresetFive", "Secondary RAM Timing Preset", CustPlatform.Mariko, 4, - ["WARNING: Unstable timings can corrupt your nand", "Secondary Timing Preset for both AUTO_ADJ and CUSTOM_ADJ", "Values are : tWTR", "0 : Do Not Adjust (2400Mhz: ??) (CUST_ADJ only)", "1 : 10 (Default timing)", "2 : 8", "3 : 6", "4 : 4", "5 : 2", "6 : 1"], - 1, [0, 6], 1), - new AdvEntry("ramTimingPresetSix", "Tertiary RAM Timing Preset", CustPlatform.Mariko, 4, - ["WARNING: Unstable timings can corrupt your nand", "Tertiary Timing Preset for both AUTO_ADJ and CUSTOM_ADJ", "Values are : tREFpb", "0 : Do Not Adjust (2400Mhz: 325) (CUST_ADJ only)", "1 : 488 (Default timing)", "2 : 976", "3 : 1952", "4 : 3256", "5 : MAX"], - 1, [0, 5], 1), - new AdvEntry("ramTimingPresetSeven", "Latency Decrement", CustPlatform.Mariko, 4, - ["WARNING: Unstable timings can corrupt your nand", "Latency decrement for both AUTO_ADJ and CUSTOM_ADJ", "This preset decreases Write/Read related delays. Values are Write - Read", "0 : 0 - 0, Do Not Adjust for CUST_ADJ", "1 : '-2' - '-4'", "2 : '-4' - '-8'", "3 : '-6' - '-12'", "4 : '-8' - '-16'", "5 : '-10' - '-20'", "6 : '-12' - '-24'"], - 0, [0, 6], 1) -]; - -const GpuTable = [ - new GpuEntry("0", "76.8"), - new GpuEntry("1", "153.6"), - new GpuEntry("2", "230.4"), - new GpuEntry("3", "307.2"), - new GpuEntry("4", "384.0"), - new GpuEntry("5", "460.8"), - new GpuEntry("6", "537.6"), - new GpuEntry("7", "614.4"), - new GpuEntry("8", "691.2"), - new GpuEntry("9", "768.0"), - new GpuEntry("10", "844.8"), - new GpuEntry("11", "921.6"), - new GpuEntry("12", "998.4"), - new GpuEntry("13", "1075.2"), - new GpuEntry("14", "1152.0"), - new GpuEntry("15", "1228.8"), - new GpuEntry("16", "1267.2") -]; - -/* ===== ErrorToolTip ===== */ -class ErrorToolTip { - constructor(id, msg) { - this.id = id; - this.msg = msg; - this.element = document.getElementById(id); - if (msg) this.setMsg(msg); - } - - setMsg(msg) { - this.msg = String(msg); - return this; - } - - show() { - if (this.element) this.element.setAttribute("aria-invalid", "true"); - if (this.msg && this.element) { - this.element.setAttribute("title", this.msg); - if (this.element.parentElement) { - this.element.parentElement.setAttribute("data-tooltip", this.msg); - this.element.parentElement.setAttribute("data-placement", "top"); - } - } - return this; - } - - clear() { - if (this.element) this.element.removeAttribute("aria-invalid"); - if (this.element) this.element.removeAttribute("title"); - if (this.element && this.element.parentElement) { - this.element.parentElement.removeAttribute("data-tooltip"); - this.element.parentElement.removeAttribute("data-placement"); - } - return this; - } - - addChangeListener() { - if (!this.element) return; - this.element.addEventListener("change", () => { - const tableEntry = CustTable.find(e => e.id === this.id); - if (tableEntry) { - tableEntry.value = Number(this.element.value); - tableEntry.validate(); - } - }); - } -} - -/* ===== CustStorage ===== */ -class CustStorage { - constructor() { - this.storage = {}; - this.key = "last_saved"; - } - - updateFromTable() { - // helper to update and validate each entry - const updateOne = (entry) => { - entry.updateValueFromElement(); - if (!entry.validate()) { - const el = entry.getInputElement(); - if (el) el.focus(); - throw new Error(`Invalid ${entry.name}`); - } - }; - - CustTable.forEach(updateOne); - AdvTable.forEach(updateOne); - GpuTable.forEach(updateOne); - - this.storage = {}; - let t = Object.fromEntries(CustTable.map(e => [e.id, e.value])); - Object.keys(t).forEach(k => (this.storage[k] = t[k])); - t = Object.fromEntries(AdvTable.map(e => [e.id, e.value])); - Object.keys(t).forEach(k => (this.storage[k] = t[k])); - // GPU table could be included too if desired - } - - setTable() { - const keys = Object.keys(this.storage); - keys.forEach(k => { - const c = CustTable.find(e => e.id === k); - if (c) c.value = this.storage[k]; - }); - keys.forEach(k => { - const a = AdvTable.find(e => e.id === k); - if (a) a.value = this.storage[k]; - }); - - // Set defaults for entries not found - CustTable.filter(e => !keys.includes(e.id)).forEach(e => e.value = e.defval); - AdvTable.filter(e => !keys.includes(e.id)).forEach(e => e.value = e.defval); - - // Validate and set element values - CustTable.forEach(e => { - if (!e.validate()) { - const el = e.getInputElement(); - if (el) el.focus(); - throw new Error(`Invalid ${e.name}`); - } - e.setElementValue(); - }); - AdvTable.forEach(e => { - if (!e.validate()) { - const el = e.getInputElement(); - if (el) el.focus(); - throw new Error(`Invalid ${e.name}`); - } - e.setElementValue(); - }); - GpuTable.forEach(e => { - if (!e.validate()) { - const el = e.getInputElement(); - if (el) el.focus(); - throw new Error(`Invalid ${e.name}`); - } - e.setElementValue(); - }); - } - - save() { - localStorage.setItem(this.key, JSON.stringify(this.storage)); - } - - load() { - const raw = localStorage.getItem(this.key); - if (!raw) return null; - const parsed = JSON.parse(raw); - - // Filter unknown keys and warn - const custIds = CustTable.map(e => e.id); - const ignoredCust = Object.keys(parsed).filter(k => !custIds.includes(k)); - if (ignoredCust.length) console.log("Ignored (cust):", ignoredCust); - Object.keys(parsed).filter(k => custIds.includes(k)).forEach(k => (this.storage[k] = parsed[k])); - - const advIds = AdvTable.map(e => e.id); - const ignoredAdv = Object.keys(parsed).filter(k => !advIds.includes(k)); - if (ignoredAdv.length) console.log("Ignored (adv):", ignoredAdv); - Object.keys(parsed).filter(k => advIds.includes(k)).forEach(k => (this.storage[k] = parsed[k])); - - return this.storage; - } -} - -/* ===== Cust (binary parser/patcher) ===== - This class follows the original structure: - - find magic in buffer using magicLen, mapper sizes - - parse data using size-mapped getters/setters - - produce Blob for download (patched buffer) -*/ -class Cust { - constructor() { - this.storage = new CustStorage(); - this.magic = 1414747459; // original magic - this.magicLen = 4; - this.mapper = { - 2: { get: (offset) => this.view.getUint16(offset, true), set: (offset, v) => this.view.setUint16(offset, v, true) }, - 4: { get: (offset) => this.view.getUint32(offset, true), set: (offset, v) => this.view.setUint32(offset, v, true) } - }; - } - - findMagicOffset() { - this.view = new DataView(this.buffer); - for (let offset = 0; offset < this.view.byteLength; offset += this.magicLen) { - if (this.mapper[this.magicLen].get(offset) === this.magic) { - this.beginOffset = offset; - return; - } - } - throw new Error("Invalid loader.kip file (magic not found)"); - } - - save() { - // update from table (validate & populate storage) - this.storage.updateFromTable(); - - const writeOne = (entry) => { - if (!entry.offset && entry.offset !== 0) { - // If offset is missing in the UI, it's an error in mapping - const el = entry.getInputElement(); - if (el) el.focus(); - throw new Error(`Failed to get offset for ${entry.name}`); - } - const mapper = this.mapper[entry.size]; - if (!mapper) { - const el = entry.getInputElement(); - if (el) el.focus(); - throw new Error(`Unknown size at ${entry.name}`); - } - mapper.set(entry.offset, entry.value); - }; - - // The original iterated over tables and used .offset values stored in entries - CustTable.forEach(writeOne); - AdvTable.forEach(writeOne); - GpuTable.forEach(writeOne); - - // persist storage to localStorage - this.storage.save(); - - // create download link for patched buffer - const blob = new Blob([this.buffer], { type: "application/octet-stream" }); - const url = URL.createObjectURL(blob); - - const a = document.createElement("a"); - a.href = url; - a.download = "loader.kip"; - document.body.appendChild(a); - a.click(); - a.remove(); - URL.revokeObjectURL(url); - - // UI: enable load last saved if needed - this.toggleLoadLastSavedBtn(true); - } - - removeHTMLForm() { - CustTable.forEach(e => e.removeElement()); - } - - toggleLoadLastSavedBtn(visible) { - const btn = document.getElementById("load_saved"); - if (!btn) return; - if (visible) { - btn.addEventListener("click", () => { - if (this.storage.load()) this.storage.setTable(); - }); - btn.style.removeProperty("display"); - btn.removeAttribute("disabled"); - } else { - btn.style.setProperty("display", "none"); - } - } - - createHTMLForm() { - // create elements for each table entry - CustTable.forEach(e => e.createElement("config-list-basic")); - - // Add separators/titles for advanced and gpu lists - const advContainer = document.getElementById("config-list-advanced"); - if (advContainer) { - const p = document.createElement("p"); - p.innerHTML = "Advanced configuration"; - advContainer.appendChild(p); - } - const gpuContainer = document.getElementById("config-list-gpu"); - if (gpuContainer) { - const p = document.createElement("p"); - p.innerHTML = "Gpu Volt configuration"; - gpuContainer.appendChild(p); - } - - AdvTable.forEach(e => e.createElement("config-list-advanced")); - GpuTable.forEach(e => e.createElement("config-list-gpu")); - - // Buttons: load default, load saved, save - const loadDefaultBtn = document.getElementById("load_default"); - if (loadDefaultBtn) { - loadDefaultBtn.removeAttribute("disabled"); - loadDefaultBtn.addEventListener("click", () => { - CustTable.forEach(e => e.setElementDefaultValue()); - }); - } - - // show load saved if we have saved storage - this.toggleLoadLastSavedBtn(this.storage.load() !== null); - - const saveBtn = document.getElementById("save"); - if (saveBtn) { - saveBtn.removeAttribute("disabled"); - saveBtn.addEventListener("click", () => { - try { - this.save(); - } catch (err) { - console.error(err); - alert(String(err)); - } - }); - } - } - - initCustTabs() { - // map nav buttons (role="tablist") to platform toggles - const tabButtons = Array.from(document.querySelectorAll('nav[role="tablist"] > button, .cust-platform-tabs > button')); - if (!tabButtons.length) return; - tabButtons.forEach(btn => { - btn.removeAttribute("disabled"); - const plat = Number(btn.getAttribute("data-platform")); - btn.addEventListener("click", (ev) => { - // style toggling - const outlineClass = ["outline"]; - btn.classList.remove(...outlineClass); - tabButtons.filter(x => x !== btn).forEach(x => x.classList.add(...outlineClass)); - // show/hide entries - CustTable.forEach(e => { - if (e.isAvailableFor(plat)) e.showElement(); else e.hideElement(); - }); - }); - }); - } - - parse() { - // beginOffset should already exist - let offset = this.beginOffset + this.magicLen; - // read revision (4-byte) - this.rev = this.mapper[4].get(offset); - if (this.rev !== CUST_REV_ADV) throw new Error(`Unsupported custRev, expected: ${CUST_REV_ADV}, got ${this.rev}`); - offset += 4; - const revEl = document.getElementById("cust_rev"); - if (revEl) revEl.innerHTML = `Cust v${this.rev} is loaded.`; - - const readOne = (entry) => { - entry.offset = offset; - const mapper = this.mapper[entry.size]; - if (!mapper) { - const el = entry.getInputElement(); - if (el) el.focus(); - throw new Error(`Unknown size at ${entry.name}`); - } - entry.value = mapper.get(offset); - offset += entry.size; - entry.validate(); - }; - - CustTable.forEach(readOne); - AdvTable.forEach(readOne); - GpuTable.forEach(readOne); - } - - load(buffer) { - try { - this.buffer = buffer; - this.findMagicOffset(); - this.removeHTMLForm(); - this.parse(); - this.initCustTabs(); - this.createHTMLForm(); - } catch (err) { - console.error(err); - alert(String(err)); - } - } -} - -/* ===== Release fetching classes ===== */ -class ReleaseAsset { - constructor(obj) { - this.downloadUrl = obj?.browser_download_url; - this.updatedAt = obj?.updated_at; - } -} - -class ReleaseInfo { - constructor() { - this.ocLatestApi = "https://api.github.com/repos/Horizon-OC/Horizon-OC/releases/latest"; - } - - async load() { - try { - const json = await this.responseFromApi(this.ocLatestApi); - this.parseOcResponse(json); - } catch (err) { - console.error(err); - alert(String(err)); - } - } - - async responseFromApi(url) { - const res = await fetch(url, { method: "GET", headers: { Accept: "application/json" } }); - if (!res.ok) throw new Error(`Failed to connect to "${url}": ${res.status}`); - return await res.json(); - } - - parseOcResponse(json) { - this.ocVer = json.tag_name; - const assets = json.assets || []; - this.loaderKipAsset = new ReleaseAsset(assets.find(a => a.name.endsWith("hoc.kip")) || {}); - this.sdOutZipAsset = new ReleaseAsset(assets.find(a => a.name.endsWith("dist.zip")) || {}); - } -} - -class DownloadSection { - constructor() { - this.element = document.getElementById("download_btn_grid") || document.getElementById("download"); - } - - async load() { - // Wait until the element is visible in the viewport - for (; !this.isVisible();) { - await new Promise(resolve => setTimeout(resolve, 1000)); - } - const release = new ReleaseInfo(); - await release.load(); - this.update("loader_kip_btn", `hoc.kip ${release.ocVer}
${release.loaderKipAsset.updatedAt}`, release.loaderKipAsset.downloadUrl); - this.update("sdout_zip_btn", `dist.zip ${release.ocVer}
${release.sdOutZipAsset.updatedAt}`, release.sdOutZipAsset.downloadUrl); - } - - isVisible() { - if (!this.element) return true; - const r = this.element.getBoundingClientRect(); - return r.top > 0 && r.left > 0 && r.bottom - r.height < (window.innerHeight || document.documentElement.clientHeight) && r.right - r.width < (window.innerWidth || document.documentElement.clientWidth); - } - - update(id, html, href) { - const el = document.getElementById(id); - if (!el) return; - el.innerHTML = html; - el.removeAttribute("aria-busy"); - if (href) el.setAttribute("href", href); - } -} - -/* ===== Initialization wiring ===== */ -document.addEventListener("DOMContentLoaded", async () => { - // Ensure containers exist (fail gracefully if not) - const ensureEl = (id, createTag = "div") => { - if (!document.getElementById(id)) { - const container = document.createElement(createTag); - container.id = id; - container.classList.add("mt-4"); - // append to config section or body as fallback - const config = document.getElementById("config") || document.body; - config.appendChild(container); - } - }; - - ["config-list-basic", "config-list-advanced", "config-list-gpu"].forEach(id => ensureEl(id)); - - // Wire file input - const fileInput = document.getElementById("file"); - if (fileInput) { - fileInput.addEventListener("change", (ev) => { - const cust = new Cust(); - const target = ev.target; - if (!target || !target.files) return; - const fr = new FileReader(); - fr.readAsArrayBuffer(target.files[0]); - fr.onloadend = (e) => { - if (e.target && e.target.readyState === FileReader.DONE) { - cust.load(e.target.result); - } - }; - }); - } - - // Wire DownloadSection (non-blocking) - (async () => { - try { - const ds = new DownloadSection(); - await ds.load(); - } catch (err) { - console.warn("DownloadSection failed:", err); - } - })(); - - // If HTML had DOMContentLoaded wiring in original minified code, emulate it: - // some initialization that original file did on DOMContentLoaded -> start the DownloadSection (done above) -}); diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 1ea49697..00000000 --- a/docs/index.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - Horizon OC - - - - - - - -
-

Horizon OC

-

Upgrade the performance of your Nintendo Switch Console

- - -
- - -
-

What is Horizon OC?

-

- Horizon OC is a tool to overclock your modded Nintendo Switch console. - This can help improve frame rates, load times, and latency in games. -

-
- - -
-
-
-

⚡ Performance

-

Overclocking your console can lead to significantly better performance in demanding titles.

-
-
-

☣ Safety

-

Horizon OC aims to be as safe as possible, while allowing advanced users to customize it further.

-
-
-

🔧 Customizable

-

Horizon OC comes with an advanced configurator allowing you to tune it for your own needs.

-
-
-
- - -
-

Get Horizon OC

- Download Latest Release -

Ensure you are using the latest version of Atmosphere.

-
- - -
- This website is open source and licensed under the GPLv2. -
- - - - -