From bb4a6362dd34a0ad1359f30cddbf54ebfa4b84b6 Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 12 May 2021 23:42:11 +0200 Subject: [PATCH] New generation style! * More dynamic: Includes version in the index.html * Still no external dependencies. * Added lockpick and TegraExplorer --- .gitignore | 3 + Makefile | 15 ++++ gen_bin_js.py | 85 ------------------ gen_contents.py | 148 ++++++++++++++++++++++++++++++++ html/core.html | 28 ++++++ html/post.html | 12 +++ index.html => html/preface.html | 117 ++++++++++--------------- main.js | 4 + 8 files changed, 253 insertions(+), 159 deletions(-) create mode 100644 Makefile delete mode 100644 gen_bin_js.py create mode 100644 gen_contents.py create mode 100644 html/core.html create mode 100644 html/post.html rename index.html => html/preface.html (64%) diff --git a/.gitignore b/.gitignore index 81cbfef..63cdff3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ hekate.bin.js +lockpick.bin.js +tegraexplorer.bin.js +index.html diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..aacc37f --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +generate_files: + @python gen_contents.py + +clean: + @rm hekate.bin.js + @rm lockpick.bin.js + @rm tegraexplorer.bin.js + @rm index.html + +.PHONY: help + +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.DEFAULT_GOAL := help diff --git a/gen_bin_js.py b/gen_bin_js.py deleted file mode 100644 index bb6af51..0000000 --- a/gen_bin_js.py +++ /dev/null @@ -1,85 +0,0 @@ -import requests -import io -import shutil -import zipfile - -API_URL = "https://api.github.com/repos/CTCaer/hekate/releases/latest" -BIN_NAME = "hekate_ctcaer_" -SERIALIZED_FILENAME = "hekate.bin.js" - -### BASIC FILE STUFF, DO NOT EDIT -DEFAULT_SERIALIZED_CONTENTS = """ -const hekate = new Uint8Array([ - {} -]); -""" - -## Script that downloads the latest bin from hekate and serializes it as a .bin.js file. - -def download_file_to_bytes_io(url) -> io.BytesIO: - r = requests.get(url) - filedata = io.BytesIO() - if r.status_code == 200: - filedata.write(r.content) - filedata.seek(0) - return filedata - -def extract_specific_file_from_zip(zip_file) -> io.BytesIO: - filename = None - filedata = io.BytesIO() - with zipfile.ZipFile(zip_file) as z: - for zinfo in z.infolist(): - if zinfo.filename.startswith(BIN_NAME): - filename = zinfo.filename - break - if filename is None: - raise Exception("Zipfile does not contain required match.") - - with z.open(filename) as zfile: - shutil.copyfileobj(zfile, filedata) - filedata.seek(0) - return filedata - -def fetch_hekate_zip() -> io.BytesIO: - r = requests.get(API_URL) - jdata = r.json() - return download_file_to_bytes_io([d for d in jdata["assets"] if d["name"].startswith("hekate_ctcaer")][0]["browser_download_url"]) - -def get_hekate_payload(hekate_zip: io.BytesIO) -> io.BytesIO: - return extract_specific_file_from_zip(hekate_zip) - -def serialize_to_js(hekate_payload: io.BytesIO, filename: str): - hekate_payload = hekate_payload.read() - - # List so we can loop byte for byte - serialized = [bytes([b]) for b in hekate_payload] - final_str = [] - - # Formatting - max_ctr = 16 - str_on_line = 0 - - # Special check to ensure that a comma isn't placed on the end - last_byte = len(serialized) - 1 - for idx, byte in enumerate(serialized): - if idx != last_byte: - final_str.append(f"0x{byte.hex()}, ") - else: - final_str.append(f"0x{byte.hex()}") - - # More readable - str_on_line += 1 - if str_on_line >= max_ctr: - str_on_line = 0 - final_str.append("\n ") - - final_str = "".join(final_str) - - out_string = DEFAULT_SERIALIZED_CONTENTS.format(final_str) - with open(filename, "w") as outfile: - outfile.write(out_string) - -if __name__ == "__main__": - api_response = fetch_hekate_zip() - payload = get_hekate_payload(api_response) - serialize_to_js(payload, SERIALIZED_FILENAME) \ No newline at end of file diff --git a/gen_contents.py b/gen_contents.py new file mode 100644 index 0000000..a552ffe --- /dev/null +++ b/gen_contents.py @@ -0,0 +1,148 @@ +import requests +import io +import shutil +import zipfile + +bin_files = { + "hekate": { + "API_URL": "https://api.github.com/repos/CTCaer/hekate/releases/latest", # API version url + "BIN_NAME": "hekate_ctcaer", # BIN_NAME -> used to determine what file to extract from zip or what to download from API + "SERIALIZED_FILENAME": "hekate.bin.js", # File to store serialized contents in. + "SHORT_NAME": "hekate", + "zipped": True # Do we need to extract a zip or is it just a raw payload. + }, + "tegraexplorer": { + "API_URL": "https://api.github.com/repos/suchmememanyskill/TegraExplorer/releases/latest", + "BIN_NAME": "TegraExplorer", + "SERIALIZED_FILENAME": "tegraexplorer.bin.js", + "SHORT_NAME": "tegraexplorer", + "zipped": False, + }, + "lockpick": { + "API_URL": "https://api.github.com/repos/shchmue/Lockpick_RCM/releases/latest", + "BIN_NAME": "Lockpick_RCM", + "SERIALIZED_FILENAME": "lockpick.bin.js", + "SHORT_NAME": "lockpick", + "zipped": False, + }, +} + +### BASIC FILE STUFF, DO NOT EDIT +DEFAULT_SERIALIZED_CONTENTS = """ +const {} = new Uint8Array([ + {} +]); +""" + +## Script that downloads the latest bin from hekate and serializes it as a .bin.js file. + +def download_file_to_bytes_io(url) -> io.BytesIO: + r = requests.get(url) + filedata = io.BytesIO() + if r.status_code == 200: + filedata.write(r.content) + filedata.seek(0) + return filedata + +def extract_specific_file_from_zip(zip_file, bin_name) -> io.BytesIO: + filename = None + filedata = io.BytesIO() + with zipfile.ZipFile(zip_file) as z: + for zinfo in z.infolist(): + if zinfo.filename.startswith(bin_name): + filename = zinfo.filename + break + if filename is None: + raise Exception("Zipfile does not contain required match.") + + with z.open(filename) as zfile: + shutil.copyfileobj(zfile, filedata) + filedata.seek(0) + return filedata + +def fetch_hekate_zip(api_url) -> io.BytesIO: + r = requests.get(api_url) + jdata = r.json() + return download_file_to_bytes_io([d for d in jdata["assets"] if d["name"].startswith("hekate_ctcaer")][0]["browser_download_url"]) + +def fetch_github_repo_file(api_url, filename) -> io.BytesIO: + r = requests.get(api_url) + jdata = r.json() + return download_file_to_bytes_io([d for d in jdata["assets"] if d["name"].startswith(filename)][0]["browser_download_url"]) + +def fetch_github_latest_tag(api_url) -> io.BytesIO: + r = requests.get(api_url) + jdata = r.json() + return jdata["tag_name"] + +def serialize_to_js(short_name: str, payload: io.BytesIO, filename: str): + payload = payload.read() + + # List so we can loop byte for byte + serialized = [bytes([b]) for b in payload] + final_str = [] + + # Formatting + max_ctr = 16 + str_on_line = 0 + + # Special check to ensure that a comma isn't placed on the end + last_byte = len(serialized) - 1 + for idx, byte in enumerate(serialized): + if idx != last_byte: + final_str.append(f"0x{byte.hex()}, ") + else: + final_str.append(f"0x{byte.hex()}") + + # More readable + str_on_line += 1 + if str_on_line >= max_ctr: + str_on_line = 0 + final_str.append("\n ") + + final_str = "".join(final_str) + + out_string = DEFAULT_SERIALIZED_CONTENTS.format(short_name, final_str) + with open(filename, "w") as outfile: + outfile.write(out_string) + +def generate_js(program: str): + api_response = fetch_github_repo_file(bin_files[program]["API_URL"], bin_files[program]["BIN_NAME"]) + if bin_files[program]["zipped"]: + payload = extract_specific_file_from_zip(api_response, bin_files[program]["BIN_NAME"]) + else: + payload = api_response + serialize_to_js(bin_files[program]["SHORT_NAME"], payload, bin_files[program]["SERIALIZED_FILENAME"]) + +def generate_html(): + with open("html/preface.html", "r") as preface_file: + preface = preface_file.read() + + with open("html/core.html", 'r') as template_file: + core = template_file.read() + + with open("html/post.html", "r") as post_file: + post = post_file.read() + + html_data_list = [] + html_data_list.append(preface) + html_data_list.append(core.format( + fetch_github_latest_tag(bin_files["hekate"]["API_URL"]), + fetch_github_latest_tag(bin_files["tegraexplorer"]["API_URL"]), + fetch_github_latest_tag(bin_files["lockpick"]["API_URL"]), + )) + html_data_list.append(post) + + + out_string = "".join(html_data_list) + with open("index.html", "w") as indexfile: + indexfile.write(out_string) + +def main(): + generate_js("hekate") + generate_js("tegraexplorer") + generate_js("lockpick") + generate_html() + +if __name__ == "__main__": + main() diff --git a/html/core.html b/html/core.html new file mode 100644 index 0000000..c91fdd4 --- /dev/null +++ b/html/core.html @@ -0,0 +1,28 @@ +

