From path cache to game index: search less, run faster
A game with thousands of resources should not rediscover where they are over and over. Kirin takes an idea born from compatibility and turns it into an access layer designed to know first and search second.

RPG Maker XP understands much of a game as a collection of paths. Graphics, sounds, maps and data live behind names such as Graphics/Characters/Hero.png or Audio/BGM/Town.ogg. This seems trivial in a small project. In a huge one, locating resources becomes an engine responsibility.
The issue is not the existence of a folder called Graphics. It appears when the runtime must repeatedly answer questions it could already know: whether a file exists, the exact spelling of its name, its extension, the contents of a directory, or which logical path corresponds to a physical resource.
The fastest optimization is not always a faster search. Often, it is avoiding the need to repeat that search.
What is a path cache?
A path cache remembers the relationship between the name used by the game and the resource's real location. Instead of constantly querying the file system, the engine can discover a path once and retain the answer.
This is especially useful for software inherited from Windows. Many RPG Maker XP projects were developed under the assumption that Hero.png, hero.png and HERO.PNG would resolve identically. That assumption is unsafe on Linux or Android.
mkxp-z already implements a pathCache for precisely this compatibility problem: it stores a lowercased, normalized full path and relates it to the path preserving the real capitalization.
In its original context, the cache primarily reproduces historical Windows behavior. Kirin retains that role but raises the system's ambition.
From compatibility feature to infrastructure
Once the engine has walked the project and knows its files, an obvious question follows: why use that knowledge only to repair letter case?
Kirin begins to treat the full path set as a game catalog. A path is no longer merely a string to interpret; it becomes a key into structures prepared specifically to answer queries.
The conceptual consequence matters: the engine stops behaving only like a folder browser and starts behaving like a query system.
An indexed table instead of a repeated search
A traditional project can be imagined as a tree the runtime must traverse:
An index changes the question. Instead of “walk this structure until you find Pikachu,” the operation becomes “give me the resource associated with this key.”
Kirin uses in-memory hash tables to make path resolution behave like an indexed lookup. On average, a hash lookup locates an entry without sequentially scanning every project resource. Having twenty thousand files should not turn each request into a search across twenty thousand possibilities.
Search
- Receive a path
- Query the file system
- Enumerate or test candidates
- Compare names
- Obtain the resource
Query
- Receive a path
- Normalize the key
- Query the index
- Obtain the entry
- Open the resource
This does not remove the cost of opening and reading the file when its contents are actually needed. It removes part of the work required to discover which file answers the request.
Build once, answer thousands of times
Every index has a construction cost. Something must traverse the project, normalize its paths and prepare the tables. The advantage appears when that relatively expensive operation replaces thousands of later operations.
Kirin's current implementation keeps a main path map, statistics for each resource, direct-resolution aliases and a representation of directory entries. Once organized, common queries such as existence, name resolution and directory listing can remain in memory.
- Fewer repeated directory enumerations
- Fewer filename and extension probes
- Fewer existence checks against storage
- Less work resolving capitalization differences
- Reusable answers across Ruby, graphics and audio systems
The philosophy is simple: if Kirin already knows that a resource exists, it need not ask the operating system again every time Ruby requests it.
The index can resolve incomplete names too
RGSS does not always request resources with an explicit extension. A script may ask for Graphics/Pictures/Menu when the physical file is Graphics/Pictures/Menu.png.
While building the index, Kirin can generate aliases connecting different logical forms to the same resource:
Both keys can point to the same resolved path. Discovering that relationship moves out of each individual access and into a preparation stage. The implementation can also account for normalized aliases involving certain accent differences—another source of incompatibility when long-lived Windows projects move to systems with different rules.
Persisting knowledge: the binary index
At this point, the catalog could still be rebuilt every time the game starts. That speeds up runtime queries but still rediscovers thousands of files at every launch. Kirin takes another step: it persists the catalog as a binary file.
The file uses a signature derived from the mounted path set to identify its context. On Android, the implementation also avoids relying directly on APK installation paths that may change across reinstalls. The index should represent the game and its resource space, not a temporary system location.
The current format is identified internally by the MKXPPC04 header and serializes normalized paths, real paths, basic file data and per-directory lists. It is written through a temporary file followed by a final replacement, preventing a partially written cache from being treated as valid.
The binary is not searched; it is loaded
Persisting the index would offer little if every request had to open and scan the binary. Kirin does not work that way. The binary preserves knowledge between executions. At startup, Kirin reads it and reconstructs the query structures in RAM. The game's hot path then uses those in-memory tables.
Storage preserves. Memory answers.
This distinction explains the system's efficiency. A folder scan is not replaced with a scan through a larger file. A persistent representation restores the engine quickly to a state in which the answers are already organized.
Directories can stop being questions too
Many optimizations focus on opening files, but a game may also ask what a directory contains. Without an index, listing it means another file-system query.
Kirin builds a complete in-memory representation of directory entries. Listing operations can therefore use the prepared catalog rather than enumerate storage again.
This matters especially on Android, where the game's logical tree may combine external storage, resources mounted through PhysFS and application-owned content. Every avoided call removes layers of work that add no new information when the engine already knows the answer.
The index does not eliminate compatibility
Optimizing a runtime for old games requires caution: some projects do unusual things. They may create files dynamically, depend on unexpected paths or use APIs beyond the common case.
An aggressive index therefore does not make the physical file system disappear. Kirin can use the indexed path normally and retain a physical fallback when the prepared structures cannot resolve a request.
Optimize the common case without closing the door on the exceptional one. That rule matters when compatibility spans projects built over more than twenty years.
The file system stops being the game's database
This is the conceptual change that interests us most. In RPG Maker XP, the folders are effectively the catalog: to know what exists, the runtime inspects the file tree. Kirin tries to separate those responsibilities.
Legacy model
- The folders hold the truth
- The runtime asks the file system
- Paths resolve when needed
- Work is repeated
Indexed model
- The catalog knows the logical truth
- Keys resolve resources
- Answers live in RAM
- Storage delivers the data
This opens the door to a larger evolution. Once the game accesses resources through a logical catalog, their physical representation can change without Ruby knowing. A resource may be a loose PNG today and an entry in an indexed graphics package tomorrow. To RGSS, its name can remain exactly the same.
The fastest search is the one already resolved
Performance discussions often evoke faster instructions, JIT compilers or a more capable GPU. Yet many bottlenecks arise simply from doing unnecessary work too many times.
Do not walk a directory when its contents are known. Do not try extensions when the existing one is known. Do not query storage when the answer is in RAM. Do not rebuild a whole catalog when it can be restored from a prepared binary.
That is the evolution Kirin seeks for the path cache. An idea used by mkxp-z primarily to reproduce Windows tolerance begins to become general-purpose resource-access infrastructure.
The project still sees familiar paths. Ruby still asks for Graphics/Characters/Pikachu. Compatibility remains above. Underneath, however, the engine can stop searching like a 2004 program and start querying like a system that already knows its own game.
Try Kirin on Android
Read the installation guide and add a compatible project to your library.
View installation guide