Lightweight C++20 asset protection library for making game assets, application resources, and proprietary binary files harder to identify and casually rip.
RipStop wraps existing data in a project-specific binary envelope. It adds compression, deterministic scrambling, contextual asset binding, and corruption checks without requiring a custom file format, custom scrambler, config generator, or encryption stack.
RipStop provides:
- project-specific file identity
- Deflate compression
- deterministic built-in scrambling
- CRC corruption/context checks
- byte, typed, file, and
std::istream-bridge APIs - no callbacks, logging, retries, or process termination
RipStop provides asset obfuscation plus corruption and decode-context checks. It does not provide encryption, cryptographic confidentiality, or protection against intentional forgery. Do not use it for credentials, personal data, or cryptographic secrets.
Common files such as JSON, textures, meshes, shaders, audio, and custom binary blobs can expose recognizable headers or payload patterns. RipStop encodes those bytes into a project-specific wrapper so basic file inspection, signature scanning, and automated extraction tools have less obvious data to work with.
Typical uses include:
- protecting game assets such as maps, models, textures, shaders, audio, and dialogue
- hiding bundled resources, proprietary data files, presets, scripts, and internal caches
- replacing recognizable file signatures with project-specific identity
- binding encoded data to an expected project, asset class, or logical asset ID
- adding resource protection to any game engine, desktop application, tool, or custom loader
This is practical C++ asset obfuscation and file hardening against casual ripping—not DRM or cryptographic security. A determined reverse engineer who can inspect the running application can recover the data.
RipStop sits between plain archive packaging and cryptographic encryption:
- easier and more robust than maintaining a one-off XOR-and-compression wrapper
- lighter to integrate than an encryption stack with key management
- harder to identify and casually extract than raw files or standard archive formats
Use RipStop when you want an engine-agnostic C++ asset codec that raises the cost of automated extraction without pretending client-side assets can be made impossible to recover.
- Plug and play: provide one stable project seed, then call
encode()anddecode() - Format agnostic: wrap any in-memory bytes without redesigning their original format
- Small API: byte, typed, file, and
std::istream-bridge workflows - Deterministic by default: stable output supports reproducible builds and efficient patches
- Self-contained package: installed consumers do not need to manage miniz, doctest, or CPM
- Predictable failures: structured error codes; no hidden logging, callbacks, retries, or exits
add_subdirectory(path/to/ripstop-codec)
target_link_libraries(my_app PRIVATE RipStopCodec::ripstop-codec)Installed packages work too:
find_package(RipStopCodec 1.1 CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE RipStopCodec::ripstop-codec)Native Visual Studio/MSBuild projects can instead import:
<Import Project="path\to\ripstop-codec\cmake\RipStop.MSBuild.targets" />This builds and links RipStop through its own CMake target, keeping CPM and miniz private to RipStop. See INSTALL.md for requirements and generated output paths.
#include <ripstop/Codec.h>
constexpr auto project =
ripstop::codec::make_project_options("your-company:your-product:change-this");Keep this seed stable after shipping. Changing it makes existing encoded assets unreadable. No generated file, copied template, or setup tool is required.
std::vector<float> input{1.0f, 2.0f, 3.0f};
auto encoded = ripstop::codec::encode(std::span{input}, project);
if (!encoded) {
std::cerr << ripstop::codec::to_string(encoded.error) << '\n';
return;
}
auto decoded = ripstop::codec::decode_to_vector<float>(*encoded, project);
if (!decoded) {
std::cerr << ripstop::codec::to_string(decoded.error) << '\n';
return;
}Compression and built-in scrambling are enabled by default. Most applications need no custom
scrambler, hook, policy class, config generator, or generated code. Full runnable example:
examples/basic.cpp.
Use AssetOptions when a project has multiple asset classes or logical asset IDs:
const ripstop::codec::AssetOptions asset{
.format_tag = ripstop::codec::utils::hash_string("mesh"),
.context_seed = ripstop::codec::utils::hash_string("maps/forest_region"),
};
auto encoded = ripstop::codec::encode(bytes, project, asset);
auto decoded = ripstop::codec::decode(*encoded, project, asset);format_tag, context_seed, and password are caller-supplied decode context; they are not stored
in the header. Wrong values fail decompression or CRC validation. The password is only another
non-cryptographic scramble input. Leave nonce = 0 for reproducible builds; use a varying nonce
only when non-deterministic output is useful for anti-diffing.
All failures return ErrorCode. RipStop never logs, aborts, retries, or invokes hidden callbacks.
if (result.error != ripstop::codec::ErrorCode::Success) {
log(ripstop::codec::to_string(result.error));
}to_string() returns stable readable names. Cast ErrorCode to its underlying integer when numeric
telemetry is preferred.
Most users should keep the built-in scrambler. A custom scrambler must:
- use a stable, project-owned, nonzero
scramble_id - be deterministic
- be self-inverse because the same function runs during encode and decode
- remain available for every asset written with that ID
See examples/custom_scrambler.cpp.
Legacy v1.0 custom scramblers using ID 0 remain supported. New custom integrations should use a
nonzero ID so assets identify their algorithm unambiguously.
- Format v1 uses native little-endian, native object representation for typed overloads.
- Typed overloads accept trivially copyable types. Their padding and layout remain compiler/ABI dependent; serialize explicitly for portable assets.
- Default maximum encoded or decoded payload is 256 MiB.
SecureWipeaccepts strings and trivially copyable live buffers. It is best-effort memory hygiene, not a guarantee against compiler/runtime copies.MemStreamdoes not own its source buffer. Keep that buffer alive for the stream lifetime.