Recommended Posts

RoboPhred    132

I think I have access to edit the appearance but I need to investigate it more.  I will try to get the bare minimal function in tomorrow, but hopefully we can get a nice visual Sims style editor on that tab eventually.

  • Like 1

Share this post


Link to post
Share on other sites
Grimgaw    428

This is amazing!

What I'm missing in game is experience display. Always wondered how far dupe is from getting skill up. Should I let Nisbet research a bit longer so she can get that +1 to learning?

Now I can check! Anyone knows what are the experience breakpoints btw?

Share this post


Link to post
Share on other sites
RoboPhred    132
MAX_GAINED_ATTRIBUTE_LEVEL = 20
EXPERIENCE_LEVEL_POWER = 1.7f;
TARGET_MAX_LEVEL_CYCLE = 400;

num = (
    Mathf.Pow(
        currentLevel / MAX_GAINED_ATTRIBUTE_LEVEL,
        EXPERIENCE_LEVEL_POWER
    ) * TARGET_MAX_LEVEL_CYCLE * 600.0);

nextLevelExperience = (
    Mathf.Pow(
        (currentLevel + 1) / MAX_GAINED_ATTRIBUTE_LEVEL,
        EXPERIENCE_LEVEL_POWER
    ) * TARGET_MAX_LEVEL_CYCLE * 600.0
) - num;

I'm sure this math can be explained easier, but its too early in the morning for me to attempt to understand what its really doing.

In my hazy, sleep deprived state, it sort of maybe looks like Triangular Numbers, which I have used when writing similar leveling systems.

 

...upon closer look, I would guess that the total experience required to go from level 0 to level TargetLevel is

( (TargetLevel / 20) ^ 1.7 ) * 400 * 600

The experience to the next level from the current one would naturally be the experience from 0 to next, subtracted by the experience from 0 to current (the "num" variable above)

I keep seeing references to 600 as the game-time of 1 cycle as well.

Edited by RoboPhred
  • Thanks 2

Share this post


Link to post
Share on other sites
RoboPhred    132

What edits are you performing?  Can you send me a save file and the details of what you are changing so I can reproduce it?

Feel free to contact me on the ONI discord for more support, I hang out in the modding section.

 

Edit: Keep in mind that its possible to use this to place the game into states it was never intended to handle.  Make sure your edits are consistent with how the game operates (For example, don't give someone a job they aren't capable of doing due to previous jobs not being mastered)

Edited by RoboPhred

Share this post


Link to post
Share on other sites

I just modified the dupes skill points !! as an example 10 points in the architect's ability to construct and 10 points in the skill of the conzinheiro, following this example when I modify more than 3 dupes, the game crashScreenshot.png

Edited by Esquimerisflay

Share this post


Link to post
Share on other sites
TLW    24

Is there a description of the save format anywhere?

I tried to grock the parser, but... you are in a maze of twisty files, all alike. I'm a C person, not a TS person.

Edited by TLW

Share this post


Link to post
Share on other sites
RoboPhred    132
16 hours ago, Esquimerisflay said:

I just modified the dupes skill points !! as an example 10 points in the architect's ability to construct and 10 points in the skill of the conzinheiro, following this example when I modify more than 3 dupes, the game crashScreenshot.png

I tried modifying any number of skills on many duplicants.  It works fine, so the issue is not simply modify any 3.  Something about your save file or the game state does not like one of the changes you are making. 

Again, I will need your save file and exactly what you are modifying in order to figure out the issue.

 

10 hours ago, TLW said:

Is there a description of the save format anywhere?

I tried to grock the parser, but... you are in a maze of twisty files, all alike. I'm a C person, not a TS person.

 

I had started on documenting it at one point, you can see the remaining bits of documentation over at https://github.com/RoboPhred/oni-save-parser/blob/master/src/versions/7_3/interfaces.ts.

Thats generally a good file to work through, as it contains the major structures of the save, and I tried to keep the fields in order.

The actual parsing is done in the implementations folder, through the parse() function.  The weird bit is the template data, which has been split into multiple (too many) files.  This section comes near the start of the file, lists the name/fields/properties of many of the objects used in the file, and allows the parser to handle most of the data without understanding it ahead of time.  You might find it easier to understand in its pre-refactor monolith form.

If you want to hop into the discord modding channel, I would be happy to help you dig into it more!

Edited by RoboPhred

Share this post


Link to post
Share on other sites
6 hours ago, RoboPhred said:

Eu tentei modificar qualquer número de habilidades em muitos duplicantes. Funciona bem, então o problema não é simplesmente modificar qualquer 3. Algo sobre o seu arquivo salvo ou o estado do jogo não gosta de uma das mudanças que você está fazendo. 

Mais uma vez, vou precisar do seu arquivo salvo e  exatamente o  que você está modificando para descobrir o problema.

 

 