Payload:

+
+
+

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+ + + +

+
+ + +
diff --git a/html/post.html b/html/post.html new file mode 100644 index 0000000..bd355f4 --- /dev/null +++ b/html/post.html @@ -0,0 +1,12 @@ + +

Result:

+ + + + + + + + + + diff --git a/index.html b/html/preface.html similarity index 64% rename from index.html rename to html/preface.html index 5f12076..9723451 100644 --- a/index.html +++ b/html/preface.html @@ -1,74 +1,43 @@ - - - - - - WebFG - - - -

Web Fusée Launcher

-

Fusee Launcher ported to JavaScript using WebUSB.

-

- Source can be found on GitHub (or by hitting view source, there is no backend!). - Ported from fusee-launcher. - Thanks to ktemkin and ReSwitched for Fusée Gelée and a ton of other things! -

- -

-

Instructions:

-
    -
  1. Put the Switch in RCM, and connect it to your device.
  2. -
  3. Select either the example payload, or upload one.
  4. -
  5. Press 'Do the thing!'
  6. -
  7. On the consent screen that appears, select 'APX' and hit confirm.
  8. -
  9. If all goes well, the payload will launch!
  10. -
-

- -

-

Random stuff:

- -

- -

Payload:

-
-
-

- - -

-

- - -

