v0.17.1: Rebuilt with latest SDK

Should work on all firmwares, up to 13.60.
This commit is contained in:
maj0r committed 2026-09-30 14:32:13 -03:00
1 parent 89095e4b8a
commit 548f21a41f
9 files changed
+210 -199

No files matched your search

+8 -1
View File
@@ -1,6 +1,11 @@
# CheatRunner — Changelog
## v0.17
## v0.17.1
- **Added: support for 13.60 firmware** — rebuilt against the latest ps5-payload-sdk.
<details>
<summary><b>v0.17</b></summary>
- **Fixed: the trainer/cheat menu, Settings page, and Support/donate popup could silently fail to appear on some PS5 firmwares** (reported on 4.03) — the "Cheat menu loaded" toast would show, but the menu itself never appeared, same for Settings and Support. Root cause, confirmed against that firmware's actual WebKit source: every full-screen overlay in the dashboard positioned itself with the CSS `inset` shorthand, which is gated behind WebKit's `CSSLogicalEnabled` runtime flag — off by default in this build. With `inset` silently dropped, the overlay never got a `top`/`right`/`bottom`/`left`, so it had no size or position to render at, regardless of `display`/`visibility`/`opacity`. (Two earlier attempts in this same release — switching `display:none` to `visibility`/`opacity`, then adding a `transition` — treated it as a compositing/paint timing issue and didn't address this.) Replaced `inset` with explicit `top`/`right`/`bottom`/`left` everywhere it was used for positioning (the three overlays, plus several decorative background/glow/shimmer effects that had the same silent failure but went unnoticed since they're cosmetic). Confirmed fixed on the reporting user's PS5.
- **Fixed: the Settings page's ON/OFF toggle switches were too cramped for the text**, and the green "ON" fill sat slightly off-center — a CSS math bug on every toggle switch in the app, not just Settings.
@@ -16,6 +21,8 @@
- **Added an "Enable hotkey" switch in Settings → Hotkey, off by default.** On some firmwares (seen on 4.xx) the hook can still crash SceShellUI despite the fix above — the rest of CheatRunner works fully with it off; turn it on once you've confirmed it's stable on your firmware.
- **Fixed a garbled "…" character** ("Search settings…", the health panel's "Loading…") showing as mojibake on the PS5 browser — replaced with plain periods.
</details>
<details>
<summary><b>v0.16</b></summary>
-194
View File
@@ -1,194 +0,0 @@
cmake_minimum_required(VERSION 3.10)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
project(CheatRunnerPayload C CXX)
set(CHEATRUNNER_VERSION "0.17")
# Embed CheatRunner.png as a C byte array (cheatrunner_png.h in build dir).
# If the asset is missing, generate a tiny fallback icon so builds still work.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CheatRunner.png")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/CheatRunner.png" _CR_PNG_HEX HEX)
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," _CR_PNG_BYTES "${_CR_PNG_HEX}")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cheatrunner_png.h"
"static const unsigned char g_cheatrunner_png[] = {${_CR_PNG_BYTES}};\n"
)
else()
message(WARNING "CheatRunner.png missing; generating fallback cheatrunner_png.h")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cheatrunner_png.h"
"static const unsigned char g_cheatrunner_png[] = {"
"0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,"
"0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x01,"
"0x00,0x00,0x00,0x01,0x08,0x06,0x00,0x00,0x00,0x1F,0x15,0xC4,"
"0x89,0x00,0x00,0x00,0x0D,0x49,0x44,0x41,0x54,0x78,0x9C,0x63,"
"0x00,0x01,0x00,0x00,0x05,0x00,0x01,0x0D,0x0A,0x2D,0xB4,0x00,"
"0x00,0x00,0x00,0x49,0x45,0x4E,0x44,0xAE,0x42,0x60,0x82};\n"
)
endif()
# Embed dist/CheatRunner.pkg as a C byte array (cheatrunner_tile_pkg.h).
# Build the PKG first with: cd pkg-source && ./build-pkg.sh
# If absent the auto-install feature is compiled out but everything else works.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/dist/CheatRunner.pkg")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/dist/CheatRunner.pkg" _PKG_HEX HEX)
string(LENGTH "${_PKG_HEX}" _PKG_HEX_LEN)
math(EXPR _PKG_LEN "${_PKG_HEX_LEN} / 2")
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," _PKG_BYTES "${_PKG_HEX}")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cheatrunner_tile_pkg.h"
"static const unsigned char g_cheatrunner_tile_pkg[] = {${_PKG_BYTES}};\n"
"static const size_t g_cheatrunner_tile_pkg_len = ${_PKG_LEN}u;\n"
)
set(_HAVE_TILE_PKG ON)
message(STATUS "CheatRunner tile PKG: embedding ${_PKG_LEN} bytes from dist/CheatRunner.pkg")
else()
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cheatrunner_tile_pkg.h"
"/* dist/CheatRunner.pkg not present at configure time — auto-install disabled */\n"
)
set(_HAVE_TILE_PKG OFF)
message(STATUS "CheatRunner tile PKG: dist/CheatRunner.pkg not found — auto-install disabled (run pkg-source/build-pkg.sh first)")
endif()
# Gzip-compress the dashboard assets at build time on the host (real zlib,
# not available in the target SDK) and embed the finished bytes — the PS5
# binary just serves them, it never compresses anything itself.
# find_package(Python3) instead of a hardcoded "python3" command: on Windows
# python3.exe is often a Microsoft Store app-execution-alias stub, not a real
# interpreter, while the actual install is reachable as "python"/"py".
find_package(Python3 COMPONENTS Interpreter REQUIRED)
foreach(_ASSET html css js)
execute_process(
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_gzip_header.py"
"${CMAKE_CURRENT_SOURCE_DIR}/src/dashboard_${_ASSET}.inc"
"${CMAKE_CURRENT_BINARY_DIR}/dashboard_${_ASSET}_gz.h"
"g_dashboard_${_ASSET}_gz"
RESULT_VARIABLE _GZ_RC
)
if(NOT _GZ_RC EQUAL 0)
message(FATAL_ERROR "Failed to gzip-compress dashboard_${_ASSET}.inc for embedding")
endif()
endforeach()
file(GLOB CHEATRUNNER_SRC CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
)
# --- Hotkey injected payload ------------------------------------------------
# A separate PIE executable, built as its own target then embedded into
# CheatRunner.elf as a byte array and written into SceShellUI via elfldr_exec.
add_executable(cr_getdata_payload
src/payload/cr_getdata_payload.c
src/payload/cr_detour.c
src/payload/hde64.c
)
set_target_properties(cr_getdata_payload PROPERTIES
OUTPUT_NAME cr_getdata_payload.elf
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/payload"
)
target_include_directories(cr_getdata_payload PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/src/payload"
)
target_compile_options(cr_getdata_payload PRIVATE -fPIE -Wall -Wextra -O2)
target_link_options(cr_getdata_payload PRIVATE "-Wl,--allow-multiple-definition")
target_link_libraries(cr_getdata_payload PRIVATE kernel_sys)
if(CMAKE_STRIP)
add_custom_command(TARGET cr_getdata_payload POST_BUILD
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:cr_getdata_payload>
)
endif()
set(_GETDATA_BLOB_HEADER "${CMAKE_CURRENT_BINARY_DIR}/cr_getdata_payload_blob.h")
add_custom_command(
OUTPUT "${_GETDATA_BLOB_HEADER}"
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_blob_header.py"
"$<TARGET_FILE:cr_getdata_payload>"
"${_GETDATA_BLOB_HEADER}"
"g_cr_getdata_payload_elf"
DEPENDS cr_getdata_payload
COMMENT "Embedding cr_getdata_payload.elf as a byte array"
)
add_custom_target(cr_getdata_payload_blob DEPENDS "${_GETDATA_BLOB_HEADER}")
add_executable(CheatRunner
${CHEATRUNNER_SRC}
src/third_party/cJSON.c
src/third_party/stb_impl.c
src/third_party/mc4/aes.c
src/third_party/mc4/base64.c
src/third_party/mc4/mc4decrypter.c
)
set_target_properties(CheatRunner PROPERTIES
OUTPUT_NAME CheatRunner.elf
)
add_dependencies(CheatRunner cr_getdata_payload_blob)
target_include_directories(CheatRunner PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
target_include_directories(CheatRunner PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_compile_options(CheatRunner PRIVATE -Wall -Wextra -O2)
target_compile_definitions(CheatRunner PRIVATE CHEATRUNNER_VERSION="${CHEATRUNNER_VERSION}")
target_link_options(CheatRunner PRIVATE "-Wl,--allow-multiple-definition")
target_compile_definitions(CheatRunner PRIVATE CHEATRUNNER_HAVE_BROWSER_OPEN=1)
set(CHEATRUNNER_LIBS
kernel_sys
SceSystemService
SceUserService
SceAppInstUtil
SceHttp
SceSsl
SceNet
ScePad
pthread
)
# ps5-payload-sdk exposes sceLncUtil* through libSceSystemService.
# Do not link -lSceLncUtil: many SDK bundles do not ship a separate library,
# but libSceSystemService contains the needed stubs.
target_compile_definitions(CheatRunner PRIVATE
CHEATRUNNER_HAVE_SCE_LNCUTIL=1
CHEATRUNNER_HAVE_SCE_USER_LIST=1
)
if(_HAVE_TILE_PKG)
target_compile_definitions(CheatRunner PRIVATE CHEATRUNNER_HAVE_TILE_PKG=1)
endif()
message(STATUS "Using sceLncUtil* symbols from SceSystemService")
message(STATUS "sceUserServiceGetLoginUserIdList: enabled")
# SQLite-backed app.db scanner (primary game list, falls back to folder scan).
#
# The ps5-payload-sdk does not ship libsqlite3. To enable:
# 1. Download the amalgamation from https://sqlite.org/download.html
# (sqlite-amalgamation-*.zip → sqlite3.c + sqlite3.h)
# 2. Place both files in src/third_party/
# 3. Re-run cmake with -DCHEATRUNNER_SQLITE_BUNDLED=ON
# (or flip the option(... OFF) below to ON before configuring)
#
option(CHEATRUNNER_SQLITE_BUNDLED "Bundle sqlite3 amalgamation (src/third_party/sqlite3.c)" OFF)
if(CHEATRUNNER_SQLITE_BUNDLED)
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/third_party/sqlite3.c")
message(FATAL_ERROR "CHEATRUNNER_SQLITE_BUNDLED=ON but src/third_party/sqlite3.c not found. "
"Download the amalgamation from https://sqlite.org/download.html")
endif()
target_sources(CheatRunner PRIVATE src/third_party/sqlite3.c)
target_compile_definitions(CheatRunner PRIVATE
CHEATRUNNER_HAVE_SQLITE_APPDB=1
SQLITE_THREADSAFE=0
SQLITE_DEFAULT_MEMSTATUS=0
SQLITE_OMIT_LOAD_EXTENSION
)
message(STATUS "app.db scanner: SQLite bundled from src/third_party/sqlite3.c")
else()
message(STATUS "app.db scanner: disabled (folder fallback only) — see CMakeLists.txt to enable")
endif()
target_link_libraries(CheatRunner PRIVATE ${CHEATRUNNER_LIBS})
if(CMAKE_STRIP)
add_custom_command(TARGET CheatRunner POST_BUILD
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:CheatRunner>
)
endif()
+129
View File
@@ -0,0 +1,129 @@
# Standalone Makefile for CheatRunner.elf — compiles directly with the PS5
# payload SDK toolchain.
#
# Usage:
# make # build build-make/CheatRunner.elf
# make clean
# make SQLITE=1 # force-enable bundled sqlite3 (auto-detected otherwise)
# make SQLITE=0 # force-disable it
# PS5_PAYLOAD_SDK=/path/to/sdk make
VERSION := 0.17.1
BUILD_DIR := build-make
GENDIR := $(BUILD_DIR)/gen
OBJDIR := $(BUILD_DIR)/obj
ELF := $(BUILD_DIR)/CheatRunner.elf
# ---------------------------------------------------------------- SDK resolve
PS5_PAYLOAD_SDK ?=
SDK_ROOT := $(patsubst %/toolchain/prospero.cmake,%,$(firstword \
$(wildcard $(PS5_PAYLOAD_SDK)/toolchain/prospero.cmake) \
$(wildcard ../../ps5-payload-sdk/toolchain/prospero.cmake) \
$(wildcard ps5-payload-sdk/toolchain/prospero.cmake) \
$(wildcard /opt/ps5-payload-sdk/toolchain/prospero.cmake)))
ifeq ($(SDK_ROOT),)
$(error PS5 payload SDK not found. Set PS5_PAYLOAD_SDK, or place it at ../../ps5-payload-sdk, ./ps5-payload-sdk, or /opt/ps5-payload-sdk)
endif
CC := $(SDK_ROOT)/bin/prospero-clang
CXX := $(SDK_ROOT)/bin/prospero-clang++
STRIP := $(SDK_ROOT)/bin/prospero-strip
# ---------------------------------------------------------------- sources
SRC_C := $(wildcard src/*.c) \
src/third_party/cJSON.c \
src/third_party/stb_impl.c \
src/third_party/mc4/aes.c \
src/third_party/mc4/base64.c \
src/third_party/mc4/mc4decrypter.c
SRC_CPP := $(wildcard src/*.cpp)
SQLITE ?= $(if $(and $(wildcard src/third_party/sqlite3.c),$(wildcard src/third_party/sqlite3.h)),1,0)
ifeq ($(SQLITE),1)
SRC_C += src/third_party/sqlite3.c
endif
OBJ_C := $(patsubst src/%.c,$(OBJDIR)/%.o,$(SRC_C))
OBJ_CPP := $(patsubst src/%.cpp,$(OBJDIR)/%.o,$(SRC_CPP))
OBJS := $(OBJ_C) $(OBJ_CPP)
# ---------------------------------------------------------------- generated headers
PNG_SRC := $(wildcard CheatRunner.png)
PKG_SRC := $(wildcard dist/CheatRunner.pkg)
HAVE_TILE_PKG := $(if $(PKG_SRC),1,0)
PAYLOAD_ELF := $(BUILD_DIR)/payload/cr_getdata_payload.elf
PAYLOAD_SRC := src/payload/cr_getdata_payload.c src/payload/cr_detour.c src/payload/hde64.c
GEN_HEADERS := $(GENDIR)/cr_getdata_payload_blob.h $(GENDIR)/cheatrunner_png.h $(GENDIR)/cheatrunner_tile_pkg.h \
$(GENDIR)/dashboard_html_gz.h $(GENDIR)/dashboard_css_gz.h $(GENDIR)/dashboard_js_gz.h
# ---------------------------------------------------------------- flags
DEFINES := -DCHEATRUNNER_VERSION=\"$(VERSION)\" \
-DCHEATRUNNER_HAVE_BROWSER_OPEN=1 \
-DCHEATRUNNER_HAVE_SCE_LNCUTIL=1 \
-DCHEATRUNNER_HAVE_SCE_USER_LIST=1
ifeq ($(HAVE_TILE_PKG),1)
DEFINES += -DCHEATRUNNER_HAVE_TILE_PKG=1
endif
ifeq ($(SQLITE),1)
DEFINES += -DCHEATRUNNER_HAVE_SQLITE_APPDB=1 -DSQLITE_THREADSAFE=0 \
-DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_LOAD_EXTENSION
endif
INCLUDES := -I$(GENDIR) -Isrc
CFLAGS := -fPIE -Wall -Wextra -O2 $(INCLUDES) $(DEFINES)
CXXFLAGS := $(CFLAGS)
LDFLAGS := -Wl,--allow-multiple-definition
LIBS := -lkernel_sys -lSceSystemService -lSceUserService -lSceAppInstUtil \
-lSceHttp -lSceSsl -lSceNet -lScePad -lpthread
.PHONY: all clean
all: $(ELF)
$(ELF): $(OBJS) | $(BUILD_DIR)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
@if [ -x "$(STRIP)" ]; then $(STRIP) --strip-all $@; fi
$(OBJDIR)/%.o: src/%.c $(GEN_HEADERS)
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(OBJDIR)/%.o: src/%.cpp $(GEN_HEADERS)
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) -c $< -o $@
# Hotkey payload: built as its own PIE ELF, then embedded into CheatRunner.elf
$(PAYLOAD_ELF): $(PAYLOAD_SRC) | $(BUILD_DIR)
@mkdir -p $(dir $@)
$(CC) -fPIE -Wall -Wextra -O2 -Isrc -Isrc/payload $(LDFLAGS) -o $@ $(PAYLOAD_SRC) -lkernel_sys
@if [ -x "$(STRIP)" ]; then $(STRIP) --strip-all $@; fi
$(GENDIR)/cr_getdata_payload_blob.h: $(PAYLOAD_ELF) tools/gen_blob_header.py | $(GENDIR)
python3 tools/gen_blob_header.py $< $@ g_cr_getdata_payload_elf
$(GENDIR)/cheatrunner_png.h: $(PNG_SRC) | $(GENDIR)
@if [ -f CheatRunner.png ]; then \
python3 -c "d=open('CheatRunner.png','rb').read(); open('$@','w').write('static const unsigned char g_cheatrunner_png[] = {'+','.join(str(b) for b in d)+'};\n')"; \
else \
printf '%s\n' 'static const unsigned char g_cheatrunner_png[] = {0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x06,0x00,0x00,0x00,0x1F,0x15,0xC4,0x89,0x00,0x00,0x00,0x0D,0x49,0x44,0x41,0x54,0x78,0x9C,0x63,0x00,0x01,0x00,0x00,0x05,0x00,0x01,0x0D,0x0A,0x2D,0xB4,0x00,0x00,0x00,0x00,0x49,0x45,0x4E,0x44,0xAE,0x42,0x60,0x82};' > $@; \
fi
$(GENDIR)/cheatrunner_tile_pkg.h: $(PKG_SRC) | $(GENDIR)
@if [ -f dist/CheatRunner.pkg ]; then \
python3 -c "d=open('dist/CheatRunner.pkg','rb').read(); open('$@','w').write('static const unsigned char g_cheatrunner_tile_pkg[] = {'+','.join(str(b) for b in d)+'};\nstatic const unsigned long g_cheatrunner_tile_pkg_len = %du;\n'%len(d))"; \
else \
printf '/* dist/CheatRunner.pkg not present -- auto-install disabled */\n' > $@; \
fi
$(GENDIR)/dashboard_%_gz.h: src/dashboard_%.inc tools/gen_gzip_header.py | $(GENDIR)
python3 tools/gen_gzip_header.py $< $@ g_dashboard_$*_gz
$(GENDIR) $(BUILD_DIR):
@mkdir -p $@
clean:
rm -rf $(BUILD_DIR)
+1 -1
View File
@@ -281,7 +281,7 @@ Recommended project layout:
CheatRunner/
├── CheatRunnerPayload/
│ ├── src/
│ ├── CMakeLists.txt
│ ├── Makefile
│ └── build-cheatrunner.ps1
└── PS5-Payload-dev/
└── sdk-master/
+1 -1
View File
@@ -64,7 +64,7 @@
#endif
#ifndef CHEATRUNNER_VERSION
#define CHEATRUNNER_VERSION "0.17"
#define CHEATRUNNER_VERSION "0.17.1"
#endif
#define MAX_GAMES CR_APPDB_MAX_GAMES
+1 -1
View File
@@ -53,7 +53,7 @@ cr_api_dashboard_handle(int fd, const char *method, const char *path,
if (!strcmp(path, "/cache.appcache")) {
static const char appcache[] =
"CACHE MANIFEST\n"
"# CheatRunner v0.17\n"
"# CheatRunner v0.17.1\n"
"# Build: " __DATE__ " " __TIME__ "\n"
"\n"
"CACHE:\n"
+1 -1
View File
@@ -30,7 +30,7 @@
#include "cr_tile_pkg.h"
#ifndef CHEATRUNNER_VERSION
#define CHEATRUNNER_VERSION "0.17"
#define CHEATRUNNER_VERSION "0.17.1"
#endif
/* Async-signal-safe crash handler: writes a one-line entry to the crash log
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
# Build-time helper: embeds a binary file as a static C byte array.
#
# Usage: gen_blob_header.py <blob_path> <header_out_path> <array_name>
import sys
def main():
blob_path, header_path, array_name = sys.argv[1:4]
with open(blob_path, "rb") as f:
data = f.read()
with open(header_path, "w") as f:
f.write("static const unsigned char %s[] = {\n" % array_name)
for i in range(0, len(data), 20):
f.write(",".join(str(b) for b in data[i:i + 20]))
f.write(",\n")
f.write("};\n")
if __name__ == "__main__":
main()
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
# Build-time helper: extracts a dashboard .inc asset's exact byte content by
# compiling and running it through the host C compiler (so C string-escape
# parsing is never reimplemented by hand), gzip-compresses it, and writes a
# header with the compressed bytes as a static C array.
#
# Usage: gen_gzip_header.py <inc_path> <header_out_path> <array_name>
import gzip
import os
import subprocess
import sys
import tempfile
def extract_raw_bytes(inc_path):
inc_dir = os.path.dirname(os.path.abspath(inc_path))
inc_name = os.path.basename(inc_path)
with tempfile.TemporaryDirectory() as td:
src = os.path.join(td, "dump.c")
exe = os.path.join(td, "dump")
with open(src, "w") as f:
f.write('#include <stdio.h>\n')
f.write('static const char DATA[] =\n#include "%s"\n;\n' % inc_name)
f.write('int main(void) { fwrite(DATA, 1, sizeof(DATA) - 1, stdout); return 0; }\n')
subprocess.run(["cc", "-I", inc_dir, src, "-o", exe], check=True)
return subprocess.run([exe], check=True, stdout=subprocess.PIPE).stdout
def main():
inc_path, header_path, array_name = sys.argv[1:4]
raw = extract_raw_bytes(inc_path)
gz = gzip.compress(raw, compresslevel=9, mtime=0)
with open(header_path, "w") as f:
f.write("static const unsigned char %s[] = {\n" % array_name)
for i in range(0, len(gz), 20):
f.write(",".join(str(b) for b in gz[i:i + 20]))
f.write(",\n")
f.write("};\n")
f.write("static const unsigned long %s_len = %dul;\n" % (array_name, len(gz)))
f.write("static const unsigned long %s_raw_len = %dul;\n" % (array_name, len(raw)))
if __name__ == "__main__":
main()