Eu comecei a documentá-lo em um ponto, você pode ver os restantes pedaços de documentação em  https://github.com/RoboPhred/oni-save-parser/blob/master/src/versions/7_3/interfaces.ts .

Isso geralmente é um bom arquivo para trabalhar, pois contém as principais estruturas do save, e eu tentei manter os campos em ordem.

A análise real é feita na   pasta de implementações , através da função parse (). O bit estranho é o  modelo de dados , que foi dividido em vários arquivos (muitos). Esta seção chega perto do início do arquivo, lista o nome / campos / propriedades de muitos dos objetos usados no arquivo e permite que o analisador manipule a maioria dos dados sem compreendê-lo antecipadamente. Você pode achar mais fácil de entender em sua forma de monólito pré-refatoradora  .

Se você quiser entrar no canal de modulação de discórdia, eu ficaria feliz em ajudá-lo a investigar mais!

ok,what's your discord address? and how do I send you my file for you to look at what may be causing the game to fail?

Share this post


Link to post
Share on other sites
TLW    24
14 hours ago, RoboPhred said:

I had started on documenting it at one point, you can see the remaining bits of documentation over at https://github.com/RoboPhred/oni-save-parser/blob/master/src/versions/7_3/interfaces.ts.

Thats generally a good file to work through, as it contains the major structures of the save, and I tried to keep the fields in order.

The actual parsing is done in the implementations folder, through the parse() function.  The weird bit is the template data, which has been split into multiple (too many) files.  This section comes near the start of the file, lists the name/fields/properties of many of the objects used in the file, and allows the parser to handle most of the data without understanding it ahead of time.  You might find it easier to understand in its pre-refactor monolith form.

If you want to hop into the discord modding channel, I would be happy to help you dig into it more!

The particular issue I've got is actually fairly early on.

It goes <0x11,"Klei.SaveFileRoot">, <numFields:int32_t=3>, <numProps:int32_t=0>, <0xc,"WidthInCells">, which all makes sense.

But then there's five (5) bytes between the end of WidthInCells and the start of HeightInCells. The last 4 are the length of the string, as expected... Which would seem to imply that WidthInCells is a byte [0x06? Which seems wrong]. Or that I'm missing something when scanning through. But the only definition I see of "widthInCells" in your template is here - and it simply declares it as a number.

In general, I am finding it very difficult to trace through and find what types map to which deserializers. When the names match it's relatively simple... but what deserializer maps to e.g. Map<string, Uint8Array>? Dictionary? If so, how does that map? Or even 'number'? Ditto, take e.g. Enumeration. Is it simply syntactic sugar around TypeSerializer<number>?

...actually, retracing through the code I think I've just realized that I've been parsing it all wrong - or rather I've been parsing templates as though they were actual data. Am I right in saying that 0x6 means that it's an int32, as in TypeId? If so that makes a whole lot more sense.

Also: what's the value of UserDefinedGeneric and how is it distinguished from SByte?

Edit: just successfully parsed the templates... now on to the actual file.

Edited by TLW

Share this post


Link to post
Share on other sites
watermelen671    10,157

Hm...if I were to take a pre-thermal file, edit it, and then put it back into my saves, would it still be classified as pre-thermal, or is it "modernized", so to speak. 

I'm not really sure if I'm making any sense or not. :wilson_drool:

Share this post


Link to post
Share on other sites
RoboPhred    132

@TLW Glad you are having success!  The thing you need to worry about now is the compression.  There's a flag in the header that indicates if it is compressed, and everything after templates would be deflated if so.  The game uses Ionic.Zlib for this purpose.

After that, just pay attention to the data length values for the prefabs and game objects; some game object behaviors have additional data stored after their template which they have custom parse logic for.  oni-save-parser currently just shoves that into a byte array until I get around to writing the logic for those.

 

3 hours ago, watermelen671 said:

Hm...if I were to take a pre-thermal file, edit it, and then put it back into my saves, would it still be classified as pre-thermal, or is it "modernized", so to speak. 

I'm not really sure if I'm making any sense or not. :wilson_drool:

 

This might be possible, but the library will not attempt to "upgrade" the header.  The thing I would be worried about is if they took the opportunity of the breaking save to add any extra data to some of the behaviors that the game will expect to see.  It might be loadable in this state, but has a high potential for Strange Bugs to occur.  If you are willing to put the effort in, you could probably try to shim in this missing state from a more recent save, but there is a lot of data that ties together I have yet to untangle (mainly, the stuff I shove into the extraData field on behaviors).

If you want to give this a try, you can make a simple node program that uses oni-save-parser which rewrites the header like this:

Warning: The following code is For Science, and shouldn't be countenanced by the sane or rational.

// ...load old save into ArrayBuffer

const saveGame = parseSaveGame(fileData);
saveGame.header.headerVersion = 1
saveGame.header.isCompressed = true;
saveGame.header.gameInfo.saveMajorVersion = 7;
saveGame.header.gameInfo.saveMinorVersion = 3;

