This book is a work in progress, comments are welcome to: johno(at)johno(dot)se

Back to index...



During the later stages of development on the Josephine project, load performance on Juice became a problem (as Josephine used Juice as a persistence format for savegames, among other things). Experiments led to the creation of Ice, intended to be a better run-time replacement for Juice. The original version of Ice, shipped as part of Josephine, was both read and write capable. Later Ice evolved into a purely read/only file format.

Ice was a binary "optimized" version of Juice, which was intended for use in more performance critical applications. Ice can be seen as a "frozen" or "exported" version of Juice. Most concepts in Juice were retained, but Ice was however a read-only format. Linked lists were replaced with static arrayes, this being possible as Ice was read-only and immutable. Ice also supported localization of fields in UTF-8 format. Ice files (.ice) were typically created as a by product of saving .juice files in the JuiceMaker.

Hashed names

Ice used a string hashing scheme for both type and instance names. This means that all type and instance names are stored as int values both on disk and at runtime. For this reason, the IsType(const char* aType) and IsNamed(const char* aType) interfaces were provided, which both hash the passed string and compare this value to the stored type / instance name. Additionally, the conversion process provided a .hash file which mapped type and instance names to their hashed values. This was useful for looking up a human readable name for a given hash value while debugging, as well as determining if there were any hash collisions for names within the namespace. In such cases, it was a good idea to adjust the names so that no collisions occured.


Ice supported localized fields by converting the values of all LOCTEXT members to a zero based integer index (during the JuiceToIce conversion process), and creating a localization file (.loc) in UTF-8 format to store the localized strings. At runtime, Ice looked up the localized string using this internal index. .loc files were only created if the Juice namespace being converted to Ice actually contains any LOCTEXT members.

The workflow concept was thus that the development team produced content in Juice format with no regard for localization other than declaring localized text fields as LOCTEXT instead of TEXT. The game application itself was designed to use Ice as the data supplier, and when the data was to be localized, all .loc files were simply sent to the localization team. .loc files wrer in UTF-8 format, and each line consisted of a descriptive field followed by a tab delimiter and finally the actual text to be localized.

Usage (C++)

The Ice library offered a C++ interface to read Ice data. Note that Ice was read-only as opposed to Juice which was read/write.

//BEGIN example2.cpp

#include "ice.h"

bool LoadIce()
    Ice* example;
    Ice* tank;
    Ice* pos;

    //load the file
    example = Ice::Create("");
        return false;

    //access a top level instance
    tank = example->GetMember("Tank");
        return false;

    //access a member instance
    pos = tank->GetMember("myPosition");

    //access the values of an instance's members
    ::fprintf(stderr, "%f %f %f",
        pos->GetMemberValue("myX"), pos->GetMemberValue("myY"), pos->GetMemberValue("myZ"));

    return true;

Back to index...