A high-performance, XOR-obfuscated asset loading system for AppGameKit (AGK). This tool allows you to package your game assets into a single obfuscated file and load them directly from memory, improving resistance to casual extraction and load times.
- XOR Obfuscation: Basic protection to deter casual extraction.
- Vectorized Loading: Optimized 4-byte integer reading and decryption for maximum performance in AGK Tier 1.
- Direct Memory Loading: Assets stream from memblocks straight into AGK IDs, minimizing disk I/O.
- 4-Byte Alignment: Assets are automatically padded to ensure high-speed integer-aligned CPU access.
Use the provided Python script to bundle your media folder into a single .pak file.
Syntax:
python build_assets.py <source_folder> <output_file>Example:
python build_assets.py media media/assets.pak- This scans the
mediafolder. - Aligns all files to 4-byte boundaries (adding padding if necessary).
- XOR-obfuscates the data using the key defined in the script.
- Generates
media/assets.pak.
Note: Ensure the
XOR_KEYinbuild_assets.pymatches the one insrc/pack_loader.agc.
Include the loader in your main project file.
#include "src/pack_loader.agc"
// Optional: Use a constant to toggle between Packed and Loose modes
#constant USE_PACK 1Initialize the loader at the start of your game. This reads the manifest headers into memory.
if USE_PACK
Pack_Init("assets.pak")
endifLoads an image from the pack into an AGK Image ID (PNG/JPG).
global playerImg as integer
if USE_PACK
// Load directly from memory (fastest)
playerImg = Pack_LoadImage("img/player.png")
else
// Fallback for development (loose files)
playerImg = LoadImage("img/player.png")
endifLoads a sound from the pack into an AGK Sound ID (WAV/OGG).
global clickSnd as integer
if USE_PACK
clickSnd = Pack_LoadSound("sfx/click.ogg")
else
clickSnd = LoadSoundOGG("sfx/click.ogg")
endif
// Usage
PlaySound(clickSnd)Loads streaming music from the pack into an AGK Music/Sound ID (OGG preferred).
global musicID as integer
if USE_PACK
musicID = Pack_LoadMusic("music/theme.ogg")
else
musicID = LoadMusicOGG("music/theme.ogg")
endif
PlayMusic(musicID, 1) // loopReads a text file from the pack and returns it as a string (config, shaders, levels).
local jsonContent as string
if USE_PACK
jsonContent = Pack_LoadText("data/config.json")
else
// Use helper for loose files (reads entire file to string)
jsonContent = Load_JSON("data/config.json")
endif
// Parse the JSON
myConfig.fromJSON(jsonContent)Gets a memblock with the exact bytes from the pack; caller deletes it.
local bytesID as integer
bytesID = Pack_LoadBytes("data/blob.bin")
if bytesID > 0
// Read or pass the memblock to your parser
// ...
DeleteMemblock(bytesID)
endifClose the pack file when the application terminates to free file handles and memory.
Pack_Close()- Ensure Python 3 is installed and available as
pythonorpython3in your PATH (only needed to build assets). - Build the pack:
python build_assets.py media media/assets.pak
- In your AGK project:
#include "src/pack_loader.agc"- Call
Pack_Init("assets.pak")at startup. - Use the loader functions as needed:
Pack_LoadImage()Pack_LoadSound()Pack_LoadMusic()Pack_LoadText()Pack_LoadBytes()
- Call
Pack_Close()on shutdown.
| Function | Return | Description |
|---|---|---|
Pack_Init(filePath) |
void |
Opens the pack file and parses the file table. |
Pack_LoadImage(virtualPath) |
integer |
Loads an image from the pack. Returns Image ID. |
Pack_LoadSound(virtualPath) |
integer |
Loads a sound (WAV/OGG) from the pack. Returns Sound ID. |
Pack_LoadMusic(virtualPath) |
integer |
Loads music/streaming OGG (or sound fallback) from the pack. Returns Music/Sound ID. |
Pack_LoadText(virtualPath) |
string |
Loads a text file from the pack. Returns the content as a string. |
Pack_LoadBytes(virtualPath) |
integer |
Loads raw bytes from the pack. Returns a memblock ID (caller deletes). |
Pack_Close() |
void |
Closes the pack file and releases the key memblock. |