Save
| Extension | .pc
|
|---|---|
| Folder | <persistentDataPath>/saves/
|
| Header | Unity JsonUtility (GameData), one line
|
| Body | Newtonsoft JSON (ContentData aggregate)
|
| File obfuscation | XOR every character with 129
|
In PC Simulator, a save (a save file) is a persistent snapshot of a play session. Saves use the .pc extension, are stored under the application persistent data directory, and combine a small Unity JSON header with a larger Newtonsoft JSON body. The stored file is XOR-masked with a fixed key. Save Editor covers external editing; Main Menu covers listing and importing files.
Saves are written through SaveManager after SaveManager.Loader has been set (usually from the menu or an example load). Loading runs inside the gameplay scene and rebuilds money, time, difficulty, environment, the player transform, registered scene objects, and world items that implement ISave.
Purpose
A save records enough state to restore a room after exit: global session fields (money, timers, difficulty flags), the player pose, data from each configured SceneObject, and each world Item that participates in ISave. SaveManager.LoadData() consumes the static SaveManager.Loader instance prepared before the scene loads.
Storage location and naming
SaveUtility.GetFolderPath() returns Application.persistentDataPath + "/saves/" and creates the directory if it is missing. The extension is SaveUtility.extension, defined as ".pc".
SaveUtility.GetNewPath(string name) places a file in that folder. If name + ".pc" exists, the routine appends (n) with increasing n. Names that already end with (digits) continue numbering from that index. Import uses this path to avoid overwriting existing files.
On-disk format
Obfuscation
SaveUtility.EncryptDecrypt XORs each UTF-16 code unit with the integer 129. The same function is used on read and write. This is reversible and is not strong encryption; it is adequate only as a light obfuscator.
Logical layout
After decryption, the text is split on the first newline:
- Header: one line of JSON for
GameData, parsed withJsonUtility.FromJson<GameData>. - Body: remaining text, deserialized with
JsonConvert.DeserializeObject<ContentData>.
DataLoader.WriteToFile writes JsonUtility.ToJson(GameData), one newline, then the body string, then applies XOR and writes with File.WriteAllText. A null header parse throws Invalid file!
Header schema (GameData)
| Field | Type | Role |
|---|---|---|
version |
string |
Set from Application.version on save.
|
roomName |
string |
Label in the Main Menu file list. |
coin |
int |
Cash (Main.Money).
|
room |
int |
Added to menu startRoomSceneIndex when opening the scene.
|
gravity |
bool |
If false at load, Physics.gravity is set to zero.
|
hardcore |
bool |
Selects hardcore load behaviour. |
playtime |
float |
Main.playTime (timed achievements).
|
temperature |
float |
AC-related value depending on mode. |
ac |
bool |
AC power when not hardcore. |
light |
bool |
Lamp switch; type default is true. |
sign |
string |
Non-empty forces read-only example mode. |
Body schema
SaveManager.SaveData serializes an object with:
playerData:PlayerDatafromPlayer.SavePlayer().scene: aJObjectfilled by eachSceneObject.ToData.itemData: array of records withspawnId,id, position, rotation, anddataforISavecomponents.
spawnId loads Resources.Load("Components/" + spawnId). Items with position.y < -20 are omitted on save. Load failures (missing prefab or FromData exceptions) increment an error count.
PlayerData
Position x, y, z; body yaw ry; camera pitch rx (normalized from local euler angles). LoadPlayer applies position only if y is between -100 and 100.
Save pipeline
On save, SaveManager copies version, money, play time, lamp state; if not hardcore, copies AC target and power from AirConditioner.instance; builds JSON content; assigns Loader.Content; calls WriteToFile().
Load pipeline
Read-only when Loader.Path is empty or GameData.sign is non-empty: sets Main.example, hides the configured save button.
Header restores money, play time, lamp, gravity, hardcore versus normal AC wiring. Empty Loader.Content activates a designer preset object and skips item deserialize.
OnDestroy resets default gravity and AirConditioner.temperature to normal constants.
Integration
- Main Menu: file list, load, import.
- Examples:
StreamingAssets/Examples/*.pcmay load without a user path (read-only). - Bitcoin: stored in
PlayerPrefs, not in the inspectedGameDatafields.
Compatibility
Import UI text references saves from version 1.7.0 and above. Invalid or truncated files throw or fail individual slots.
See also
References
SaveUtility.cs,DataLoader.cs,GameData.cs,ContentData.cs,ItemData.cs,PlayerData.cs,SaveManager.cs,ISave.cs,SceneObject.cs,Player.cs