Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC
This commit is contained in:
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -652,9 +652,8 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
// Copy unmodified 1600000 table to tmp
|
||||
std::memcpy(reinterpret_cast<void *>(tmp), reinterpret_cast<void *>(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) {
|
||||
|
||||
@@ -437,6 +437,12 @@ void MiscGui::listUI()
|
||||
);
|
||||
|
||||
std::vector<NamedValue> 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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
168
dist/README.md
vendored
168
dist/README.md
vendored
@@ -1,168 +0,0 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
<img src="assets/logo.png" alt="logo" width="350"/>
|
||||
|
||||
---
|
||||
|
||||

|
||||

|
||||
[](https://dsc.gg/horizonoc)
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
</div>
|
||||
|
||||
## ⚠️ 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
|
||||
BIN
dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
vendored
BIN
dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
vendored
Binary file not shown.
BIN
dist/atmosphere/kips/hoc.kip
vendored
BIN
dist/atmosphere/kips/hoc.kip
vendored
Binary file not shown.
BIN
dist/switch/.overlays/Horizon-OC-Monitor.ovl
vendored
BIN
dist/switch/.overlays/Horizon-OC-Monitor.ovl
vendored
Binary file not shown.
BIN
dist/switch/.overlays/horizon-oc-overlay.ovl
vendored
BIN
dist/switch/.overlays/horizon-oc-overlay.ovl
vendored
Binary file not shown.
@@ -1,140 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Horizon OC Configurator</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(160deg, #1a1a1a 0%, #2a2a2a 50%, #3a3a3a 100%);
|
||||
color: white;
|
||||
font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.glass:hover {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.button-glass {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
color: white;
|
||||
padding: 0.8rem 1.6rem;
|
||||
border-radius: 9999px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.button-glass:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
input[type="number"], input[type="file"] {
|
||||
padding: 0.5rem 0.8rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
outline: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
input[type="number"]:focus, input[type="file"]:focus {
|
||||
border-color: #b8f3ff;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.15);
|
||||
padding: 1.5rem 0;
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 4rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation / Go Back -->
|
||||
<nav class="flex justify-start p-6">
|
||||
<a href="index.html" class="button-glass">← Go Back to Main Page</a>
|
||||
</nav>
|
||||
|
||||
<!-- Configurator -->
|
||||
<section id="config" class="max-w-4xl mx-auto px-6 py-16 glass mt-6" data-aos="fade-up">
|
||||
<h2 class="text-3xl font-bold mb-4 text-center">Configurator</h2>
|
||||
<p class="text-gray-400 text-center mb-8">Configure frequencies and voltages to suit your hardware and preferences.</p>
|
||||
|
||||
<form class="space-y-6 text-center">
|
||||
<!-- Unit selection buttons -->
|
||||
<div class="flex justify-center gap-2">
|
||||
<button type="button" class="button-glass">All</button>
|
||||
<button type="button" class="button-glass">Erista</button>
|
||||
<button type="button" class="button-glass">Mariko</button>
|
||||
</div>
|
||||
|
||||
<!-- File upload -->
|
||||
<div class="flex flex-col items-center">
|
||||
<input type="file" id="file">
|
||||
<p class="text-sm text-gray-400 mt-2">Upload loader.kip here</p>
|
||||
</div>
|
||||
|
||||
<!-- Example numeric inputs -->
|
||||
<div class="flex flex-wrap justify-center gap-4 mt-4">
|
||||
<div>
|
||||
<label for="commonEmcMemVolt">EMC Voltage:</label>
|
||||
<input type="number" id="commonEmcMemVolt" min="0" max="1250000" step="12500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="commonCpuVolt">CPU Voltage:</label>
|
||||
<input type="number" id="commonCpuVolt" min="0" max="1250000" step="12500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="commonGpuVolt">GPU Voltage:</label>
|
||||
<input type="number" id="commonGpuVolt" min="0" max="1250000" step="12500">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer>
|
||||
Built with ❤️. All trademarks and logos are the property of their respective owners.
|
||||
</footer>
|
||||
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
<script>AOS.init({ once: true, duration: 700 });</script>
|
||||
<script src="configurator.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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 = `<strong>${this.name}</strong>`;
|
||||
label.classList.add("block", "mb-1");
|
||||
|
||||
const desc = document.createElement("blockquote");
|
||||
desc.innerHTML = "<ul>" + (this.desc || []).map(d => `<li>${d}</li>`).join("") + "</ul>";
|
||||
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,
|
||||
[
|
||||
"<b>0</b>: AUTO_ADJ_ALL: Auto adjust mtc table with LPDDR4 3733 Mbps specs, 8Gb density. Change timing with Advanced Config (Default)",
|
||||
"<b>1</b>: CUSTOM_ADJ_ALL: Adjust only non-zero preset timings in Advanced Config",
|
||||
"<b>2</b>: 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)", "<b>WARNING:</b> 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", "<b>WARNING:</b> 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", "<b>0</b> : Default Table", "<b>1</b> : Undervolt Level 1 (SLT - CPU speedo < 1650)", "<b>2</b> : 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", "<b>0</b> : Default Table", "<b>1</b> : Undervolt Level 1 (SLT: Aggressive)", "<b>2</b> : Undervolt Level 2 (HiOPT: Drastic)", "<b>3</b> : 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,
|
||||
["<b>WARNING</b>: Unstable timings can corrupt your nand", "Select Timing Preset for both AUTO_ADJ and CUSTOM_ADJ", "Values are : tRCD - tRP - tRAS (tRC = tRP + tRAS)", "<b>0</b> : Do Not Adjust (2400Mhz: 12 - 12 - 28) (CUST_ADJ only)", "<b>1</b> : 18 - 18 - 42 (Default timing)", "<b>2</b> : 17 - 17 - 39", "<b>3</b> : 16 - 16 - 36", "<b>4</b> : 15 - 15 - 34", "<b>5</b> : 14 - 14 - 32", "<b>6</b> : 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", "<b>0</b> : Do Not Adjust (2400Mhz: 6.6 - 26.6) (CUST_ADJ only)", "<b>1</b> : 10 - 40 (Default timing) (3733 specs)", "<b>2</b> : 7.5 - 30 (4266 specs)", "<b>3</b> : 6 - 24", "<b>4</b> : 4 - 16", "<b>5</b> : 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", "<b>0</b> : Do Not Adjust (2400Mhz: ?? - 5) (CUST_ADJ only)", "<b>1</b> : 18 - 7.5 (Default timing)", "<b>2</b> : 15 - 7.5", "<b>3</b> : 15 - 6", "<b>4</b> : 12 - 6", "<b>5</b> : 12 - 4", "<b>6</b> : 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", "<b>0</b> : Do Not Adjust (2400Mhz: 93.3) (CUST_ADJ only)", "<b>1</b> : 140 (Default timing)", "<b>2</b> : 120", "<b>3</b> : 100", "<b>4</b> : 80", "<b>5</b> : 70", "<b>6</b> : 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", "<b>0</b> : Do Not Adjust (2400Mhz: ??) (CUST_ADJ only)", "<b>1</b> : 10 (Default timing)", "<b>2</b> : 8", "<b>3</b> : 6", "<b>4</b> : 4", "<b>5</b> : 2", "<b>6</b> : 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", "<b>0</b> : Do Not Adjust (2400Mhz: 325) (CUST_ADJ only)", "<b>1</b> : 488 (Default timing)", "<b>2</b> : 976", "<b>3</b> : 1952", "<b>4</b> : 3256", "<b>5</b> : 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", "<b>0</b> : 0 - 0, Do Not Adjust for CUST_ADJ", "<b>1</b> : '-2' - '-4'", "<b>2</b> : '-4' - '-8'", "<b>3</b> : '-6' - '-12'", "<b>4</b> : '-8' - '-16'", "<b>5</b> : '-10' - '-20'", "<b>6</b> : '-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 <b>${release.ocVer}</b><br>${release.loaderKipAsset.updatedAt}`, release.loaderKipAsset.downloadUrl);
|
||||
this.update("sdout_zip_btn", `dist.zip <b>${release.ocVer}</b><br>${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)
|
||||
});
|
||||
153
docs/index.html
153
docs/index.html
@@ -1,153 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Horizon OC</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
|
||||
<style>
|
||||
/* Background gradient */
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
|
||||
color: white;
|
||||
background: linear-gradient(160deg, #1a1a1a 0%, #2a2a2a 50%, #3a3a3a 100%);
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Liquid glass panels */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.glass:hover {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
/* Hero gradient text */
|
||||
.gradient-text {
|
||||
background: linear-gradient(to right, #a3baff, #b8f3ff);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
/* Buttons: Liquid glass style */
|
||||
.button-glass {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
color: #fff;
|
||||
padding: 0.8rem 2rem;
|
||||
border-radius: 9999px;
|
||||
font-weight: 600;
|
||||
transition: all 0.25s ease;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button-glass:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
footer {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.15);
|
||||
padding: 1.5rem 0;
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 4rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* GitHub Icon */
|
||||
.github-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: white;
|
||||
transition: fill 0.3s;
|
||||
}
|
||||
.button-glass:hover .github-icon {
|
||||
fill: #b8f3ff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Header -->
|
||||
<header class="text-center py-20">
|
||||
<h1 class="text-6xl font-extrabold mb-4 gradient-text">Horizon OC</h1>
|
||||
<p class="text-gray-300 text-lg">Upgrade the performance of your Nintendo Switch Console</p>
|
||||
|
||||
<div class="mt-6 flex justify-center gap-4">
|
||||
<a href="#download" class="button-glass">Download Now</a>
|
||||
<a href="configurator.html" class="button-glass">Open Configurator</a>
|
||||
<a href="https://github.com/Horizon-OC/Horizon-OC" target="_blank" class="button-glass">
|
||||
<!-- GitHub SVG Icon -->
|
||||
<svg class="github-icon" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8a8 8 0 005.47 7.59c.4.07.55-.17.55-.38v-1.32C3.73 14.9 3.27 13.4 3.27 13.4c-.36-.9-.88-1.14-.88-1.14-.72-.49.05-.48.05-.48.8.06 1.22.83 1.22.83.71 1.21 1.87.86 2.33.66.07-.52.28-.86.5-1.06-2.22-.25-4.55-1.11-4.55-4.95 0-1.09.39-1.98 1.03-2.68-.1-.25-.45-1.27.1-2.65 0 0 .84-.27 2.75 1.02A9.56 9.56 0 018 3.43a9.6 9.6 0 012.5.34c1.9-1.29 2.74-1.02 2.74-1.02.55 1.38.2 2.4.1 2.65.64.7 1.03 1.59 1.03 2.68 0 3.85-2.34 4.7-4.57 4.95.29.25.54.73.54 1.48v2.2c0 .21.15.45.55.38A8 8 0 0016 8c0-4.42-3.58-8-8-8z"/>
|
||||
</svg>
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- About Section -->
|
||||
<section class="max-w-3xl mx-auto text-center py-16 px-6 glass" data-aos="fade-up">
|
||||
<h2 class="text-3xl font-bold mb-4">What is Horizon OC?</h2>
|
||||
<p class="text-gray-300">
|
||||
Horizon OC is a tool to overclock your modded Nintendo Switch console.
|
||||
This can help improve frame rates, load times, and latency in games.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Features -->
|
||||
<section class="py-20">
|
||||
<div class="max-w-6xl mx-auto grid md:grid-cols-3 gap-8 px-6">
|
||||
<div class="glass p-6 text-center" data-aos="zoom-in" data-aos-delay="100">
|
||||
<h3 class="text-xl font-semibold mb-2">⚡ Performance</h3>
|
||||
<p class="text-gray-300">Overclocking your console can lead to significantly better performance in demanding titles.</p>
|
||||
</div>
|
||||
<div class="glass p-6 text-center" data-aos="zoom-in" data-aos-delay="200">
|
||||
<h3 class="text-xl font-semibold mb-2">☣ Safety</h3>
|
||||
<p class="text-gray-300">Horizon OC aims to be as safe as possible, while allowing advanced users to customize it further.</p>
|
||||
</div>
|
||||
<div class="glass p-6 text-center" data-aos="zoom-in" data-aos-delay="300">
|
||||
<h3 class="text-xl font-semibold mb-2">🔧 Customizable</h3>
|
||||
<p class="text-gray-300">Horizon OC comes with an advanced configurator allowing you to tune it for your own needs.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Download Section -->
|
||||
<section id="download" class="text-center py-20 glass max-w-3xl mx-auto px-6" data-aos="fade-up">
|
||||
<h2 class="text-3xl font-bold mb-6">Get Horizon OC</h2>
|
||||
<a href="#" class="button-glass">Download Latest Release</a>
|
||||
<p class="text-gray-400 mt-4">Ensure you are using the latest version of Atmosphere.</p>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer>
|
||||
This website is open source and licensed under the GPLv2.
|
||||
</footer>
|
||||
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
<script>AOS.init({ once: true, duration: 800 });</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user