I've been tinkering with the
.fvne_ic files in which FurryVNE saves Interactions. I can offer a preliminary report. The
tl;dr is that
there's currently no reason to manually edit the save files.
Technical Details
Compression/decompression is straightforward ("bare" LZMA, no headers). Manipulation of the file contents is annoying because it doesn't really use delimiters - they're
present, but largely irrelevant. Each block of data (such as a string) is preceded by a byte which declares its length. Like-for-like replacement (e.g. swapping one GUID for another GUID, or changing the value of a float) is easy and safe. Any
complex rewrite activity must include careful inspection and adjustment of the length-declaration bytes - otherwise, the EasySave2 library will fail to parse the file and so FVNE will fail to load it.
Data blocks are usually nested. You might splice in two extra letters into a string, adjust the local length declaration from 0x08 to 0x0a, and assume that all is well. Well, your "minor" edit also changed the parent block from 242287 bytes to 242289 bytes. You can either hunt down and edit that value ... or (my preferred approach) just setup a local buffer of junk data and then insert/remove bytes as needed to preserve the total file length.
Findings
FVNE automatically assigns tags to the Interaction. They're currently invisible to the user but they're saved in the
.fvne_ic file. Examples: male/female, vaginal_penetration, male/male, anal_penetration. This is a nice bit of idiot-proof streamlining which should make it convenient to browse Interactions once they're available on the Cloud.
The Interaction itself has a name and GUID. Presumably the Interaction class inherits from an underlying type which has these two properties, because they're useless during editing+playback. There's no disambiguation benefit because it's impossible for multiple Interactions to be loaded concurrently. The player currently has no means to view or edit the Interaction name (it's literally named "Interaction" in the save file). The name will presumably be relevant once we're sharing and downloading these things. Cloud storage seems to use integer serial numbers, so the GUID may not actually be useful on the backend.
FVNE assigns a GUID to each keyframe. I expected to find GUIDs for named elements (such as Characters and Timelines). Using them for keyframes seems very wasteful. It definitely bloats the save file and makes everything harder to read.
The FVNE file-loading code is very robust. I attempted to force mis-typed data into various fields (such as giving it a floating-point value where it expected a refence to a Character, or a Null reference where it expected a Rotation value). These efforts
never produced an aberrant result in-game, nor was I able to crash the game. FVNE simply ignored/discarded the invalid input while continuing to process the file. It would accept any valid data before+after the corruption, retaining as much as possible.
It is unusual to see this level of dev effort applied to input handling, because these files are
not supposed to be user-editable. It suggests a very high level of professionalism by the developer. Alternatively, the team might have encountered a lot of invalid
.fvne_ic files during development. Perhaps FVNE's save-file format has evolved over time, and robust code was added to ensure that the game could reliably import files generated by its own previous versions.
You can rename the default Timeline. The change is persistent (the game will
not revert it during save/load operations) but it's purely cosmetic. The "tluafed" timeline still behaves as the default (i.e. it has zero duration, it automatically receives Timeline self-adjustments, it cannot be deleted).
The game logic supports extended characters (they're just bytes in the save file). FurryVNE doesn't always render them correctly (maybe due to font limitations), but they reproduce correctly across save/load/compress/decompress. If you want to name a Scene ﴿ﭏ﴾ then feel free to do so; there's no danger of corrupting your file.
There's a sharp contrast in file-handling behavior. If the file length is off by a single byte, then EasySave2 will throw a parser error and reject the entire file (without giving any guidance about what
specifically is wrong). If EasySave2 is able to parse the file, then FVNE will work very hard to make sense of it. The first step is very brittle but the second is very forgiving.
FVNE's input-sanitizing and fault-tolerant approach is a problem for me. I was hoping to deliberately inject some garbage in order to selectively disable an element at runtime, but I've been unsuccesful thus far. The worst I was able to accomplish was parenting a Node onto itself (the game continues to run, but the character uncontrollably flies away due to positive feedback).
There are several properties (such as a Character's name) which are "timeless" - any change to its value is applied across the entire Scene. Adjustment of these values does not get captured into the currently active Timeline, nor does it get stored in the Default timeline. When the Interaction is saved, these values are written into a special section called
_objData_.
_objData_ syntax differs from the usual timeline-adjustable stuff and (AFAICT) it is not susceptible to manipulation via keyframes. If someone can figure out how to rewrite
_objData_ at runtime then please let me know - one of my projects is on-hold until I find a way to alter timeless properties.
It's possible to inject syntactically-valid but semantically-dubious values. PenetratorWeight has a range of [0...1]. You can inject a keyframe with PenetratorWeight of -1 or 69. This doesn't really do anything; the characters still move along the expected tracks and obey intuitive in-game limits. They just reach the limit much sooner than expected. Similarly, out-of-bounds values do
not allow us to increase the power of expressions. You can edit the Interaction file and force a value of 500% into the Anger parameter, but when the file is loaded in-game the character will show 100% anger. You can specify -100% Fear but they'll show 0% instead.
Practical (?) applications
It's possible to create unreachable keyframes. Keyframe timing is a floating-point value in the range [0...1]. If your timeline is 60 seconds long, then a timing value of 0.5 means that the keyframe occurs at 00:30.00. By editing the
.fvne_ic file, you can shift the timing to 1.1 (i.e. the keyframe is now scheduled to occur at 00:66:00). The resulting keyframe will
never be reached during playback, but it can cause some jerky motion if it's spliced into an otherwise-smooth animation loop (because the interpolation logic doesn't "understand" that the keyframe's timing is broken).
Note: unreachable keyframes are normally invisible to the user. But if it's
near the normal range (e.g. 1.02), you'll still be able to click on it. You can then drag-and-drop it onto a normal part of the timeline. I can't think of a use-case for this, but maybe someone out there has a clever idea.
It's possible to edit the Version information at the top of the file. This isn't currently relevant, because the 2024-06-19 version of the game seems to ignore this tag. It will load Interaction files even if I give them very silly version numbers (such as 40000-01-01 or 1969-06-09).
Let's assume that FVNE eventually starts to apply strict version-checking, and the game refuses to load any Interaction files created in a version newer than the one that you're running. In that case, you might be able to bypass the check by manually editing the version information within the
.fvne_ic file.
Note: you would presumably also need to download the
.fvne_compiled_character files and alter
their Version information as well. That's a more straightforward edit, because those files are uncompressed and hence they don't require any LZMA operations.
Edit - Two More Applications
It is possible to find-and-replace an entire "row" within the timeline, perfectly preserving all of the keyframe data (timestamps. values, interpolation) but applying them to a different object within the scene.
The most obvious application involves Coupling objects. When you designate a Character as the Target of a Coupling, that Character's pelvis becomes permanently slaved to the position+rotation of the Coupling node. Perhaps you had previously done a lot of animation work with the Character's pelvis (such as dancing, striptease, foreplay, etc).
FVNE laughs at your lack of foresight. It invites you to
re-do all of your work: manually shift+rotate the Coupling node to recreate each pelvis position. And then you must right-click each new keyframe to apply the correct interpolation option.
But there's a better solution! With a bit of careful work in the
.fvne_ic file, you can reassign all of your previous animation work onto the Coupling node itself. The Coupling node will dance around the stage according to your original plan, and it will drag the Character's pelvis into all of the appropriate positions. It's a straightforward text swap, and the complexity of the operation does
not change based on the number of keyframes involved. It's the same amount of work for 1 keyframe or 100 keyframes.
The details are somewhat tricky. You must correctly identify the GUIDs of the Nodes which you intend to swap, and the changes will potentially span several different Timelines (don't forget that the
Default timeline is also implicated). I could write out a tutorial if people are curious, but it would probably be easier to just send me your
.fvne_ic file so that I can perform the replacement.
If you intend to "swap" two Characters then please
do not rely on this approach. This technique demands one manual operation for each row of each timeline. A complex scene will probably have hundreds of rows; you'll inevitably make a mistake (which usually "bricks" your Interaction file) before the work is complete. Use the main menu instead.
File >
Load & Replace.
It is possible to fine-tune the numeric values of a node within its local space. That is, you can specify xyz coordinates and rotation relative to the parent object.
In practice, this is mostly worthless. Precise adjustment is nice when you can see immediate results. When you must translate each floating point value into hex, save+compress the file, and then reload the scene... you'll probably endure 1 minute (or more!) of "lag" before you can observe the effect of your edit. The process would be impractical and frustrating.
But there's still a valid use-case. Let's say that you want one object to follow another object exactly - but it's very tricky to get the two perfectly aligned via FVNE's mouse-driven widgets. Instead, just assign Object A as the parent of Object B. Don't worry about the fact that they're far apart and pointing in different directions. Save the file, decompress and open in the hex editor, and clear out all of those unwanted numbers until only zeroes remain. I neglected to make a before-and-after screenshot, so only the zero coordinates are shown below. I added some markup to explain the meaning of each section.