Skip to content
Archives and protection
On this page

Archives and protection

How the SDK packs a level into one file or puts it behind a password, and how it recognizes what it was handed

Export modes

A level leaves the device in one of the LevelExportMode shapes:

ValueModeWhat is writtenProtection
0Foldera plain folder, the same shape as on disknone
1FolderProtectedLevela folder where only level.json.gpg is encryptedOpenPGP
2TarGz<name>.tar.gznone
3TarGzProtected<name>.tar.gz.gpgOpenPGP
4Zip<name>.zipnone
5ZipProtected<name>.zip.gpgOpenPGP
6ZipEncrypted<name>.zip with AES-256 entriesthe zip's own

The number is the identity of a mode and never changes. The order in the editor's dropdown is set separately (DisplayOrder)

Every format is an open standard. tar -xzf, gpg -d and any archiver open them without the game:

gpg -d level.tar.gz.gpg > level.tar.gz
tar -xzf level.tar.gz

Two protection schemes

ZipEncrypted is the default way to put a level behind a password. The person receiving it has an archiver and most likely does not have gpg

OpenPGP covers a document, a folder and an archive with one scheme. Only it can protect a folder

In FolderProtectedLevel the metadata, the cover and the media stay readable. So a level browser still shows the card

The inner extension stays in the name: level.json.gpg. That is exactly how gpg -c level.json names the file, so the format is known without a guess

What goes inside

LevelArchiveBuilder computes the contents from the model, not from a folder listing. A file the level does not reference stays out. The report says how many were left behind

The level's references are handled like this:

  • a resource with AbsolutePath is copied into the archive. Its reference is rewritten to LevelPath, but in the exported copy only
  • a DirectUrl stays as it is and is reported: a URL cannot travel inside a file
  • a missing file is reported with the code archive.resource_missing

Recognition by bytes

Anybody can write a file name. So ArchiveFormatSniffer decides by the first 8 bytes:

First bytesFormat
1F 8BTarGz
50 4B 03 04, 50 4B 05 06, 50 4B 07 08, 50 4B 30 30Zip
37 7A BC AF 27 1CSevenZip
an OpenPGP packet tag, checked lastOpenPgp: decrypted, then sniffed again
Warning

.7z is not read and not written. It is recognized only so the refusal can name it (Unsupported). Then the person re-packs the level as zip instead of deciding the file is broken

Reading

LevelArchiveReader.ReadAsync takes a folder or a seekable Stream. It returns a LevelArchiveContent whose LevelArchiveOpenResult is one of:

ResultMeaning
Okread
PassphraseRequiredthere is no passphrase, ask for it
WrongPassphrasethe person already entered a passphrase, and it is wrong
Damagedthe file is damaged
NotAnArchivethis is not an archive
Unsupportedthe format is recognized but not supported

PeekMetaAsync reads only the metadata, for a card or a catalogue

A server reads whatever somebody chose to upload. So the reader works under ArchiveLimits. The defaults are:

  • 4096 entries
  • 512 MiB per entry
  • 1 GiB in total

Nothing is unpacked outside the store it was given

The editor side of export - Export and protection