RoboPhred Posted April 13, 2018 Share Posted April 13, 2018 A browser-based tool for editing ONI save files Version 3.15.0 is Here! Features: Compatility follows the most recent stable branch Open Source Local editing: The save file is never sent to any server; all edits are performed in browser Offline editing: Under settings, enable "Offline Mode" for use when no internet is available. Split into Standard and Advanced editors Loading and Saving progress reports Editors Difficulty Duplicant Name Gender Appearance Health, stamina, diseases, infections, other values Health state (Alive, Dead, Invulnurable) Skills Experience Job mastery Interests Add and remove Traits Apply and remove status effects* Geysers Emission type (Now with visual changes!) Emission rate Lifecycle time Active time Emission time Materials list View total stored and loose material Delete loose material for performance Raw data editor Edit All the Things * While you can find effects like "Vomit" and "NarcolepticSleep" in here, they are purely the "effect" of such conditions. They are what changes the dup's stress or stanima, but do not trigger the actual behavior. Use the save editor at your own risk! Use it sparingly, and do not report crashes for saves modified in uncanny ways. The editor can be found at: https://robophred.github.io/oni-duplicity/ Please report bugs and make feature requests at: https://github.com/RoboPhred/oni-duplicity/issues The site is built in Typescript using the React framework, with Redux as a backing store. Both projects are written in Typescript, and can be found here: Save Parser Library Web Editor Source The editor and its underlying library is fully open source under the MIT license, meaning you can do whatever with it so long as you include the license and my copyright notice with it. Link to comment Share on other sites More sharing options...
Lifegrow Posted April 13, 2018 Share Posted April 13, 2018 This sounds amazing - will be trying it over the weekend. Excellent work buddy Link to comment Share on other sites More sharing options...
Lutzkhie Posted April 13, 2018 Share Posted April 13, 2018 appearance is not yet available but this is great Link to comment Share on other sites More sharing options...
RoboPhred Posted April 13, 2018 Author Share Posted April 13, 2018 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. Link to comment Share on other sites More sharing options...
Oozinator Posted April 13, 2018 Share Posted April 13, 2018 3 hours ago, RoboPhred said: Scale (Tall and skinny! Tiny! Fat!) Wee my dreams come true! Link to comment Share on other sites More sharing options...
Grimgaw Posted April 13, 2018 Share Posted April 13, 2018 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? Link to comment Share on other sites More sharing options...
RoboPhred Posted April 13, 2018 Author Share Posted April 13, 2018 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. Link to comment Share on other sites More sharing options...
RoboPhred Posted April 14, 2018 Author Share Posted April 14, 2018 New version rolled out: Support for editing duplicant gender, voice, and appearance. Link to comment Share on other sites More sharing options...
Esquimerisflay Posted April 15, 2018 Share Posted April 15, 2018 I am in trouble, when I edit more than 3 dupes to enter the game he crash, someone could say why ?? Link to comment Share on other sites More sharing options...
RoboPhred Posted April 15, 2018 Author Share Posted April 15, 2018 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) Link to comment Share on other sites More sharing options...
Esquimerisflay Posted April 15, 2018 Share Posted April 15, 2018 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 crash Link to comment Share on other sites More sharing options...
TLW Posted April 16, 2018 Share Posted April 16, 2018 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. Link to comment Share on other sites More sharing options...
RoboPhred Posted April 16, 2018 Author Share Posted April 16, 2018 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 crash 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! Link to comment Share on other sites More sharing options...
Esquimerisflay Posted April 16, 2018 Share Posted April 16, 2018 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? Link to comment Share on other sites More sharing options...
TLW Posted April 17, 2018 Share Posted April 17, 2018 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. Link to comment Share on other sites More sharing options...
watermelen671 Posted April 17, 2018 Share Posted April 17, 2018 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. Link to comment Share on other sites More sharing options...
RoboPhred Posted April 17, 2018 Author Share Posted April 17, 2018 @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. 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. Link to comment Share on other sites More sharing options...
RoboPhred Posted April 17, 2018 Author Share Posted April 17, 2018 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. Link to comment Share on other sites More sharing options...
watermelen671 Posted April 17, 2018 Share Posted April 17, 2018 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. Tbh I'm only asking because a certain friend just wants Gabriel back. Spoiler Link to comment Share on other sites More sharing options...
TLW Posted April 18, 2018 Share Posted April 18, 2018 @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. Link to comment Share on other sites More sharing options...
RoboPhred Posted April 18, 2018 Author Share Posted April 18, 2018 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. Link to comment Share on other sites More sharing options...
TLW Posted April 18, 2018 Share Posted April 18, 2018 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. Link to comment Share on other sites More sharing options...
RoboPhred Posted April 18, 2018 Author Share Posted April 18, 2018 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? Link to comment Share on other sites More sharing options...
TLW Posted April 18, 2018 Share Posted April 18, 2018 Name before entry. We should get our terminology consistent... perhaps parseTemplate versus parseKnownTemplate<type>? I shall do so. Link to comment Share on other sites More sharing options...
Transwarp Posted April 18, 2018 Share Posted April 18, 2018 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. Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.
Please be aware that the content of this thread may be outdated and no longer applicable.