Revert "Merge branch 'master' of https://github.com/souldbminersmwc/Switch-OC-Suite-2"
This reverts commit76ac2d9219, reversing changes made todfbb341fa0.
This commit is contained in:
@@ -10,7 +10,7 @@ This project is very dangerous and can possibly damage your console. If you deci
|
||||
|
||||
Overclocking Suite for Nintendo Switch consoles running Atmosphere CFW.
|
||||
|
||||
[Project Homepage](https://souldbminersmwc.github.io/Switch-OC-Suite-2)
|
||||
[Project Homepage](https://hanai3Bi.github.io/Switch-OC-Suite)
|
||||
|
||||
**DISCLAIMER: USE AT YOUR OWN RISK!**
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang=en>
|
||||
<meta charset=UTF-8>
|
||||
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||
<title>Switch OC Suite 2 | "If Switch OC Suite is so good, then why isn't there a Switch OC Suite 2?".</title>
|
||||
<title>Switch OC Suite 2| Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2.</title>
|
||||
<meta name=description content="If Switch OC Suite is so good, then why isn't there a Switch OC Suite 2?">
|
||||
<link rel=stylesheet href=https://unpkg.com/@picocss/pico@latest/css/pico.min.css>
|
||||
<style>
|
||||
|
||||
15
pages/build.sh
Executable file
15
pages/build.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
[ -d "./tmp" ] || mkdir ./tmp
|
||||
[ -d "./dist" ] || mkdir ./dist
|
||||
|
||||
# README_HTML=`pandoc -f gfm -t html5 ../README.md`
|
||||
|
||||
cp -Rf ./src/*.html ./tmp/
|
||||
tsc ./src/main.ts --outDir ./tmp/ -lib es2019,dom -t es2015
|
||||
|
||||
for FILE in ./tmp/*; do
|
||||
minify "${FILE}" > "./dist/${FILE/.\/tmp\/}"
|
||||
done
|
||||
|
||||
rm -fr ./tmp
|
||||
233
pages/dist/index.html
vendored
Normal file
233
pages/dist/index.html
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
<!doctype html>
|
||||
<html lang=en>
|
||||
<meta charset=UTF-8>
|
||||
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||
<title>Switch OC Suite 2 | <i>"If Switch OC Suite is so good, why isn't there a Switch OC Suite 2?"</i>.</title>
|
||||
<meta name=description content="Next-generation overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2.">
|
||||
<link rel=stylesheet href=https://unpkg.com/@picocss/pico@latest/css/pico.min.css>
|
||||
<style>
|
||||
#nav-list{display:none}#nav-popup{display:unset}@media (min-width:768px){#nav-list{display:unset}#nav-popup{display:none}}details[open]>summary:not([role]):not(:focus){color:unset}div.hero>nav.container-fluid{position:fixed;top:0;z-index:99;-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px)}div.hero>header{padding-top:6rem}button[role=tab]{border-radius:0;border-left-width:calc(var(--border-width)/ 2);border-right-width:calc(var(--border-width)/ 2)}button[role=tab]:first-child{border-top-left-radius:var(--border-radius);border-bottom-left-radius:var(--border-radius);border-left-width:var(--border-width)}button[role=tab]:last-child{border-top-right-radius:var(--border-radius);border-bottom-right-radius:var(--border-radius);border-right-width:var(--border-width)}button[role=tab]:not(.outline){pointer-events:none;cursor:default}div.cust-element{margin:1.5rem 0 1.5rem 0}div.cust-element>blockquote,div.cust-element>blockquote>ul{margin:0;font-size:calc(var(--font-size) * .8);color:var(--muted-color)}div#download_btn_grid>a{margin:.5rem 0}
|
||||
</style>
|
||||
<div class=hero>
|
||||
<nav class=container-fluid>
|
||||
<ul>
|
||||
<li><a href=#head class=contrast><b>Switch OC Suite 2</b></a></ul>
|
||||
<ul id=nav-popup>
|
||||
<li>
|
||||
<details role=list dir=rtl>
|
||||
<summary aria-haspopup=group role=link class=contrast>Jump to</summary>
|
||||
<ul role=group>
|
||||
<li><a href=#readme>README</a>
|
||||
<li><a href=#download>Download</a>
|
||||
<li><a href=#config>Configurator</a>
|
||||
<li><a href=https://github.com/souldbminersmwc/Switch-OC-Suite-2 target=_blank>GitHub repository</a></ul>
|
||||
</details>
|
||||
</ul>
|
||||
<ul id=nav-list>
|
||||
<li><a href=#readme>README</a>
|
||||
<li><a href=#download>Download</a>
|
||||
<li><a href=#config>Config</a>
|
||||
<li><a href=https://github.com/souldbminersmwc/Switch-OC-Suite-2 target=_blank>GitHub</a></ul>
|
||||
</nav>
|
||||
<header class=container>
|
||||
<hgroup>
|
||||
<h1>Switch OC Suite</h1>
|
||||
<h2>Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2.</h2>
|
||||
</hgroup>
|
||||
</header>
|
||||
</div>
|
||||
<section aria-label=README id=readme>
|
||||
<div class=container>
|
||||
<article>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h2>README</h2>
|
||||
<h3>🚨DISCLAIMER: THIS IS PROVIDED AS IS. USE AT YOUR OWN RISK!🚨</h3>
|
||||
</hgroup>
|
||||
<ul>
|
||||
<li>Overclocking in general will shorten the lifespan of some hardware components. <strong>YOU ARE RESPONSIBLE for any problem or potential damage</strong> if unsafe frequencies are ENABLED in sys-clk-OC. Issues like asking for bypassing limit will BE IGNORED OR CLOSED WITHOUT REPLY.
|
||||
<li>Due to HorizonOS design, instabilities from unsafe RAM clocks may cause filesystem corruption. <strong>Always make backup before enabling DRAM OC.</strong></ul>
|
||||
</header>
|
||||
<h3>Features</h3>
|
||||
<details open>
|
||||
<summary>For Erista variant (HAC-001)</summary>
|
||||
<ul>
|
||||
<li>CPU Overclock (Safe: 1785 MHz)
|
||||
<ul>
|
||||
<li>Unsafe
|
||||
<ul>
|
||||
<li>Due to the limit of board power draw or power IC
|
||||
<li>Unlockable frequencies up to 2091 MHz
|
||||
<li>See <a href=https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md target=_blank>README for sys-clk-OC</a></ul>
|
||||
</ul>
|
||||
<li>DRAM Overclock (Safe: 1862.4 MHz)</ul>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>For Mariko variant (HAC-001-01, HDH-001, HEG-001)</summary>
|
||||
<ul>
|
||||
<li>CPU / GPU Overclock (Safe: 1963 / 998 MHz)
|
||||
<ul>
|
||||
<li>Unsafe
|
||||
<ul>
|
||||
<li>Due to the limit of board power draw or power IC
|
||||
<li>Unlockable frequencies up to 2295 / 1267 MHz
|
||||
<li>See <a href=https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md target=_blank>README for sys-clk-OC</a></ul>
|
||||
</ul>
|
||||
<li>DRAM Overclock (Safe: 1996.8 MHz)</ul>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>Modded sys-clk and ReverseNX-RT</summary>
|
||||
<ul>
|
||||
<li style=text-decoration:line-through>Auto CPU Boost
|
||||
<ul>
|
||||
<li>For faster game loading
|
||||
<li>Enable CPU Boost (1785 MHz) when CPU Core#3 (System Core) is stressed (mainly I/O operations).
|
||||
<li>Effective only when charger is connected or governor is enabled.
|
||||
<li>This feature is considered unsafe on Erista, especially when combined with high GPU frequency or with governor enabled.</ul>
|
||||
<li style=text-decoration:line-through>CPU & GPU frequency governor (Experimental)
|
||||
<ul>
|
||||
<li>Adjust frequency based on load. Might decrease power draw but can introduce stutters. Can be turned off for specific titles.</ul>
|
||||
<li style=text-decoration:line-through>Set charging current (100 mA - 2000 mA) and charging limit (20% - 100%)
|
||||
<ul>
|
||||
<li>Long-term use of charge limit may render the battery gauge inaccurate. Performing full cycles could help recalibration, or try <a href=https://github.com/CTCaer/battery_desync_fix_nx>battery_desync_fix_nx</a>.</ul>
|
||||
<li>Global Profile
|
||||
<ul>
|
||||
<li>Designated a dummy title id <code>0xA111111111111111</code>.
|
||||
<li>Priority: "Temp overrides" > "Application profile" > "Global profile" > "System default".</ul>
|
||||
<li style=text-decoration:line-through>Sync ReverseNX Mode
|
||||
<ul>
|
||||
<li>No need to change clocks manually after toggling modes in ReverseNX (-RT and -Tool)</ul>
|
||||
</ul>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>System Settings (Optional)</summary>See <a href=https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/system_settings.md>system_settings.md</a></details>
|
||||
<h3 id=installation open>Installation</h3>
|
||||
<ol type=1>
|
||||
<li>Download latest <a href=#download>release</a>.
|
||||
<li>Grab <code>x.x.x_loader.kip</code> for your Atmosphere version, rename it to <code>loader.kip</code> and place it in <code>/atmosphere/kips/</code>.
|
||||
<li>(optional) You can customize via <a href=#config>online loader configurator</a>
|
||||
<table role=grid>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Defaults
|
||||
<th>Mariko
|
||||
<th>Erista
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>CPU OC
|
||||
<td>2295 MHz Max
|
||||
<td>2091 MHz Max
|
||||
<tr>
|
||||
<td>CPU Boost
|
||||
<td>1785 MHz
|
||||
<td>N/A
|
||||
<tr>
|
||||
<td>CPU Volt
|
||||
<td>1235 mV Max
|
||||
<td>1235 mV Max
|
||||
<tr>
|
||||
<td>GPU OC
|
||||
<td>1267 MHz Max
|
||||
<td>998 MHz Max
|
||||
<tr>
|
||||
<td>RAM OC
|
||||
<td>1996 MHz
|
||||
<td>1862 MHz
|
||||
<tr>
|
||||
<td>RAM Volt
|
||||
<td>Disabled
|
||||
<td>Disabled
|
||||
<tr>
|
||||
<td>RAM Timing
|
||||
<td>Auto-Adjusted
|
||||
<td>Auto-Adjusted
|
||||
<tr>
|
||||
<td>CPU UV
|
||||
<td>Disabled
|
||||
<td>N/A
|
||||
<tr>
|
||||
<td>GPU UV
|
||||
<td>Disabled
|
||||
<td>N/A</table>
|
||||
<li>Hekate-ipl bootloader Only (fss0) (Not required for AMS fusee)
|
||||
<ul>
|
||||
<li>At boot entry section in <code>bootloader/hekate_ipl.ini</code>, Add <code>kip1=atmosphere/kips/loader.kip</code> to any line that works.</ul>
|
||||
<li>Install [sys-clk-oc]<br>official [sys-clk] (2.0.0+) is compatible but not recommended (no bugfixes or additional features).
|
||||
<li>(optional) Copy SdOut.zip for useful utilities.</ol>
|
||||
<details>
|
||||
<summary>How to build this project</summary>
|
||||
<ol type=1>
|
||||
<li>Grab necessary patches from the repo, then compile sys-clk, ReverseNX-RT and Atmosphere loader with devkitpro.
|
||||
<li>Before compiling Atmosphere loader, run <code>patch.py</code> in <code>Atmosphere/stratosphere/loader/source/</code> to insert oc module into loader sysmodule.
|
||||
<li>When compilation is done, uncompress the kip to make it work with configurator: <code>hactool -t kip1 Atmosphere/stratosphere/loader/out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=./loader.kip</code></ol>
|
||||
</details>
|
||||
<h3 id=faq>Frequently Asked Questions</h3>
|
||||
<details open>
|
||||
<summary>How to enable unsafe frequencies in sys-clk-OC?</summary>
|
||||
<ul>
|
||||
<li>Above all else, you should know <a href=#readme>what "unsafe" means and issues might arise</a>.
|
||||
<li>See the end of <a href=https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md target=_blank>README in sys-clk-OC</a>. Place this line <code>allow_unsafe_freq=1</code> under <code>[values]</code> section in <code>/config/sys-clk/config.ini</code></ul>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>I would like to bypass limit enforced in sys-clk to improve handheld performance without charger connected.</summary>
|
||||
<ul>
|
||||
<li>Bypassing clock cappings will be bad for battery.
|
||||
<li>See the end of <a href=https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md target=_blank>README in sys-clk-OC</a>. Place this line <code>uncapped_clocks=1</code> under <code>[values]</code> section in <code>/config/sys-clk/config.ini</code></ul>
|
||||
</details>
|
||||
<footer>
|
||||
<details role=list>
|
||||
<summary aria-haspopup=group role=button class=secondary>Acknowledgement</summary>
|
||||
<ul role=group>
|
||||
<li><a href=https://github.com/CTCaer/hekate target=_blank>CTCaer for Hekate-ipl bootloader, RE and hardware research</a>
|
||||
<li><a href=https://devkitpro.org/ target=_blank>devkitPro for All-In-One homebrew toolchains</a>
|
||||
<li><a href=https://github.com/masagrator/ReverseNX-RT target=_blank>masagrator for ReverseNX-RT and info on BatteryChargeInfoFields in psm module</a>
|
||||
<li><a href=https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual target=_blank>Nvidia for Tegra X1 Technical Reference Manual</a>
|
||||
<li><a href=https://github.com/retronx-team/sys-clk target=_blank>RetroNX team for sys-clk</a>
|
||||
<li><a href=https://github.com/Atmosphere-NX/Atmosphere target=_blank>SciresM and Reswitched Team for the state-of-the-art Atmosphere CFW of Switch</a>
|
||||
<li><a href=http://switchbrew.org/wiki/ target=_blank>Switchbrew wiki for Switch in-depth info</a>
|
||||
<li><a href=https://gitlab.com/switchroot/kernel target=_blank>Switchroot for their modified L4T kernel and device tree</a>
|
||||
<li>ZatchyCatGames for RE and original OC loader patches for Atmosphere
|
||||
<li><a href=https://github.com/KazushiMe/Switch-OC-Suite target=_blank>KazushiMe for original Switch-OC-Suite</a>
|
||||
<li>lineon for research and help</ul>
|
||||
</details>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<section aria-label=Download id=download>
|
||||
<div class=container>
|
||||
<article>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h2>Download</h2>
|
||||
<h3>Get latest version of Switch OC Suite and its corresponding Atmosphere package here.</h3>
|
||||
</hgroup>
|
||||
</header>
|
||||
<div class=grid id=download_btn_grid><a role=button aria-busy=true id=loader_kip_btn>Generating link, please wait...</a> <a role=button aria-busy=true id=sdout_zip_btn>Generating link, please wait...</a> <a target=_blank role=button aria-busy=true id=ams_btn>Generating link, please wait...</a></div>
|
||||
<footer>See <a href=#installation>Installation section</a> for how to use Switch OC Suite.</footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<section aria-label=Config id=config>
|
||||
<div class=container>
|
||||
<article>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h2>Configurator</h2>
|
||||
<h3>Configure frequencies and voltages to suit your hardware and preferences.</h3>
|
||||
</hgroup>
|
||||
</header>
|
||||
<form id=form>
|
||||
<nav role=tablist><button type=button role=tab id=tab_all data-platform=0 disabled>All</button> <button type=button role=tab class=outline id=tab_erista data-platform=1 disabled>Erista</button> <button type=button role=tab class=outline id=tab_mariko data-platform=2 disabled>Mariko</button></nav><label for=file><input id=file type=file> <small id=cust_rev>Upload loader.kip here</small></label>
|
||||
<div id=config-list-basic></div>
|
||||
<div id=config-list-advanced></div>
|
||||
<div id=config-list-gpu></div>
|
||||
</form>
|
||||
<footer>
|
||||
<div class=grid><button type=button id=load_default role=button disabled>Load Default</button> <button type=button id=load_saved role=button disabled>Load Last Saved</button> <button type=submit id=save role=button disabled>Save</button></div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<footer class=container><small>Build with <a href=https://picocss.com target=_blank>Pico</a>. All trademarks, logos and brand names are the property of their respective owners, used for identification purposes only.</small></footer>
|
||||
<script src=./main.js></script>
|
||||
341
pages/dist/main.js
vendored
Normal file
341
pages/dist/main.js
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
var __awaiter = this && this.__awaiter || function(e, t, i, n) {
|
||||
return new(i || (i = Promise))((function(a, r) {
|
||||
function s(e) {
|
||||
try {
|
||||
o(n.next(e))
|
||||
} catch (e) {
|
||||
r(e)
|
||||
}
|
||||
}
|
||||
|
||||
function l(e) {
|
||||
try {
|
||||
o(n.throw(e))
|
||||
} catch (e) {
|
||||
r(e)
|
||||
}
|
||||
}
|
||||
|
||||
function o(e) {
|
||||
var t;
|
||||
e.done ? a(e.value) : (t = e.value, t instanceof i ? t : new i((function(e) {
|
||||
e(t)
|
||||
}))).then(s, l)
|
||||
}
|
||||
o((n = n.apply(e, t || [])).next())
|
||||
}))
|
||||
};
|
||||
const CUST_REV_ADV = 11;
|
||||
var CustPlatform;
|
||||
! function(e) {
|
||||
e[e.Undefined = 0] = "Undefined", e[e.Erista = 1] = "Erista", e[e.Mariko = 2] = "Mariko", e[e.All = 3] = "All"
|
||||
}(CustPlatform || (CustPlatform = {}));
|
||||
class CustEntry {
|
||||
constructor(e, t, i, n, a, r, s = [0, 1e6], l = 1, o = !0) {
|
||||
this.id = e, this.name = t, this.platform = i, this.size = n, this.desc = a, this.defval = r, this.step = l, this.zeroable = o, this.min = s[0], this.max = s[1]
|
||||
}
|
||||
validate() {
|
||||
let e = new ErrorToolTip(this.id).clear();
|
||||
return Number.isNaN(this.value) || void 0 === this.value ? (e.setMsg("Invalid value: Not a number").show(), !1) : !(!this.zeroable || 0 != this.value) || (this.value < this.min || this.value > this.max ? (e.setMsg(`Expected range: [${this.min}, ${this.max}], got ${this.value}.`).show(), !1) : this.value % this.step == 0 || (e.setMsg(`${this.value} % ${this.step} ≠ 0`).show(), !1))
|
||||
}
|
||||
getInputElement() {
|
||||
return document.getElementById(this.id)
|
||||
}
|
||||
updateValueFromElement() {
|
||||
var e;
|
||||
this.value = Number(null === (e = this.getInputElement()) || void 0 === e ? void 0 : e.value)
|
||||
}
|
||||
isAvailableFor(e) {
|
||||
return e === CustPlatform.Undefined || this.platform === e || this.platform === CustPlatform.All
|
||||
}
|
||||
createElement() {
|
||||
let e = this.getInputElement();
|
||||
if (!e) {
|
||||
let t = document.createElement("div");
|
||||
t.classList.add("grid", "cust-element"), e = document.createElement("input"), e.min = String(this.zeroable ? 0 : this.min), e.max = String(this.max), e.id = this.id, e.type = "number", e.step = String(this.step);
|
||||
let i = document.createElement("label");
|
||||
i.setAttribute("for", this.id), i.innerHTML = this.name, i.appendChild(e), t.appendChild(i);
|
||||
let n = document.createElement("blockquote");
|
||||
n.innerHTML = "<ul>" + this.desc.map((e => `<li>${e}</li>`)).join("") + "</ul>", n.setAttribute("for", this.id), t.appendChild(n), document.getElementById("config-list-basic").appendChild(t), new ErrorToolTip(this.id).addChangeListener()
|
||||
}
|
||||
e.value = String(this.value)
|
||||
}
|
||||
setElementValue() {
|
||||
this.getInputElement().value = String(this.value)
|
||||
}
|
||||
setElementDefaultValue() {
|
||||
this.getInputElement().value = String(this.defval)
|
||||
}
|
||||
removeElement() {
|
||||
let e = this.getInputElement();
|
||||
e && e.parentElement.parentElement.remove()
|
||||
}
|
||||
showElement() {
|
||||
let e = this.getInputElement();
|
||||
e && e.parentElement.parentElement.style.removeProperty("display")
|
||||
}
|
||||
hideElement() {
|
||||
let e = this.getInputElement();
|
||||
e && e.parentElement.parentElement.style.setProperty("display", "none")
|
||||
}
|
||||
}
|
||||
class AdvEntry extends CustEntry {
|
||||
createElement() {
|
||||
let e = this.getInputElement();
|
||||
if (!e) {
|
||||
let t = document.createElement("div");
|
||||
t.classList.add("grid", "cust-element"), e = document.createElement("input"), e.min = String(this.zeroable ? 0 : this.min), e.max = String(this.max), e.id = this.id, e.type = "number", e.step = String(this.step);
|
||||
let i = document.createElement("label");
|
||||
i.setAttribute("for", this.id), i.innerHTML = this.name, i.appendChild(e), t.appendChild(i);
|
||||
let n = document.createElement("blockquote");
|
||||
n.innerHTML = "<ul>" + this.desc.map((e => `<li>${e}</li>`)).join("") + "</ul>", n.setAttribute("for", this.id), t.appendChild(n), document.getElementById("config-list-advanced").appendChild(t), new ErrorToolTip(this.id).addChangeListener()
|
||||
}
|
||||
e.value = String(this.value)
|
||||
}
|
||||
}
|
||||
class GpuEntry extends CustEntry {
|
||||
constructor(e, t, i = CustPlatform.Mariko, n = 4, a = ["range: 610 ≤ x ≤ 1000"], r = 610, s = [610, 1e3], l = 5, o = !1) {
|
||||
super(e, t, i, n, a, r, s, l, o), this.id = e, this.name = t, this.platform = i, this.size = n, this.desc = a, this.defval = r, this.step = l, this.zeroable = o
|
||||
}
|
||||
createElement() {
|
||||
let e = this.getInputElement();
|
||||
if (!e) {
|
||||
let t = document.createElement("div");
|
||||
t.classList.add("grid", "cust-element"), e = document.createElement("input"), e.min = String(this.zeroable ? 0 : this.min), e.max = String(this.max), e.id = this.id, e.type = "number", e.step = String(this.step);
|
||||
let i = document.createElement("label");
|
||||
i.setAttribute("for", this.id), i.innerHTML = this.name, i.appendChild(e), t.appendChild(i);
|
||||
let n = document.createElement("blockquote");
|
||||
n.innerHTML = "<ul>" + this.desc.map((e => `<li>${e}</li>`)).join("") + "</ul>", n.setAttribute("for", this.id), t.appendChild(n), document.getElementById("config-list-gpu").appendChild(t), new ErrorToolTip(this.id).addChangeListener()
|
||||
}
|
||||
e.value = String(this.value)
|
||||
}
|
||||
}
|
||||
var 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 wihout 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."], 1785e3, [102e4, 3e6], 1, !1), 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, [11e5, 125e4], 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, [16e5, 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, [16e5, 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, [55e4, 65e4], 5e3), 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 Configuation 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)],
|
||||
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)],
|
||||
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")];
|
||||
class ErrorToolTip {
|
||||
constructor(e, t) {
|
||||
this.id = e, this.msg = t, this.id = e, this.element = document.getElementById(e), t && this.setMsg(t)
|
||||
}
|
||||
setMsg(e) {
|
||||
return this.msg = e, this
|
||||
}
|
||||
show() {
|
||||
var e, t, i, n, a, r;
|
||||
return null === (e = this.element) || void 0 === e || e.setAttribute("aria-invalid", "true"), this.msg && (null === (t = this.element) || void 0 === t || t.setAttribute("title", this.msg), null === (n = null === (i = this.element) || void 0 === i ? void 0 : i.parentElement) || void 0 === n || n.setAttribute("data-tooltip", this.msg), null === (r = null === (a = this.element) || void 0 === a ? void 0 : a.parentElement) || void 0 === r || r.setAttribute("data-placement", "top")), this
|
||||
}
|
||||
clear() {
|
||||
var e, t, i, n, a, r;
|
||||
return null === (e = this.element) || void 0 === e || e.removeAttribute("aria-invalid"), null === (t = this.element) || void 0 === t || t.removeAttribute("title"), null === (n = null === (i = this.element) || void 0 === i ? void 0 : i.parentElement) || void 0 === n || n.removeAttribute("data-tooltip"), null === (r = null === (a = this.element) || void 0 === a ? void 0 : a.parentElement) || void 0 === r || r.removeAttribute("data-placement"), this
|
||||
}
|
||||
addChangeListener() {
|
||||
var e;
|
||||
null === (e = this.element) || void 0 === e || e.addEventListener("change", (e => {
|
||||
let t = CustTable.filter((e => e.id === this.id))[0];
|
||||
t.value = Number(this.element.value), t.validate()
|
||||
}))
|
||||
}
|
||||
}
|
||||
class CustStorage {
|
||||
constructor() {
|
||||
this.storage = {}, this.key = "last_saved"
|
||||
}
|
||||
updateFromTable() {
|
||||
let e = e => {
|
||||
var t;
|
||||
if (e.updateValueFromElement(), !e.validate()) throw null === (t = e.getInputElement()) || void 0 === t || t.focus(), new Error(`Invalid ${e.name}`)
|
||||
};
|
||||
CustTable.forEach(e), AdvTable.forEach(e), GpuTable.forEach(e), this.storage = {};
|
||||
let t = Object.fromEntries(CustTable.map((e => [e.id, e.value])));
|
||||
Object.keys(t).forEach((e => this.storage[e] = t[e])), t = Object.fromEntries(AdvTable.map((e => [e.id, e.value]))), Object.keys(t).forEach((e => this.storage[e] = t[e]))
|
||||
}
|
||||
setTable() {
|
||||
let e = Object.keys(this.storage);
|
||||
e.forEach((e => CustTable.filter((t => t.id == e))[0].value = this.storage[e])), e.forEach((e => AdvTable.filter((t => t.id == e))[0].value = this.storage[e])), CustTable.filter((t => !e.includes(t.id))).forEach((e => e.value = e.defval)), AdvTable.filter((t => !e.includes(t.id))).forEach((e => e.value = e.defval)), CustTable.forEach((e => {
|
||||
var t;
|
||||
if (!e.validate()) throw null === (t = e.getInputElement()) || void 0 === t || t.focus(), new Error(`Invalid ${e.name}`);
|
||||
e.setElementValue()
|
||||
})), AdvTable.forEach((e => {
|
||||
var t;
|
||||
if (!e.validate()) throw null === (t = e.getInputElement()) || void 0 === t || t.focus(), new Error(`Invalid ${e.name}`);
|
||||
e.setElementValue()
|
||||
})), GpuTable.forEach((e => {
|
||||
var t;
|
||||
if (!e.validate()) throw null === (t = e.getInputElement()) || void 0 === t || t.focus(), new Error(`Invalid ${e.name}`);
|
||||
e.setElementValue()
|
||||
}))
|
||||
}
|
||||
save() {
|
||||
localStorage.setItem(this.key, JSON.stringify(this.storage))
|
||||
}
|
||||
load() {
|
||||
let e = localStorage.getItem(this.key);
|
||||
if (!e) return null;
|
||||
let t = JSON.parse(e),
|
||||
i = CustTable.map((e => e.id)),
|
||||
n = Object.keys(t).filter((e => !i.includes(e)));
|
||||
return n.length && console.log(`Ignored: ${n}`), Object.keys(t).filter((e => i.includes(e))).forEach((e => this.storage[e] = t[e])), i = AdvTable.map((e => e.id)), n = Object.keys(t).filter((e => !i.includes(e))), n.length && console.log(`Ignored: ${n}`), Object.keys(t).filter((e => i.includes(e))).forEach((e => this.storage[e] = t[e])), this.storage
|
||||
}
|
||||
}
|
||||
class Cust {
|
||||
constructor() {
|
||||
this.storage = new CustStorage, this.magic = 1414747459, this.magicLen = 4, this.mapper = {
|
||||
2: {
|
||||
get: e => this.view.getUint16(e, !0),
|
||||
set: (e, t) => this.view.setUint16(e, t, !0)
|
||||
},
|
||||
4: {
|
||||
get: e => this.view.getUint32(e, !0),
|
||||
set: (e, t) => this.view.setUint32(e, t, !0)
|
||||
}
|
||||
}
|
||||
}
|
||||
findMagicOffset() {
|
||||
this.view = new DataView(this.buffer);
|
||||
for (let e = 0; e < this.view.byteLength; e += this.magicLen)
|
||||
if (this.mapper[this.magicLen].get(e) == this.magic) return void(this.beginOffset = e);
|
||||
throw new Error("Invalid loader.kip file")
|
||||
}
|
||||
save() {
|
||||
this.storage.updateFromTable();
|
||||
let e = e => {
|
||||
var t, i;
|
||||
if (!e.offset) throw null === (t = e.getInputElement()) || void 0 === t || t.focus(), new Error(`Failed to get offset for ${e.name}`);
|
||||
let n = this.mapper[e.size];
|
||||
if (!n) throw null === (i = e.getInputElement()) || void 0 === i || i.focus(), new Error(`Unknown size at ${e.name}`);
|
||||
n.set(e.offset, e.value)
|
||||
};
|
||||
CustTable.forEach(e), AdvTable.forEach(e), GpuTable.forEach(e), this.storage.save();
|
||||
let t = document.createElement("a");
|
||||
t.href = window.URL.createObjectURL(new Blob([this.buffer], {
|
||||
type: "application/octet-stream"
|
||||
})), t.download = "loader.kip", t.click(), this.toggleLoadLastSavedBtn(!0)
|
||||
}
|
||||
removeHTMLForm() {
|
||||
CustTable.forEach((e => e.removeElement()))
|
||||
}
|
||||
toggleLoadLastSavedBtn(e) {
|
||||
let t = document.getElementById("load_saved");
|
||||
e ? (t.addEventListener("click", (() => {
|
||||
this.storage.load() && this.storage.setTable()
|
||||
})), t.style.removeProperty("display"), t.removeAttribute("disabled")) : t.style.setProperty("display", "none")
|
||||
}
|
||||
createHTMLForm() {
|
||||
var e, t;
|
||||
CustTable.forEach((e => e.createElement()));
|
||||
let i = document.createElement("p");
|
||||
i.innerHTML = "Advanced configuration", null === (e = document.getElementById("config-list-advanced")) || void 0 === e || e.appendChild(i);
|
||||
let n = document.createElement("p");
|
||||
n.innerHTML = "Gpu Volt configuration", null === (t = document.getElementById("config-list-gpu")) || void 0 === t || t.appendChild(n), AdvTable.forEach((e => e.createElement())), GpuTable.forEach((e => e.createElement()));
|
||||
let a = document.getElementById("load_default");
|
||||
a.removeAttribute("disabled"), a.addEventListener("click", (() => {
|
||||
CustTable.forEach((e => e.setElementDefaultValue()))
|
||||
})), this.toggleLoadLastSavedBtn(null !== this.storage.load());
|
||||
let r = document.getElementById("save");
|
||||
r.removeAttribute("disabled"), r.addEventListener("click", (() => {
|
||||
try {
|
||||
this.save()
|
||||
} catch (e) {
|
||||
console.error(e), alert(e)
|
||||
}
|
||||
}))
|
||||
}
|
||||
initCustTabs() {
|
||||
const e = Array.from(document.querySelectorAll('nav[role="tablist"] > button'));
|
||||
e.forEach((t => {
|
||||
t.removeAttribute("disabled");
|
||||
let i = Number(t.getAttribute("data-platform"));
|
||||
t.addEventListener("click", (n => {
|
||||
const a = ["outline"];
|
||||
t.classList.remove(...a), e.filter((e => e != t)).forEach((e => e.classList.add(...a))), CustTable.forEach((e => {
|
||||
e.isAvailableFor(i) ? e.showElement() : e.hideElement()
|
||||
}))
|
||||
}))
|
||||
}))
|
||||
}
|
||||
parse() {
|
||||
let e = this.beginOffset + this.magicLen;
|
||||
if (this.rev = this.mapper[4].get(e), 11 != this.rev) throw new Error(`Unsupported custRev, expected: 11, got ${this.rev}`);
|
||||
e += 4, document.getElementById("cust_rev").innerHTML = `Cust v${this.rev} is loaded.`;
|
||||
let t = t => {
|
||||
var i;
|
||||
t.offset = e;
|
||||
let n = this.mapper[t.size];
|
||||
if (!n) throw null === (i = t.getInputElement()) || void 0 === i || i.focus(), new Error(`Unknown size at ${t}`);
|
||||
t.value = n.get(e), e += t.size, t.validate()
|
||||
};
|
||||
CustTable.forEach(t), AdvTable.forEach(t), GpuTable.forEach(t)
|
||||
}
|
||||
load(e) {
|
||||
try {
|
||||
this.buffer = e, this.findMagicOffset(), this.removeHTMLForm(), this.parse(), this.initCustTabs(), this.createHTMLForm()
|
||||
} catch (e) {
|
||||
console.error(e), alert(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
class ReleaseAsset {
|
||||
constructor(e) {
|
||||
this.downloadUrl = e.browser_download_url, this.updatedAt = e.updated_at
|
||||
}
|
||||
}
|
||||
class ReleaseInfo {
|
||||
constructor() {
|
||||
this.ocLatestApi = "https://api.github.com/repos/souldbminersmwc/Switch-OC-Suite-2/releases/latest"
|
||||
}
|
||||
load() {
|
||||
return __awaiter(this, void 0, void 0, (function*() {
|
||||
try {
|
||||
this.parseOcResponse(yield this.responseFromApi(this.ocLatestApi).catch())
|
||||
} catch (e) {
|
||||
console.error(e), alert(e)
|
||||
}
|
||||
}))
|
||||
}
|
||||
responseFromApi(e) {
|
||||
return __awaiter(this, void 0, void 0, (function*() {
|
||||
const t = yield fetch(e, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json"
|
||||
}
|
||||
});
|
||||
if (t.ok) return yield t.json();
|
||||
throw new Error(`Failed to connect to "${e}": ${t.status}`)
|
||||
}))
|
||||
}
|
||||
parseOcResponse(e) {
|
||||
this.ocVer = e.tag_name, this.loaderKipAsset = new ReleaseAsset(e.assets.filter((e => e.name.endsWith("loader.kip")))[0]), this.sdOutZipAsset = new ReleaseAsset(e.assets.filter((e => e.name.endsWith("SdOut.zip")))[0]), this.sysclkOCAsset = new ReleaseAsset(e.assets.filter((e => e.name.endsWith("sys-clk-oc.zip")))[0])
|
||||
}
|
||||
}
|
||||
class DownloadSection {
|
||||
constructor() {
|
||||
this.element = document.getElementById("download_btn_grid")
|
||||
}
|
||||
load() {
|
||||
return __awaiter(this, void 0, void 0, (function*() {
|
||||
for (; !this.isVisible();) yield new Promise((e => setTimeout(e, 1e3)));
|
||||
const e = new ReleaseInfo;
|
||||
yield e.load(), this.update("loader_kip_btn", `loader.kip <b>${e.ocVer}</b><br>${e.loaderKipAsset.updatedAt}`, e.loaderKipAsset.downloadUrl), this.update("sdout_zip_btn", `SdOut.zip <b>${e.ocVer}</b><br>${e.sdOutZipAsset.updatedAt}`, e.sdOutZipAsset.downloadUrl), this.update("ams_btn", `sys-clk-oc <b>${e.ocVer}</b><br>${e.sysclkOCAsset.updatedAt}`, e.sysclkOCAsset.downloadUrl)
|
||||
}))
|
||||
}
|
||||
isVisible() {
|
||||
let e = this.element.getBoundingClientRect();
|
||||
return e.top > 0 && e.left > 0 && e.bottom - e.height < (window.innerHeight || document.documentElement.clientHeight) && e.right - e.width < (window.innerWidth || document.documentElement.clientWidth)
|
||||
}
|
||||
update(e, t, i) {
|
||||
let n = document.getElementById(e);
|
||||
n.innerHTML = t, n.removeAttribute("aria-busy"), n.setAttribute("href", i)
|
||||
}
|
||||
}
|
||||
const fileInput = document.getElementById("file");
|
||||
fileInput.addEventListener("change", (e => {
|
||||
var t = new Cust;
|
||||
if (!e.target || !e.target.files) return;
|
||||
let i = new FileReader;
|
||||
i.readAsArrayBuffer(e.target.files[0]), i.onloadend = e => {
|
||||
e.target.readyState == FileReader.DONE && t.load(e.target.result)
|
||||
}
|
||||
})), addEventListener("DOMContentLoaded", (e => __awaiter(this, void 0, void 0, (function*() {
|
||||
yield(new DownloadSection).load()
|
||||
}))));
|
||||
445
pages/src/index.html
Normal file
445
pages/src/index.html
Normal file
@@ -0,0 +1,445 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Switch OC Suite | Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2.
|
||||
</title>
|
||||
<meta name="description"
|
||||
content="Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2.">
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<style>
|
||||
#nav-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#nav-popup {
|
||||
display: unset;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
#nav-list {
|
||||
display: unset;
|
||||
}
|
||||
|
||||
#nav-popup {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
details[open] > summary:not([role]):not(:focus) {
|
||||
color: unset;
|
||||
}
|
||||
|
||||
div.hero > nav.container-fluid {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
div.hero > header {
|
||||
padding-top: 6rem;
|
||||
}
|
||||
|
||||
button[role="tab"] {
|
||||
border-radius: 0;
|
||||
border-left-width: calc(var(--border-width) / 2);
|
||||
border-right-width: calc(var(--border-width) / 2);
|
||||
}
|
||||
|
||||
button[role="tab"]:first-child {
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
border-left-width: var(--border-width);
|
||||
}
|
||||
|
||||
button[role="tab"]:last-child {
|
||||
border-top-right-radius: var(--border-radius);
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
border-right-width: var(--border-width);
|
||||
}
|
||||
|
||||
button[role="tab"]:not(.outline) {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
div.cust-element {
|
||||
margin: 1.5rem 0 1.5rem 0;
|
||||
}
|
||||
|
||||
div.cust-element > blockquote, div.cust-element > blockquote > ul {
|
||||
margin: 0;
|
||||
font-size: calc(var(--font-size) * 0.8);
|
||||
color: var(--muted-color);
|
||||
}
|
||||
|
||||
div#download_btn_grid > a {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="hero">
|
||||
<nav class="container-fluid">
|
||||
<ul>
|
||||
<li><a href="#head" class="contrast"><b>Switch OC Suite</b></a></li>
|
||||
</ul>
|
||||
<ul id="nav-popup">
|
||||
<li>
|
||||
<details role="list" dir="rtl">
|
||||
<summary aria-haspopup="group" role="link" class="contrast">Jump to</summary>
|
||||
<ul role="group">
|
||||
<li><a href="#readme">README</a></li>
|
||||
<li><a href="#download">Download</a></li>
|
||||
<li><a href="#config">Configurator</a></li>
|
||||
<li><a href="https://github.com/hanai3Bi/Switch-OC-Suite" target="_blank">GitHub repository</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
</ul>
|
||||
<ul id="nav-list">
|
||||
<li><a href="#readme">README</a></li>
|
||||
<li><a href="#download">Download</a></li>
|
||||
<li><a href="#config">Config</a></li>
|
||||
<li><a href="https://github.com/hanai3Bi/Switch-OC-Suite" target="_blank">GitHub</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<header class="container">
|
||||
<hgroup>
|
||||
<h1>Switch OC Suite</h1>
|
||||
<h2>Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2.</h2>
|
||||
</hgroup>
|
||||
</header>
|
||||
</div>
|
||||
<section aria-label="README" id="readme">
|
||||
<div class="container">
|
||||
<article>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h2>README</h2>
|
||||
<h3>🚨DISCLAIMER: THIS IS PROVIDED AS IS. USE AT YOUR OWN RISK!🚨</h3>
|
||||
</hgroup>
|
||||
<ul>
|
||||
<li>
|
||||
Overclocking in general will shorten the lifespan of some
|
||||
hardware components. <strong>YOU ARE RESPONSIBLE for any problem or
|
||||
potential damage</strong> if unsafe frequencies are ENABLED in
|
||||
sys-clk-OC. Issues like asking for bypassing limit will BE IGNORED OR
|
||||
CLOSED WITHOUT REPLY.
|
||||
</li>
|
||||
<li>
|
||||
Due to HorizonOS design, instabilities from unsafe RAM clocks may
|
||||
cause filesystem corruption. <strong>Always make backup before enabling
|
||||
DRAM OC.</strong>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
|
||||
<body>
|
||||
<h3>Features</h3>
|
||||
<details open>
|
||||
<summary>For Erista variant (HAC-001)</summary>
|
||||
<ul>
|
||||
<li>CPU Overclock (Safe: 1785 MHz)
|
||||
<ul>
|
||||
<li>Unsafe
|
||||
<ul>
|
||||
<li>Due to the limit of board power draw or power IC</li>
|
||||
<li>Unlockable frequencies up to 2091 MHz</li>
|
||||
<li>See <a
|
||||
href="https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md"
|
||||
target="_blank">README
|
||||
for sys-clk-OC</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>DRAM Overclock (Safe: 1862.4 MHz)</li>
|
||||
</ul>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>For Mariko variant (HAC-001-01, HDH-001, HEG-001)</summary>
|
||||
<ul>
|
||||
<li>CPU / GPU Overclock (Safe: 1963 / 998 MHz)
|
||||
<ul>
|
||||
<li>Unsafe
|
||||
<ul>
|
||||
<li>Due to the limit of board power draw or power IC</li>
|
||||
<li>Unlockable frequencies up to 2295 / 1267 MHz</li>
|
||||
<li>See <a
|
||||
href="https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md"
|
||||
target="_blank">README
|
||||
for sys-clk-OC</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>DRAM Overclock (Safe: 1996.8 MHz)</li>
|
||||
</ul>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>Modded sys-clk and ReverseNX-RT</summary>
|
||||
<ul>
|
||||
<li STYLE="text-decoration:line-through">Auto CPU Boost
|
||||
<ul>
|
||||
<li>For faster game loading</li>
|
||||
<li>Enable CPU Boost (1785 MHz) when CPU Core#3 (System Core) is
|
||||
stressed (mainly I/O operations).</li>
|
||||
<li>Effective only when charger is connected or governor is enabled.</li>
|
||||
<li>This feature is considered unsafe on Erista, especially when combined with high GPU frequency or with governor enabled.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li STYLE="text-decoration:line-through">CPU & GPU frequency governor (Experimental)
|
||||
<ul>
|
||||
<li>Adjust frequency based on load. Might decrease power draw but can
|
||||
introduce stutters. Can be turned off for specific titles.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li STYLE="text-decoration:line-through">Set charging current (100 mA - 2000 mA) and charging limit (20% -
|
||||
100%)
|
||||
<ul>
|
||||
<li>Long-term use of charge limit may render the battery gauge
|
||||
inaccurate. Performing full cycles could help recalibration, or try <a
|
||||
href="https://github.com/CTCaer/battery_desync_fix_nx">battery_desync_fix_nx</a>.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Global Profile
|
||||
<ul>
|
||||
<li>Designated a dummy title id <code>0xA111111111111111</code>.</li>
|
||||
<li>Priority: "Temp overrides" > "Application profile" > "Global
|
||||
profile" > "System default".</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li STYLE="text-decoration:line-through">Sync ReverseNX Mode
|
||||
<ul>
|
||||
<li>No need to change clocks manually after toggling modes in ReverseNX
|
||||
(-RT and -Tool)</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>System Settings (Optional)</summary>
|
||||
See <a
|
||||
href="https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/system_settings.md">system_settings.md</a>
|
||||
</details>
|
||||
<h3 id="installation" open>Installation</h3>
|
||||
<ol type="1">
|
||||
<li>Download latest <a href="#download">release</a>.</li>
|
||||
<li>Grab <code>x.x.x_loader.kip</code> for your Atmosphere version, rename it to <code>loader.kip</code> and
|
||||
place it in <code>/atmosphere/kips/</code>.</li>
|
||||
<li>(optional) You can customize via <a href="#config">online loader configurator</a>
|
||||
<table role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Defaults</th>
|
||||
<th>Mariko</th>
|
||||
<th>Erista</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>CPU OC</td>
|
||||
<td>2295 MHz Max</td>
|
||||
<td>2091 MHz Max</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CPU Boost</td>
|
||||
<td>1785 MHz</td>
|
||||
<td>N/A</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CPU Volt</td>
|
||||
<td>1235 mV Max</td>
|
||||
<td>1235 mV Max</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GPU OC</td>
|
||||
<td>1267 MHz Max</td>
|
||||
<td>998 MHz Max</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RAM OC</td>
|
||||
<td>1996 MHz</td>
|
||||
<td>1862 MHz</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RAM Volt</td>
|
||||
<td>Disabled</td>
|
||||
<td>Disabled</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RAM Timing</td>
|
||||
<td>Auto-Adjusted</td>
|
||||
<td>Auto-Adjusted</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CPU UV</td>
|
||||
<td>Disabled</td>
|
||||
<td>N/A</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GPU UV</td>
|
||||
<td>Disabled</td>
|
||||
<td>N/A</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
<li>Hekate-ipl bootloader Only (fss0) (Not required for AMS fusee)
|
||||
<ul>
|
||||
<li>At boot entry section in <code>bootloader/hekate_ipl.ini</code>,
|
||||
Add <code>kip1=atmosphere/kips/loader.kip</code> to any line that works.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Install [sys-clk-oc] <br>official [sys-clk] (2.0.0+) is compatible but not recommended (no bugfixes or additional features).
|
||||
</li>
|
||||
<li>(optional) Copy SdOut.zip for useful utilities.
|
||||
</li>
|
||||
</ol>
|
||||
<details>
|
||||
<summary>How to build this project</summary>
|
||||
<ol type="1">
|
||||
<li>Grab necessary patches from the repo, then compile sys-clk, ReverseNX-RT and Atmosphere loader with
|
||||
devkitpro.</li>
|
||||
<li>Before compiling Atmosphere loader, run <code>patch.py</code> in
|
||||
<code>Atmosphere/stratosphere/loader/source/</code> to insert oc module into loader sysmodule.
|
||||
</li>
|
||||
<li>When compilation is done, uncompress the kip to make it work with configurator:
|
||||
<code>hactool -t kip1 Atmosphere/stratosphere/loader/out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=./loader.kip</code>
|
||||
</li>
|
||||
</ol>
|
||||
</details>
|
||||
<h3 id="faq">Frequently Asked Questions</h3>
|
||||
<details open>
|
||||
<summary>How to enable unsafe frequencies in sys-clk-OC?</summary>
|
||||
<ul>
|
||||
<li>Above all else, you should know <a href="#readme">what "unsafe" means and issues might arise</a>.</li>
|
||||
<li>See the end of <a href="https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md"
|
||||
target="_blank">README in sys-clk-OC</a>. Place this line <code>allow_unsafe_freq=1</code> under
|
||||
<code>[values]</code> section in
|
||||
<code>/config/sys-clk/config.ini</code>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>I would like to bypass limit enforced in sys-clk to improve handheld performance without charger
|
||||
connected.</summary>
|
||||
<ul>
|
||||
<li>Bypassing clock cappings will be bad for battery</a>.</li>
|
||||
<li>See the end of <a href="https://github.com/hanai3Bi/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md"
|
||||
target="_blank">README in sys-clk-OC</a>. Place this line <code>uncapped_clocks=1</code> under
|
||||
<code>[values]</code> section in
|
||||
<code>/config/sys-clk/config.ini</code>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
</body>
|
||||
<footer>
|
||||
<details role="list">
|
||||
<summary aria-haspopup="group" role="button" class="secondary">
|
||||
Acknowledgement
|
||||
</summary>
|
||||
<ul role="group">
|
||||
<li><a href="https://github.com/CTCaer/hekate" target="_blank">CTCaer for Hekate-ipl bootloader, RE and
|
||||
hardware research</a></li>
|
||||
<li><a href="https://devkitpro.org/" target="_blank">devkitPro for All-In-One homebrew toolchains</a>
|
||||
</li>
|
||||
<li><a href="https://github.com/masagrator/ReverseNX-RT" target="_blank">masagrator for ReverseNX-RT and
|
||||
info
|
||||
on BatteryChargeInfoFields in psm module</a></li>
|
||||
<li><a href="https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual"
|
||||
target="_blank">Nvidia for Tegra X1 Technical Reference Manual</a></li>
|
||||
<li><a href="https://github.com/retronx-team/sys-clk" target="_blank">RetroNX team for sys-clk</a></li>
|
||||
<li><a href="https://github.com/Atmosphere-NX/Atmosphere" target="_blank">SciresM and Reswitched Team for
|
||||
the
|
||||
state-of-the-art Atmosphere CFW of Switch</a></li>
|
||||
<li><a href="http://switchbrew.org/wiki/" target="_blank">Switchbrew wiki for Switch in-depth info</a>
|
||||
</li>
|
||||
<li><a href="https://gitlab.com/switchroot/kernel" target="_blank">Switchroot for their modified L4T
|
||||
kernel
|
||||
and device tree</a></li>
|
||||
<li>ZatchyCatGames for RE and original OC loader patches for Atmosphere</li>
|
||||
<li><a href="https://github.com/KazushiMe/Switch-OC-Suite" target="_blank">KazushiMe for original Switch-OC-Suite</a>
|
||||
<li>lineon for research and help</li>
|
||||
</ul>
|
||||
</details>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<section aria-label="Download" id="download">
|
||||
<div class="container">
|
||||
<article>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h2>Download</h2>
|
||||
<h3>Get latest version of Switch OC Suite and its corresponding Atmosphere package here.</h3>
|
||||
</hgroup>
|
||||
</header>
|
||||
|
||||
<body>
|
||||
<div class="grid" id="download_btn_grid">
|
||||
<a role="button" aria-busy="true" id="loader_kip_btn">Generating link, please wait...</a>
|
||||
<a role="button" aria-busy="true" id="sdout_zip_btn">Generating link, please wait...</a>
|
||||
<a target="_blank" role="button" aria-busy="true" id="ams_btn">Generating link, please wait...</a>
|
||||
</div>
|
||||
</body>
|
||||
<footer>
|
||||
See <a href="#installation">Installation section</a> for how to use Switch OC Suite.
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<section aria-label="Config" id="config">
|
||||
<div class="container">
|
||||
<article>
|
||||
<header>
|
||||
<hgroup>
|
||||
<h2>Configurator</h2>
|
||||
<h3>Configure frequencies and voltages to suit your hardware and preferences.</h3>
|
||||
</hgroup>
|
||||
</header>
|
||||
|
||||
<body>
|
||||
<form id="form">
|
||||
<nav role="tablist">
|
||||
<button type="button" role="tab" class="" id="tab_all" data-platform="0" disabled>All</button>
|
||||
<button type="button" role="tab" class="outline" id="tab_erista" data-platform="1" disabled>Erista</button>
|
||||
<button type="button" role="tab" class="outline" id="tab_mariko" data-platform="2" disabled>Mariko</button>
|
||||
</nav>
|
||||
<label for="file">
|
||||
<input id="file" type="file">
|
||||
<small id="cust_rev">Upload loader.kip here</small>
|
||||
</label>
|
||||
<div id="config-list-basic"></div>
|
||||
<div id="config-list-advanced"></div>
|
||||
<div id="config-list-gpu"></div>
|
||||
</form>
|
||||
</body>
|
||||
<footer>
|
||||
<div class="grid">
|
||||
<button type="button" id="load_default" role="button" disabled>Load Default</button>
|
||||
<button type="button" id="load_saved" role="button" disabled>Load Last Saved</button>
|
||||
<button type="submit" id="save" role="button" disabled>Save</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="container">
|
||||
<small>
|
||||
Build with <a href="https://picocss.com" target="_blank">Pico</a>.
|
||||
All trademarks, logos and brand names are the property of their respective owners, used for identification purposes
|
||||
only.
|
||||
</small>
|
||||
</footer>
|
||||
</body>
|
||||
<script type="text/javascript" src="./main.js"></script>
|
||||
|
||||
</html>
|
||||
916
pages/src/main.ts
Normal file
916
pages/src/main.ts
Normal file
@@ -0,0 +1,916 @@
|
||||
/* Config: Cust */
|
||||
const CUST_REV_ADV = 11;
|
||||
|
||||
enum CustPlatform {
|
||||
Undefined = 0,
|
||||
Erista = 1,
|
||||
Mariko = 2,
|
||||
All = 3,
|
||||
};
|
||||
|
||||
class CustEntry {
|
||||
min: number;
|
||||
max: number;
|
||||
value?: number;
|
||||
offset?: number;
|
||||
|
||||
constructor(
|
||||
public id: string,
|
||||
public name: string,
|
||||
public platform: CustPlatform,
|
||||
public size: number,
|
||||
public desc: string[],
|
||||
public defval: number,
|
||||
minmax: [number, number] = [0, 1_000_000],
|
||||
public step: number = 1,
|
||||
public zeroable: boolean = true) {
|
||||
this.min = minmax[0];
|
||||
this.max = minmax[1];
|
||||
};
|
||||
|
||||
validate(): boolean {
|
||||
let tip = new ErrorToolTip(this.id).clear();
|
||||
if (Number.isNaN(this.value) || this.value === undefined) {
|
||||
tip.setMsg(`Invalid value: Not a number`).show();
|
||||
return false;
|
||||
}
|
||||
if (this.zeroable && this.value == 0)
|
||||
return true;
|
||||
if (this.value < this.min || this.value > this.max) {
|
||||
tip.setMsg(`Expected range: [${this.min}, ${this.max}], got ${this.value}.`).show();
|
||||
return false;
|
||||
}
|
||||
if (this.value % this.step != 0) {
|
||||
tip.setMsg(`${this.value} % ${this.step} ≠ 0`).show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
getInputElement(): HTMLInputElement | null {
|
||||
return document.getElementById(this.id) as HTMLInputElement;
|
||||
}
|
||||
|
||||
updateValueFromElement() {
|
||||
this.value = Number(this.getInputElement()?.value);
|
||||
}
|
||||
|
||||
isAvailableFor(platform: CustPlatform): boolean {
|
||||
return platform === CustPlatform.Undefined || this.platform === platform || this.platform === CustPlatform.All;
|
||||
}
|
||||
|
||||
createElement() {
|
||||
let input = this.getInputElement();
|
||||
if (!input) {
|
||||
let grid = document.createElement("div");
|
||||
grid.classList.add("grid", "cust-element");
|
||||
|
||||
// Label and input
|
||||
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);
|
||||
let label = document.createElement("label");
|
||||
label.setAttribute("for", this.id);
|
||||
label.innerHTML = this.name;
|
||||
label.appendChild(input);
|
||||
grid.appendChild(label);
|
||||
|
||||
// Description in blockquote style
|
||||
let desc = document.createElement("blockquote");
|
||||
desc.innerHTML = "<ul>" + this.desc.map(i => `<li>${i}</li>`).join('') + "</ul>";
|
||||
desc.setAttribute("for", this.id);
|
||||
grid.appendChild(desc);
|
||||
|
||||
document.getElementById("config-list-basic")!.appendChild(grid);
|
||||
|
||||
new ErrorToolTip(this.id).addChangeListener();
|
||||
}
|
||||
input.value = String(this.value);
|
||||
}
|
||||
|
||||
setElementValue() {
|
||||
this.getInputElement()!.value = String(this.value!);
|
||||
}
|
||||
|
||||
setElementDefaultValue() {
|
||||
this.getInputElement()!.value = String(this.defval);
|
||||
}
|
||||
|
||||
removeElement() {
|
||||
let input = this.getInputElement();
|
||||
if (input) {
|
||||
input.parentElement!.parentElement!.remove();
|
||||
}
|
||||
}
|
||||
|
||||
showElement() {
|
||||
let input = this.getInputElement();
|
||||
if (input) {
|
||||
input.parentElement!.parentElement!.style.removeProperty("display");
|
||||
}
|
||||
}
|
||||
|
||||
hideElement() {
|
||||
let input = this.getInputElement();
|
||||
if (input) {
|
||||
input.parentElement!.parentElement!.style.setProperty("display", "none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AdvEntry extends CustEntry {
|
||||
createElement() {
|
||||
let input = this.getInputElement();
|
||||
if (!input) {
|
||||
let grid = document.createElement("div");
|
||||
grid.classList.add("grid", "cust-element");
|
||||
|
||||
// Label and input
|
||||
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);
|
||||
let label = document.createElement("label");
|
||||
label.setAttribute("for", this.id);
|
||||
label.innerHTML = this.name;
|
||||
label.appendChild(input);
|
||||
grid.appendChild(label);
|
||||
|
||||
// Description in blockquote style
|
||||
let desc = document.createElement("blockquote");
|
||||
desc.innerHTML = "<ul>" + this.desc.map(i => `<li>${i}</li>`).join('') + "</ul>";
|
||||
desc.setAttribute("for", this.id);
|
||||
grid.appendChild(desc);
|
||||
|
||||
document.getElementById("config-list-advanced")!.appendChild(grid);
|
||||
|
||||
new ErrorToolTip(this.id).addChangeListener();
|
||||
}
|
||||
input.value = String(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
class GpuEntry extends CustEntry {
|
||||
constructor(
|
||||
public id: string,
|
||||
public name: string,
|
||||
public platform: CustPlatform = CustPlatform.Mariko,
|
||||
public size: number = 4,
|
||||
public desc: string[] = ["range: 610 ≤ x ≤ 1000"],
|
||||
public defval: number = 610,
|
||||
minmax: [number, number] = [610, 1000],
|
||||
public step: number = 5,
|
||||
public zeroable: boolean = false) {
|
||||
super(id, name, platform, size, desc, defval, minmax, step, zeroable);
|
||||
};
|
||||
|
||||
createElement() {
|
||||
let input = this.getInputElement();
|
||||
if (!input) {
|
||||
let grid = document.createElement("div");
|
||||
grid.classList.add("grid", "cust-element");
|
||||
|
||||
// Label and input
|
||||
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);
|
||||
let label = document.createElement("label");
|
||||
label.setAttribute("for", this.id);
|
||||
label.innerHTML = this.name;
|
||||
label.appendChild(input);
|
||||
grid.appendChild(label);
|
||||
|
||||
// Description in blockquote style
|
||||
let desc = document.createElement("blockquote");
|
||||
desc.innerHTML = "<ul>" + this.desc.map(i => `<li>${i}</li>`).join('') + "</ul>";
|
||||
desc.setAttribute("for", this.id);
|
||||
grid.appendChild(desc);
|
||||
|
||||
document.getElementById("config-list-gpu")!.appendChild(grid);
|
||||
|
||||
new ErrorToolTip(this.id).addChangeListener();
|
||||
}
|
||||
input.value = String(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
var CustTable: Array<CustEntry> = [
|
||||
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 wihout 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."],
|
||||
1785_000,
|
||||
[1020_000, 3000_000],
|
||||
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,
|
||||
[1100_000, 1250_000],
|
||||
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"],
|
||||
1862_400,
|
||||
[1600_000, 2131_200],
|
||||
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."],
|
||||
1996_800,
|
||||
[1600_000, 2502_400],
|
||||
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,
|
||||
[550_000, 650_000],
|
||||
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 Configuation 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,
|
||||
),
|
||||
];
|
||||
|
||||
var AdvTable: Array<AdvEntry> = [
|
||||
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,
|
||||
)
|
||||
];
|
||||
|
||||
var GpuTable: Array<GpuEntry> = [
|
||||
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",),
|
||||
];
|
||||
|
||||
class ErrorToolTip {
|
||||
element: HTMLElement | null;
|
||||
|
||||
constructor(public id: string, public msg?: string) {
|
||||
this.id = id;
|
||||
this.element = document.getElementById(id);
|
||||
if (msg) { this.setMsg(msg); }
|
||||
};
|
||||
|
||||
setMsg(msg: string): ErrorToolTip {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
|
||||
show(): ErrorToolTip {
|
||||
this.element?.setAttribute("aria-invalid", "true");
|
||||
if (this.msg) {
|
||||
this.element?.setAttribute("title", this.msg);
|
||||
this.element?.parentElement?.setAttribute("data-tooltip", this.msg);
|
||||
this.element?.parentElement?.setAttribute("data-placement", "top");
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
clear(): ErrorToolTip {
|
||||
this.element?.removeAttribute("aria-invalid");
|
||||
this.element?.removeAttribute("title");
|
||||
this.element?.parentElement?.removeAttribute("data-tooltip");
|
||||
this.element?.parentElement?.removeAttribute("data-placement");
|
||||
return this;
|
||||
}
|
||||
|
||||
addChangeListener() {
|
||||
this.element?.addEventListener('change', (_evt) => {
|
||||
let obj = CustTable.filter((obj) => { return obj.id === this.id; })[0];
|
||||
obj.value = Number((this.element as HTMLInputElement).value);
|
||||
obj.validate();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class CustStorage {
|
||||
storage: { [key: string]: (number | undefined) } = {};
|
||||
readonly key = "last_saved";
|
||||
|
||||
updateFromTable() {
|
||||
let update = (i => {
|
||||
i.updateValueFromElement();
|
||||
if (!i.validate()) {
|
||||
i.getInputElement()?.focus();
|
||||
throw new Error(`Invalid ${i.name}`);
|
||||
}
|
||||
});
|
||||
CustTable.forEach(update);
|
||||
AdvTable.forEach(update);
|
||||
GpuTable.forEach(update);
|
||||
|
||||
this.storage = {};
|
||||
let kv = Object.fromEntries(CustTable.map((i) => [i.id, i.value]));
|
||||
Object.keys(kv)
|
||||
.forEach(k => this.storage[k] = kv[k]);
|
||||
kv = Object.fromEntries(AdvTable.map((i) => [i.id, i.value]));
|
||||
Object.keys(kv)
|
||||
.forEach(k => this.storage[k] = kv[k]);
|
||||
}
|
||||
|
||||
setTable() {
|
||||
let keys = Object.keys(this.storage);
|
||||
keys.forEach(k => CustTable.filter(i => i.id == k)[0].value = this.storage[k]);
|
||||
keys.forEach(k => AdvTable.filter(i => i.id == k)[0].value = this.storage[k]);
|
||||
|
||||
// Set default for missing values
|
||||
CustTable.filter(i => !keys.includes(i.id))
|
||||
.forEach(i => i.value = i.defval);
|
||||
AdvTable.filter(i => !keys.includes(i.id))
|
||||
.forEach(i => i.value = i.defval);
|
||||
|
||||
CustTable.forEach(i => {
|
||||
if (!i.validate()) {
|
||||
i.getInputElement()?.focus();
|
||||
throw new Error(`Invalid ${i.name}`);
|
||||
}
|
||||
i.setElementValue();
|
||||
});
|
||||
AdvTable.forEach(i => {
|
||||
if (!i.validate()) {
|
||||
i.getInputElement()?.focus();
|
||||
throw new Error(`Invalid ${i.name}`);
|
||||
}
|
||||
i.setElementValue();
|
||||
});
|
||||
GpuTable.forEach(i => {
|
||||
if (!i.validate()) {
|
||||
i.getInputElement()?.focus();
|
||||
throw new Error(`Invalid ${i.name}`);
|
||||
}
|
||||
i.setElementValue();
|
||||
});
|
||||
}
|
||||
|
||||
save() {
|
||||
localStorage.setItem(this.key, JSON.stringify(this.storage));
|
||||
}
|
||||
|
||||
load(): { [key: string]: (number | undefined) } | null {
|
||||
let s = localStorage.getItem(this.key);
|
||||
if (!s) {
|
||||
return null;
|
||||
}
|
||||
let dict = JSON.parse(s);
|
||||
let keys = CustTable.map(i => i.id);
|
||||
let ignoredKeys: string[] = Object.keys(dict).filter(k => !keys.includes(k));
|
||||
if (ignoredKeys.length) {
|
||||
console.log(`Ignored: ${ignoredKeys}`);
|
||||
}
|
||||
Object.keys(dict)
|
||||
.filter(k => keys.includes(k))
|
||||
.forEach(k => this.storage[k] = dict[k]);
|
||||
keys = AdvTable.map(i => i.id);
|
||||
ignoredKeys = Object.keys(dict).filter(k => !keys.includes(k));
|
||||
if (ignoredKeys.length) {
|
||||
console.log(`Ignored: ${ignoredKeys}`);
|
||||
}
|
||||
Object.keys(dict)
|
||||
.filter(k => keys.includes(k))
|
||||
.forEach(k => this.storage[k] = dict[k]);
|
||||
return this.storage;
|
||||
}
|
||||
}
|
||||
|
||||
class Cust {
|
||||
buffer: ArrayBuffer;
|
||||
view: DataView;
|
||||
beginOffset: number;
|
||||
storage: CustStorage = new CustStorage();
|
||||
readonly magic = 0x54535543; // "CUST"
|
||||
readonly magicLen = 4;
|
||||
rev: number;
|
||||
|
||||
mapper : {[size: number]: { get: any, set: any }} = {
|
||||
2: {
|
||||
get: (offset: number) => this.view.getUint16(offset, true),
|
||||
set: (offset: number, value: number) => this.view.setUint16(offset, value, true),
|
||||
},
|
||||
4: {
|
||||
get: (offset: number) => this.view.getUint32(offset, true),
|
||||
set: (offset: number, value: number) => this.view.setUint32(offset, value, 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");
|
||||
}
|
||||
|
||||
save() {
|
||||
this.storage.updateFromTable();
|
||||
let saveValue = (i => {
|
||||
if (!i.offset) {
|
||||
i.getInputElement()?.focus();
|
||||
throw new Error(`Failed to get offset for ${i.name}`);
|
||||
}
|
||||
let mapper = this.mapper[i.size];
|
||||
if (!mapper) {
|
||||
i.getInputElement()?.focus();
|
||||
throw new Error(`Unknown size at ${i.name}`);
|
||||
}
|
||||
mapper.set(i.offset, i.value!);
|
||||
});
|
||||
CustTable.forEach(saveValue);
|
||||
AdvTable.forEach(saveValue);
|
||||
GpuTable.forEach(saveValue);
|
||||
|
||||
this.storage.save();
|
||||
|
||||
let a = document.createElement("a");
|
||||
a.href = window.URL.createObjectURL(new Blob([this.buffer], { type: "application/octet-stream" }));
|
||||
a.download = "loader.kip";
|
||||
a.click();
|
||||
|
||||
this.toggleLoadLastSavedBtn(true);
|
||||
}
|
||||
|
||||
removeHTMLForm() {
|
||||
CustTable.forEach(i => i.removeElement());
|
||||
}
|
||||
|
||||
toggleLoadLastSavedBtn(enable: boolean) {
|
||||
let last_btn = document.getElementById("load_saved")!;
|
||||
if (enable) {
|
||||
last_btn.addEventListener('click', () => {
|
||||
if (this.storage.load()) {
|
||||
this.storage.setTable();
|
||||
}
|
||||
});
|
||||
last_btn.style.removeProperty("display");
|
||||
last_btn.removeAttribute("disabled");
|
||||
} else {
|
||||
last_btn.style.setProperty("display", "none");
|
||||
}
|
||||
}
|
||||
|
||||
createHTMLForm() {
|
||||
CustTable.forEach(i => i.createElement());
|
||||
|
||||
let advanced = document.createElement("p");
|
||||
advanced.innerHTML = "Advanced configuration";
|
||||
document.getElementById("config-list-advanced")?.appendChild(advanced);
|
||||
|
||||
let gpu = document.createElement("p");
|
||||
gpu.innerHTML = "Gpu Volt configuration";
|
||||
document.getElementById("config-list-gpu")?.appendChild(gpu);
|
||||
|
||||
AdvTable.forEach(i => i.createElement());
|
||||
GpuTable.forEach(i => i.createElement());
|
||||
|
||||
let default_btn = document.getElementById("load_default")!;
|
||||
default_btn.removeAttribute("disabled");
|
||||
default_btn.addEventListener('click', () => {
|
||||
CustTable.forEach(i => i.setElementDefaultValue());
|
||||
});
|
||||
|
||||
this.toggleLoadLastSavedBtn(this.storage.load() !== null);
|
||||
|
||||
let save_btn = document.getElementById("save")!;
|
||||
save_btn.removeAttribute("disabled");
|
||||
save_btn.addEventListener('click', () => {
|
||||
try {
|
||||
this.save();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initCustTabs() {
|
||||
const custTabs = Array.from(document.querySelectorAll(`nav[role="tablist"] > button`)) as HTMLElement[];
|
||||
custTabs.forEach(tab => {
|
||||
tab.removeAttribute("disabled");
|
||||
let platform = Number(tab.getAttribute("data-platform")!) as CustPlatform;
|
||||
tab.addEventListener('click', (_evt) => {
|
||||
// Set other tabs to unfocused state
|
||||
const unfocusedClasses = ["outline"];
|
||||
tab.classList.remove(...unfocusedClasses);
|
||||
let otherTabs = custTabs.filter(j => j != tab);
|
||||
otherTabs.forEach(k => k.classList.add(...unfocusedClasses));
|
||||
|
||||
CustTable.forEach(e => {
|
||||
e.isAvailableFor(platform) ? e.showElement() : e.hideElement();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
parse() {
|
||||
let offset = this.beginOffset + this.magicLen;
|
||||
let revLen = 4;
|
||||
this.rev = this.mapper[revLen].get(offset);
|
||||
if (this.rev != CUST_REV_ADV) {
|
||||
throw new Error(`Unsupported custRev, expected: ${CUST_REV_ADV}, got ${this.rev}`);
|
||||
}
|
||||
offset += revLen;
|
||||
document.getElementById("cust_rev")!.innerHTML = `Cust v${this.rev} is loaded.`;
|
||||
|
||||
let loadValue = (i => {
|
||||
i.offset = offset;
|
||||
let mapper = this.mapper[i.size];
|
||||
if (!mapper) {
|
||||
i.getInputElement()?.focus();
|
||||
throw new Error(`Unknown size at ${i}`);
|
||||
}
|
||||
i.value = mapper.get(offset);
|
||||
offset += i.size;
|
||||
i.validate();
|
||||
});
|
||||
|
||||
CustTable.forEach(loadValue);
|
||||
AdvTable.forEach(loadValue);
|
||||
GpuTable.forEach(loadValue);
|
||||
}
|
||||
|
||||
load(buffer: ArrayBuffer) {
|
||||
try {
|
||||
this.buffer = buffer;
|
||||
this.findMagicOffset();
|
||||
this.removeHTMLForm();
|
||||
this.parse();
|
||||
this.initCustTabs();
|
||||
this.createHTMLForm();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* GitHub Release fetch */
|
||||
class ReleaseAsset {
|
||||
downloadUrl: string;
|
||||
updatedAt: string;
|
||||
|
||||
constructor (obj: { browser_download_url: string; updated_at: string; }) {
|
||||
this.downloadUrl = obj.browser_download_url;
|
||||
this.updatedAt = obj.updated_at;
|
||||
};
|
||||
};
|
||||
|
||||
class ReleaseInfo {
|
||||
ocVer: string;
|
||||
loaderKipAsset: ReleaseAsset;
|
||||
sysclkOCAsset: ReleaseAsset
|
||||
sdOutZipAsset: ReleaseAsset;
|
||||
|
||||
readonly ocLatestApi = "https://api.github.com/repos/hanai3Bi/Switch-OC-Suite/releases/latest";
|
||||
|
||||
async load() {
|
||||
try {
|
||||
this.parseOcResponse(await this.responseFromApi(this.ocLatestApi).catch());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert(e);
|
||||
}
|
||||
};
|
||||
|
||||
async responseFromApi(apiUrl: string) : Promise<any> {
|
||||
const response = await fetch(apiUrl, { method: 'GET', headers: { Accept: 'application/json' } } );
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
throw new Error(`Failed to connect to "${apiUrl}": ${response.status}`);
|
||||
};
|
||||
|
||||
parseOcResponse(response) {
|
||||
this.ocVer = response.tag_name;
|
||||
this.loaderKipAsset = new ReleaseAsset(response.assets.filter( a => a.name.endsWith("loader.kip") )[0]);
|
||||
this.sdOutZipAsset = new ReleaseAsset(response.assets.filter( a => a.name.endsWith("SdOut.zip") )[0]);
|
||||
this.sysclkOCAsset = new ReleaseAsset(response.assets.filter( a => a.name.endsWith("sys-clk-oc.zip") )[0]);
|
||||
};
|
||||
};
|
||||
|
||||
class DownloadSection {
|
||||
readonly element: HTMLElement = document.getElementById("download_btn_grid")!;
|
||||
|
||||
async load() {
|
||||
while(!this.isVisible()) {
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
}
|
||||
|
||||
const info = new ReleaseInfo()
|
||||
await info.load();
|
||||
this.update("loader_kip_btn", `loader.kip <b>${info.ocVer}</b><br>${info.loaderKipAsset.updatedAt}`, info.loaderKipAsset.downloadUrl);
|
||||
this.update("sdout_zip_btn", `SdOut.zip <b>${info.ocVer}</b><br>${info.sdOutZipAsset.updatedAt}`, info.sdOutZipAsset.downloadUrl);
|
||||
this.update("ams_btn", `sys-clk-oc <b>${info.ocVer}</b><br>${info.sysclkOCAsset.updatedAt}`, info.sysclkOCAsset.downloadUrl);
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
let rect = this.element.getBoundingClientRect();
|
||||
return (
|
||||
rect.top > 0 &&
|
||||
rect.left > 0 &&
|
||||
rect.bottom - rect.height < (window.innerHeight || document.documentElement.clientHeight) &&
|
||||
rect.right - rect.width < (window.innerWidth || document.documentElement.clientWidth)
|
||||
);
|
||||
}
|
||||
|
||||
update(id: string, name: string, url: string) {
|
||||
let element = document.getElementById(id)!;
|
||||
element.innerHTML = name;
|
||||
element.removeAttribute("aria-busy");
|
||||
element.setAttribute("href", url);
|
||||
}
|
||||
}
|
||||
|
||||
const fileInput = document.getElementById("file") as HTMLInputElement;
|
||||
fileInput.addEventListener('change', (event) => {
|
||||
var cust: Cust = new Cust();
|
||||
// User canceled or non files selected
|
||||
if (!event.target || !(event.target as HTMLInputElement).files) {
|
||||
return;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer((event.target as HTMLInputElement).files![0]);
|
||||
reader.onloadend = (progEvent) => {
|
||||
if (progEvent.target!.readyState == FileReader.DONE) {
|
||||
cust.load(progEvent.target!.result! as ArrayBuffer);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
addEventListener('DOMContentLoaded', async (_evt) => {
|
||||
await new DownloadSection().load();
|
||||
});
|
||||
Reference in New Issue
Block a user