Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insufficient documentation #108

Open
Swarkin opened this issue Jun 29, 2024 · 8 comments
Open

Insufficient documentation #108

Swarkin opened this issue Jun 29, 2024 · 8 comments

Comments

@Swarkin
Copy link

Swarkin commented Jun 29, 2024

Why are there no docs on how to use this crate?

  • How do I get a value out of a Property?
  • How do I iterate trough an ArrayProperty?
  • How do I convert a gvas file to structs?
@scottanderson
Copy link
Collaborator

Crate docs are available at https://docs.rs/gvas/latest/gvas/, but they might be incomplete.

@Swarkin
Copy link
Author

Swarkin commented Jun 30, 2024

Can you create a documentation page on how to convert gvas into my own structs? Its very hard to work with the GvasFile directly and I do not understand how accessing data works.

I want to make a save editor. Could you explain how to modify the data inside rust?
gvas.txt

@localcc
Copy link
Owner

localcc commented Jun 30, 2024

Not sure what you mean by convert into your own structs, but if you wanna access and edit data you can look for examples in the tests folder, here's one of them

@scottanderson
Copy link
Collaborator

The GvasFile struct is a wrapper around all of the the things that are needed to recreate the original binary file. For a save editor, you will be primarily interested in looking at the properties map, which is where the game's persisted data is stored.

pub struct GvasFile {
    pub deserialized_game_version: DeserializedGameVersion,
    pub header: GvasHeader,
    pub properties: IndexMap<String, Property>,
}

@Swarkin
Copy link
Author

Swarkin commented Jul 1, 2024

@scottanderson The problem im facing is I dont understand how to navigate the properties. There are nested lists, structs and objects and idk how to extract information? In json I would just do json["foo"]["bar"] etc. but what do I do here?

I sent my gvasfile above for reference.

@scottanderson
Copy link
Collaborator

Understanding how to navigate the properties map can be challenging due to its variability across different games. Each game may store data differently; some use simple arrays while others employ custom structs. This library accommodates these variations with a flexible structure.

The structure of each property is a Property enum wrapper around a specific property type. For example, if you wanted to store a simple string in a GVAS file, you would actually need to wrap that string in all of the following layers to store it:

  1. An Option, to give the string nullability.
  2. A StrProperty to represent the UE type of the string.
  3. A Property enum to act as a common property type that rust can store in a map.

The result is an object that looks like:

Property::StrProperty(
    StrProperty {
        value: Some(String::from("Hello, world!"))
    }
)

Some properties are themselves also enums, especially the container types (array, map, etc.). This is primarily done to make the serde serialized output flatter, more readable and concise.

If you prefer to work in JSON, you can use serde to convert a GvasFile to another format. There's also a cli wrapper for this that you may be interested in, which doesn't require you to write any code at all. See gvas2json.

@Swarkin
Copy link
Author

Swarkin commented Jul 21, 2024

Im having problems specifically with the Struct properties and how I access things inside them or how I can serde them into my own structs.

@scottanderson
Copy link
Collaborator

scottanderson commented Jul 22, 2024

Hi @Swarkin,

The reason I mentioned serde support is that the file that you shared earlier uses the Debug trait representation. In this crate, the serde representation of a GvasFile is much easier to understand than the Debug trait's representation of the data, so I recommend that you use serde_json::to_string instead of the Debug trait to provide a clearer view of the data.

This project focuses on serializing and de-serializing the GVAS format itself, rather than interpreting the specific game data encoded in GVAS files. For instance, Railroads Online stores different aspects of spline tracks in separate ArrayProperty arrays—each for start locations, end locations, tangents, etc.. This crate handles these ArrayProperty types, but doesn’t inherently understand how these arrays relate to the game's SplineTrack objects. Here’s a simplified representation of such data:

"properties": {
  "SplineTrackTypeArray": { "type": "ArrayProperty", "strings": ["rail_914_h01", "rail_914_h01", "rail_914_h01"] },
  "SplineTrackLocationArray": { "type": "ArrayProperty", "type_name": "Vector", "structs": [ /* Vector data */ ] },
  // Other arrays...
}

From the Debug trait output you attached earlier, it looks like your StructProperty values are StructPropertyValue::CustomStructs. These discriminants use a Vec<(String, Property)> to represent a custom properties map. Each entry in the Vec is a tuple of one property name and one property value.

If you do not need to support array property values in the CustomStruct, you can coerce that Vec<(String, Property)> to IndexMap<String, Property> with collect() to make it easier to work with.

use indexmap::IndexMap;

fn vec_to_indexmap(vec: Vec<(String, Property)>) -> IndexMap<String, Property> {
    // This will cause data loss if the input vec contains a property array!
    // See PR #102 for more information about this issue.
    vec.into_iter().collect()
}

fn indexmap_to_vec(map: IndexMap<String, Property>) -> Vec<(String, Property)> {
    map.into_iter().collect()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants