Skip to content

TUTORIAL Resource Packs

Javidx9 edited this page Sep 8, 2020 · 2 revisions

What are they?

Resource packs allow you to package files into a single file, to make distributing your olc::PixelGameEngine applications easier. They also provide lightweight protection for your assets, allowing you to remain compliant with many asset licences - though read the licence first to be sure.

The olc::PixelGameEngine header file contains all you need to create and work with olc::ResourcePack objects. They are structured in a way that makes it easy to either use resource packs or your regular file structures while you are developing.

This article assumes you are familiar with basic olc::PixelGameEngine objects such as olc::Sprite. However ResourcePacks can be used to store any file.

Using a Resource Pack

It may seem odd to start with using resource packs before talking about creating them, but there is a good reason. It demonstrates just how simple using a resource pack is in your existing code.

olc::ResourcePack* pack = nullptr;

// Load a sprite normally
olc::Sprite* sprite = new olc::Sprite("../some/path/to/a/file.png", pack);

The above code snippet is one of several ways to load a sprite. Note the 'pack' variable is nullptr, in this instance the sprite will load as normal from the file path specified. By default the constructor for olc::Sprite defaults the 'pack' argument to nullptr for you, that's why you don't see it. If you intend on using Resource Packs at the start of a project, I recommend specifying the 'pack' variable, even if its nullptr - this way, you can easily use Resource Packs later on.

Assuming we have created a pack file, instead of nullptr we can load the file like this:

olc::ResourcePack *pack = new olc::ResourcePack() ;
pack->LoadPack("./data/packfile.dat", "SOME KEY VALUE");

Here we create a ResourcePack object, and call the LoadPack() function. The first argument is the path to the file itself, normally this will be situated right next to you executable file, but hey, its up to you! The second argument is a decryption key that the pack was created with. If the keys don't match, then the pack will fail to load.

Now, this is important. The resource pack will contain many files, and the information to extract those files is stored in the resource pack's header. The header is scrambled with the key provided, making it difficult to identify where the files begin and end within the pack file. However, those determined enough will be able to extract your files, as the file's contents are not scrambled or encrypted in any way.

So now, the 'pack' variable is primed ready for file extraction. You don't really need to do anything else. Since 'pack' is initialised the olc::Sprite constructor will load the file from the resource pack file. But how does it know which file to use? Well, the resource pack uses the file paths you used on your development system as named identifiers for the files in the pack, so if you used "c:\some\folder\image.png" whilst developing your application, in the resource pack, the file is named "c:\some\folder\image.png". If the resource pack is used on someone else's system, it doesn't matter, it's loading from the pack, and not their file system.

olc::Sprite will handle loading from the resource pack for you, as will several other olc::PixelGameEngine related objects, though often you will want to store other information such as file data, level layouts, audio clips, whatever you want! To make things simple for you to load the file, you can access the memory buffer for a file. Note, resource packs are not entirely loaded into memory until the data is needed.

To access generic file data:

olc::ResourceBuffer rb = pack->GetFileBuffer("e:/your/file.txt");

The ResourceBuffer object contains a vector of bytes that represents the files contents, but can also be used as a std::istream to make it compatible with standard file reading operations. Typically you will need to create two types of loaders for your files, one for when you just read the files locally from your machine, and another if the data is specified in a resource pack. However, this is simplified because you can treat ResourceBuffer like a std::istream, so you can reuse code to actually parse the file data.

Creating a Resource Pack File

This article details the most basic way to create ResourcePack files, though be aware the community has developed tools to aide the process. I typically make my ResourcePack files in code like this:

olc::ResourcePack* pack = new olc::ResourcePack();
pack->AddFile("some_file1.png");
pack->AddFile("../something/else.txt");

pack->SavePack("myNewResourcePack.dat", "A REALLY CRYPTIC KEY STRING");

This way, you can comment out the AddFile() and SavePack() lines whilst you are developing, and use them when you are producing your final release just the once to produce the pack file.

Additional examples

I'll add some specific usage examples later.