News:

Simutrans.com Portal
Our Simutrans site. You can find everything about Simutrans from here.

SimutransPak.dll -- A C# library for parsing Simutrans paks

Started by TygerFish, December 14, 2012, 07:13:11 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

TygerFish


What it is and why

I've been working with JamesPetts and The Hood on some balance changes to pak128.Britain-Experimental and found it difficult to see how values looked in context.  I started with a couple spreadsheets, but that quickly became unwieldy and had the continued burden of making sure I kept them updated with the values I was changing in the pak.

Being a C# programmer by day, I'm a big fan of LINQPad:
http://www.linqpad.net/
LINQPad is one part lightweight C# sandbox, one part database frontend.  C# is a joy to use in part because of its hybrid nature; one of its pseudo-functional components is LINQPad's namesake (http://en.wikipedia.org/wiki/Language_Integrated_Query).  This allows you to combine the procedural ease of C-family languages with the set-focused operations of SQL languages.

So I wrote a plugin to parse Simutrans pak files as C# objects so that I could query them in LINQPad.

https://github.com/calvin-fisher/SimutransPak

A precompiled Windows .dll is attached to this message.

How to use it

LINQPad can be downloaded here:

http://www.linqpad.net/

To set up my library in LINQPad, go to Query > Query Properties.  On the Additional References tab (it should start as the visible one), click  Add... and browse to the location of the SimutransDat.dll.  Then just click OK and start querying.

Once you're set up, simply create a new instance of the Pak object and pass in the path of the folder as the first parameter.  Notice that the translated name is also included; if you pass in a second string parameter (the default is "en" if not specified), it will use that translation file in the text diretory.  The pak has an Objects property that serves as the main data source -- it gives you access to all recognized Simutrans objects that were in .dat files.  Then, you write your query from there... here's a LINQ tutorial to help you get started:
http://msdn.microsoft.com/en-us/library/vstudio/bb397927.aspx

Why it's cool

Here are a couple concrete examples of what you can do with this.  First, a simple question: "give me all train engines, ordered by introduction date."

https://dl.dropbox.com/u/7182124/simutrans_linqpad.PNG

This illustrates one the basic features of LINQPad that I'm using: the Dump() method inspects a C# object and translates all of its public properties into columns.  As for the query itself, we first have to filter by objects that are vehicles, then only ones that travel on the track waytype, then ones that have an engine_type declared (i.e., travel under their own power), then only ones with cost above zero (to filter out tender vehicles).

This is much easier than having to poke through .dat files, and it took about a third of a second on my computer to scan all of the pak's subdirectories, parse the files, and filter the results.

Next, a slightly more complicated question: "show me all train cars (not engines), ordered by introduction date, and include the running cost per unit of cargo."

https://dl.dropbox.com/u/7182124/simutrans_linqpad_2.PNG

Rather than using the native DatObject type for this one, I created my own anonymous type (http://msdn.microsoft.com/en-us/library/bb397696.aspx).  This is where the hybrid nature of the language really helps us out.  First, for the name and freight columns, I use the C# ?? operator (http://msdn.microsoft.com/en-us/library/ms173224(v=vs.80).aspx) to say "use the first value... unless it's null, in which case use the second one."  For the RunningcostPerUnit column, I wrote an expression that includes OvercrowdedCapacity in the total and round to three decimal places.  That allows us to easily see what the relative running cost is for each vehicle and make informed decisions about how to tweak the balance.

And for a third example, you can query multiple paks at the same time.  Since I'm merging some of The Hood's work on pak128.Britain-standard into pak128.Britain-experimental right now, it's going to be very useful to see at a glance what objects are in -standard that aren't in -experimental:

https://dl.dropbox.com/u/7182124/simutrans_linqpad_3.PNG

This one uses the LINQ correlate of a SQL left join: http://stackoverflow.com/questions/9171063/convert-sql-to-linq-left-join-with-null

Future plans

Currently, I've included a bunch of properties on the DatObject class that I personally find useful in my present work with vehicles.  But I'm planning on expanding the library so that it allows you to have a set of properties more appropriate to whatever you're querying.  Other areas of interest include config files and saving changes.

Dwachs

Parsley, sage, rosemary, and maggikraut.

Combuijs

Reading the code it parses *.dat files, not *.pak files.
Bob Marley: No woman, no cry

Programmer: No user, no bugs



sdog

mentioned on g+:


*Tool: pak file parser library*
_C# library to parse .dat files of a pak-set_

TygerFish wrote a library to parse .dat files. When developing and balancing pak-sets it is often difficult to keep the overview of the properties of the large number of objects. This library helps to parse the .dat files and work with the data in your own C# tools.

TygerFish

Attached new version to first post with a duplicate-property bugfix.  Included several more examples on the github readme.

Zeno

Hey TygerFish, this looks interesting.

Maybe you want to take a look at my mass editor project at sourceforge: http://sourceforge.net/projects/st-mass-dat/
It is done in more a manual way, but it includes many more options; most of them aren't 100% finished though, but basically reads DAT files massively and allows massive editing and saving.

Feel free to browse my code and use any part that you find useful (if you find any). Also, let me know if I can help somehow :)