const newFileData = writeSaveGame(saveGame);

// ...write newFileData into a new file.

 

Edited by RoboPhred
Fix example code. Apparently I don't know my own library.

Share this post


Link to post
Share on other sites
RoboPhred    132

Pushed an update that fixed save corruption if accent characters were found.  This can happen even if you do not rename anything, as language mods can affect internal text to the save.

If you had game crashes before, try the new version.

Share this post


Link to post
Share on other sites
watermelen671    10,157
4 hours ago, RoboPhred said:

This might be possible, but the library will not attempt to "upgrade" the header.  The thing I would be worried about is if they took the opportunity of the breaking save to add any extra data to some of the behaviors that the game will expect to see.  It might be loadable in this state, but has a high potential for Strange Bugs to occur.  If you are willing to put the effort in, you could probably try to shim in this missing state from a more recent save, but there is a lot of data that ties together I have yet to untangle (mainly, the stuff I shove into the extraData field on behaviors).

If you want to give this a try, you can make a simple node program that uses oni-save-parser which rewrites the header like this:

Warning: The following code is For Science, and shouldn't be countenanced by the sane or rational.


// ...load old save into ArrayBuffer

const saveGame = parseSaveGame(fileData);
saveGame.header.headerVersion = 1
saveGame.header.isCompressed = true;
saveGame.header.gameInfo.saveMajorVersion = 7;
saveGame.header.gameInfo.saveMinorVersion = 3;

const newFileData = writeSaveGame(saveGame);

// ...write newFileData into a new file.

I mean, I suppose I could try...but I'm terrified that if I do, I'd end up somehow causing the game to be deleted. 

Spoiler

inb4 you say that couldn't happen, it already did. My first attempt at doing this caused my AV engine to pick it up as malware...and it "cleaned" my ONI saves. :wilson_cry:

Tbh I'm only asking because a certain friend just wants Gabriel back. eh.png.05cc785e7a527feca82bb5471f36d55c.png

Spoiler

bodybuilder.png.aafb1dc881715d8e23ae1318a18bdf5e.png

 

Share this post


Link to post
Share on other sites
TLW    24

@RoboPhred - success! (Well, at least on a brand-new save). Though I need to clean it up a lot - among other things it currently takes about a minute to dump a save (partly because e.g. arrays parse byte-by-byte, partly because Python is Python, and partly because it's just really hacked together).

FYI, python's zlib library works perfectly for the decompression (zbits 15, separate decompression object). That was actually one of the easiest parts, weirdly enough.

Now to get a decompiler working so I can figure out the few remaining odd cases (SolidConduitSerializer, StateMachineController, Klei.AI.Modifiers, Storage, MinionModifiers, Navigator). I'll post the results here.

Alas, there aren't really any good C# decompilers on Linux - that I've found anyway.

Share this post


Link to post
Share on other sites
RoboPhred    132

Wait, I can imagine why you would want to remake the parser because javascript, but you made it in python of all things?

Kidding aside, theres quite a lot of extra data bolted onto those behaviors.  If you manage to find a good decompiler, look for references to "ISaveLoadableDetails", those are the ones that store additional information in additon to their templates.  I would definitely be interested in getting info on these.  I plan to get to them myself eventually, but i'm mostly focused on exposing the information I do have access to in the UI.

 

Edit:

On the subject of arrays, some of the nastier larger ones are arrays of bytes.  You tend to be able to handle those as special cases; I load them in a single gulp with ArrayBuffer in javascript, and Klei makes use of the .Net array block copy ability to eat up whole primitive arrays in one go.  There's a lot of opportunity for performance improvement here.

Edited by RoboPhred

Share this post


Link to post
Share on other sites
TLW    24

First template (do you want these here? As issues? Other? Better format?):

SolidConduitSerializer

count = int32 >= 0

[parseTemplateType() for i in range count]

Simple enough. The actual code has a lot more wrinkles - it only saves data if for cells that have content, and since it saves the count first it actually loops over everything twice (once to get the count and once to save everything). But if you're just doing a load/save roundtrip all of that work is done for you already.

Share this post


Link to post
Share on other sites
RoboPhred    132

As issues in oni-save-parser would probably be better.  Also, what template does it parse?  Is it one of those that writes the template name before each entry, or does it assume the parser already knows the name and parses the template named in the code?

Share this post


Link to post
Share on other sites
TLW    24

Name before entry. We should get our terminology consistent... perhaps parseTemplate versus parseKnownTemplate<type>?

I shall do so.

Share this post


Link to post
Share on other sites
Transwarp    6

I have 9 Dups inside my savegame and the editor shows me 12 Dups. Some name of them are double but with different jobs they learned. Some of my having a few different jobs because some are never ever needed later like an artist so I switched them to something else. All charakters who have learrned 2 oder more jobs got for each an own profile inside the save game editor.

For changing hair and body it would be nice to see how it looks like at the dups without testing inside the game how he is looking.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now