Skip to content
Documentation
On this page

Archives and protection

A level is exported as a folder, tar.gz or zip, optionally under a password: zip with AES-256 or OpenPGP. The SDK detects the format of a received file by its first bytes, not by its name

Export modes

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

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 entrieszip's own

The number is the identity of the mode, it 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 lock a level with a password. The recipient has an archiver, but most likely not gpg

OpenPGP locks 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 the level browser still shows the card

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

What goes inside

LevelArchiveBuilder works out the contents from the model, not from the list of files in the folder. A file the level does not reference stays outside. The report says how many such files were left

The level's references are handled like this:

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

Detection by bytes

Anyone 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 and detected again
Warning

.7z is neither read nor written. It is detected only so that the refusal names it (Unsupported). Then a person repacks the level as zip instead of deciding the file is broken

Reading

LevelArchiveReader.ReadAsync takes a folder or a seekable Stream. It returns LevelArchiveContent, where LevelArchiveOpenResult is one of these values:

ResultMeaning
Okread
PassphraseRequiredthere is no password, it has to be asked for
WrongPassphrasethe person has already entered a password, 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 catalog

A server reads whatever someone chose to upload. So the reader works within ArchiveLimits. By default these are:

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

Nothing is unpacked outside the storage it was given

Export from the editor's side - Export and protection