Skip to main content

Crate petro_meg

Crate petro_meg 

Source
Expand description

§Petroglyph Meg file library

MEGA files are a format used by Petroglyph for various games including Star Wars: Empire at War, Universe at War: Earth Assault, Guardians of Graxia, Rise of Immortals, Grey Goo, and Great War: Western Front.

This library provides tools for extracting their contents or authoring them. It is based on the documentation an research by Mike Lankamp found on modtools.petrolution.net

There are 3 different MEGA file versions, and the version used depends on the game. Based on the Petrolution Mod Tools Game List, the following versions are used for each of these games:

  • Version 1:
    • Star Wars: Empire at War
    • Star Wars: Empire at War: Forces of Corruption
    • Universe at War: Earth Assault
  • Version 2:
    • Guardians of Graxia
  • Version 3:
    • Rise of Immortals
    • Grey Goo
    • Great War: Western Front

The different versions are represented by types in the version module. There are two ways to work with versions. If you know what version you need to work with at compile time, you can use the explicit structs MegV1, MegV2, and MegV3. If you need to dynamically support more than one MEGA file version, you can use the enum MegVersion.

Note that this project has undergone very minimal testing, especially on the writer side. It definitely reads at least some of the MEGA files from Empire at War and Great War: Western Front, including encrypted ones, but I have not yet tested whether those games will accept MEGA files encoded by it.

§Reading

For reading, the mega file versions implement the ReadMegMega trait. This provides methods to read the metadata from the MEGA file. The metadata in the file consists of a list of FileEntrys which contain the file names and the files’ positions within the original MEGA file.

use petro_meg::reader::ReadMegMeta;
use petro_meg::version::MegV1;
let data: &[u8] = // ...
let files = MegV1.read_meg_meta(data).unwrap();

read_meg_meta works on any type which implements read.

The FileEntry does not contain the actual file contents. To get the contents, you have two options depending on what your file source is. If you have the complete file contents in a slice, you can use range to get a range that will slice to the file’s content.

let data: &[u8] = // ...
let files = MegV1.read_meg_meta(data).unwrap();
for file in files {
    let content = &data[file.range()];
}

If instead your data is a type which implements Seek, you can use extract_from to seek to the start and get a reader which will limit to just the contents.

let options = MegReadOptions::new();
let mut mega_file = // ...
let files = MegV1.read_meg_meta(&mut mega_file).unwrap();
for file in files {
    let mut content_reader = file.extract_from(&mut mega_file, &options).unwrap();
    let mut content_bytes = Vec::with_capacity(file.size() as usize);
    content_reader.read_to_end(&mut content_bytes).unwrap();
}

To extract Version 3 MEGA files which are encrypted, you must provide a MegReadOptions with the appropriate Key. A key consists of 16 bytes, and depends on the game you are extracting from. I do not provide keys, however you can find keys for some petroglyph games on the bottom of the Petrolution Mod Tools MEGA File page.

§Format Guessing

There is no clear flag in the MEGA file format for which version it is, however it is possible to guess, potentially somewhat unreliably, based on the header format. To guess formats, we provide the special version GuessVersion. GuessVersion only works for reads. Additionally, Option<MegVersion> implements ReadMegMeta, using the provided version when the option is Some or GuessVersion when it is None.

§Writing

For writing files, the various version types (excluding GuessVersion) implement the BuildMeg trait, which provides a method to construct a MegBuilder for that version. The BuildMeg::builder is generic on the type of files to put in the builder.

use petro_meg::writer::BuildMeg;
use petro_meg::version::MegV1;
let builder = MegV1.builder::<File>();

You can put files into the builder with insert. This takes a MegPathBuf to define the path that the file is inserted under. Unlike PathBuf, MegPathBuf is validated. It enforces that the path is a relative path with no double slashes or relative elements like ... MegPathBuf and the corresponding MegPath also implement case-insensitive comparisons and treat / and \ the same, even on Unix-like systems. Note that when the MEGA file is built, all paths will be converted to ASCII uppercase and path separators will be normalized to \.

let mut builder = MegV1.builder();
let path = MegPath::from_str("some/file.txt").unwrap();
let data: &[u8] = // ...
builder.insert(path.to_owned(), data);

The files inserted into the MegBuilder must implement the FileContent trait in addition to Read. FileContent provides a file_len method which allows the builder to determine the size of the file while computing the MEGA file’s metadata. This allows it to write the output file sequentially, rather than needing to pad the file for the MEGA file header, write the contents, then seek back to the beginning to fill in the header. FileContent is currently implemented for File, &[u8], Cursor and a few other types, such as Box<T> where T: FileCursor.

Once you have inserted all the files you want into the MegBuilder, you can write it to an output using MegBuilder::build.

let mut out = // ...
builder.build(&mut out).unwrap();

For V3 MEGA files, MegBuilder provides a set_encryption method which can be used to set an encryption key to use to encrypt the MEGA file’s contents.

Modules§

crypto
Provides the Key type for dealing with encrypted MEGA files.
path
Provides utility types for working with MEGA file paths.
reader
Implements MEGA file reading.
version
Proives types for selecting different MEGA file versions.
writer
Implements MEGA file writing.