- -

- - - -

-
- - -
- -

Result:

- - - - - - - - + + + + + + WebFG + + + +

Web Fusée Launcher

+

Fusee Launcher ported to JavaScript using WebUSB.

+

+ Source can be found on GitHub (or by hitting view source, there is no backend!). + Ported from fusee-launcher. + Thanks to ktemkin and ReSwitched for Fusée Gelée and a ton of other things! +

+ +

+

Instructions:

+
    +
  1. Put the Switch in RCM, and connect it to your device.
  2. +
  3. Select either the example payload, or upload one.
  4. +
  5. Press 'Do the thing!'
  6. +
  7. On the consent screen that appears, select 'APX' and hit confirm.
  8. +
  9. If all goes well, the payload will launch!
  10. +
+

+ +

+

Random stuff:

+ +

+ diff --git a/main.js b/main.js index db73779..213f150 100644 --- a/main.js +++ b/main.js @@ -117,6 +117,10 @@ document.getElementById("goButton").addEventListener("click", async () => { payload = fusee; } else if (payloadType === "hekate.bin") { payload = hekate; + } else if (payloadType === "tegraexplorer.bin") { + payload = tegraexplorer; + } else if (payloadType === "lockpick.bin") { + payload = lockpick; } else if (payloadType === "uploaded") { const file = document.getElementById("payloadUpload").files[0]; if (!file) {