STEAM GROUP
Nier: Automata Mining and Moddin N:A M&M
STEAM GROUP
Nier: Automata Mining and Moddin N:A M&M
19
IN-GAME
132
ONLINE
Founded
March 24, 2017
Language
English
All Discussions > General > Topic Details
SkacikPL Mar 24, 2017 @ 1:43pm
Base information about save files
Graphics.ini - Actually only stores GPU information, not realy relevant to any kind of modding

System data file - Stores actual settings, including PC specific settings, however stored values are only indexes of selected options within the ingame settings menu (thus can't be fed custom values) value range starts with higher values for "lower" settings and always end with 0 for "highest" value.

Actual slot save file - Can be easily edited with hex editor, byte size has to be retained otherwise game won't load save. It's possible to add flags or edit current player and other events. No doubt also stores current inventory but it seems encrypted within the file.

Feel free to expand if you have any relevant information.
< >
Showing 1-11 of 11 comments
Aemony Jan 7, 2018 @ 2:14pm 
The game uses the Steam64 ID as a signature in GameData.dat and SaveSlot_#.dat.

It is stored as little-endian in the following locations:
GameData.dat - offset 00 - 07
SaveSlot_#.dat - offset 04 - 11

GameData.dat stores general purpose settings not tied to any particular save slot nor the systems data settings. Most obvious point being the background of the title screen.

GameData.dat is read after the initial loading screen to the title screen on game launch. If the Steam64 ID stored within this file doesn't match the current player signed in to Steam then the game will overwrite the whole file from scratch, resetting the title screen background in the process.

After that the game loads and compares the signatures of the individual SaveSlot_#.dat to the current player signed in to Steam. If the signatures doesn't match then those saves will not show up under the save slot screen.
Last edited by Aemony; Jan 7, 2018 @ 2:14pm
Aemony Feb 13, 2018 @ 2:08am 
Inventory is located at block 30570 -> 31D6F in the save files. There's no separation between inventory slots, but item ID and count is separated by a divider that indicates item slot "status". As with everything else the data is stored as little-endian.

The block is 6 144 bytes large, and covers 512 items with 12 bytes dedicated to each item (6 144 / 12). Further on, this block is split into two sections, with 3072 bytes/256 items in each section. The first section is for the current "active" inventory, while the second section is for the corpse/lost inventory.

Each item is stored side-by-side and is divided into 3x 4 byte sections:

ID ID ID ID ZX ZX ZX ZX NO NO NO NO

ID = Item ID stored at that section

ZX = Status of that item slot. FF FF FF FF == Inactive item slot post-prologue. 00 00 07 00 == Active item slot.

NO = Amount of said item stored

Prologue/new game save slots are slightly different in that they use FF FF FF FF even for active item slots.

Screenshot: https://i.imgur.com/TlNORek.png

15x Small Recovery (item_rec_S, item ID 0) :
00 00 00 00 FF FF FF 0F 00 00 00

5x Medium Recovery (item_rec_M, item ID 1) :
01 00 00 00 FF FF FF 05 00 00 00

1x Large Recovery (item_rec_L, item ID 2) :
02 00 00 00 FF FF FF 01 00 00 00

3x Melee Attack Up (S) (item_stUp_pAtk_S, item ID 100) :
64 00 00 00 FF FF FF 03 00 00 00

3x Melee Defense Up (S) (item_stUp_pDef_S, item ID 120) :
78 00 00 00 FF FF FF 03 00 00 00

Edit: Experience is stored at offset 3056C, as a 2-byte value just before the inventory list begins at offset 30570.

Edit 2: DLC items are not stored in the corpse inventory section between the Prologue and City Ruins chapters. Even if you manually add them to the corpse inventory before retrieving the corpse they will not be read either. Only vanilla game items will be retrieved from the corpse inventory.

Further on, the 5-6 first item slots seems to be dedicated to the various Day One Edition and Valve Accessory Pack DLCs on Steam. If these haven't been added to the inventory yet then the game will rearrange the inventory and store those in the first slots. This can cause loss of data as, again, DLC items are ignored if they're stored in those slots and are overwritten.
Last edited by Aemony; Feb 13, 2018 @ 5:42pm
Aemony Feb 13, 2018 @ 7:58pm 
Since I was bored, here's the structure of SystemData.dat. Offsets are in decimal, and as usual the values are stored in little-endian.

00-07 - SteamID64

08-11 - Width
12-15 - Height

16-19 - Window Mode | 0 = Window, 1 = Fullscreen
20-23 - Effects | 0 = High, 1 = Low

24-27 - Monitor | 0 = \.\\DISPLAY1, 1 = \.\\DISPLAY2
28-31 - Vertical Sync | 0 = Off, 1 = On

32-35 - Anti-Aliasing | 0 = 8x, 1 = 4x, 2 = 2x, 3 = Off
36-39 - Texture Filtering | 0 = 16x, 1 = 8x, 2 = 4x, 3 = 2x, 4 = Off

40-43 - Blur | 0 = Off, 1 = On
44-47 - Shadows | 0 = High, 1 = Medium, 2 = Low

48-51 - Ambient Occlusion | 0 = Off, 1 = On
52-55 - ?

The last 4 bytes doesn't seem to store anything in particular, or at least I haven't been able to find any setting in-game that changes those, nor have I noticed any discernible difference when playing around with the value of the bytes.
Last edited by Aemony; Feb 13, 2018 @ 7:58pm
Aemony Feb 13, 2018 @ 8:19pm 
Graphic.ini stores the name of the GPU and output monitor in hexadecimal, with 2 byte for each character.

VideoCard = 100 characters max (meaning 50 characters for Latin alphabet)
Output = 40 characters max (meaning 20 characters for Latin alphabet)

Example output:
VideoCard=N.V.I.D.I.A. .G.e.F.o.r.c.e. .G.T.X. .1.0.8.0. .T.i.............................................................................................................................................................................................................O
Output=\.\...\.D.I.S.P.L.A.Y.1.........................................‰
Mostly reversed file read io function that the game uses. Handles SystemData.dat, GameData.dat and SaveData_%d.dat. It's asynchronous due to the usage of the OVERLAPPED struct and it doesn't have any sharemode on CreateFile. Which means it locks the files for modification. Another worthy note is SHGetSpecialFolderPathA is unsupported according to msdn, so it's kind of interesting why they still chose to use it.

SHGetSpecialFolderPathA Docs:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb762204(v=vs.85).aspx

void __fastcall SaveFileIO_Read(CSaveDataDevice *pSavedata) { CSaveDataDevice *pSave; // rbx int v2; // ecx int v3; // ecx int v4; // ecx QWORD v5; // rax HANDLE hFile3; // rcx HANDLE hFile4; // rcx int nSlot2; // eax char *pFileBuffer; // rax void *pData; // rcx size_t size; // r8 void *pFileBufferData; // rdx CSteamID *pSteamId; // rdi SteamInterface_t *pInterfaces; // rax int v15; // edx HANDLE hFile2; // rcx int slot_number; // eax const char *szFileName; // r8 HANDLE hFile; // rcx int nSlot; // eax DWORD bytesIO; // eax void *pBuffer; // rdx __int64 nSlot3; // [rsp+20h] [rbp-378h] char v24; // [rsp+40h] [rbp-358h] char szDest[260]; // [rsp+50h] [rbp-348h] char szAbsoluteFilePath[260]; // [rsp+160h] [rbp-238h] CHAR szPath[260]; // [rsp+270h] [rbp-128h] pSave = pSavedata; v2 = HIDWORD(pSavedata->qwFlags); if ( !v2 ) { if ( !SHGetSpecialFolderPathA((HWND)NULL, szPath, CSIDL_MYDOCUMENTS, FALSE) ) goto CleanupFailure; snprintf(szDest, MAX_PATH, "%s\\%s\\%s\\", szPath, "My Games", "NieR_Automata"); slot_number = pSave->nSlot; if ( slot_number == -2 ) { szFileName = "%sSystemData.dat"; } else { if ( slot_number != -1 ) { LODWORD(nSlot3) = pSave->nSlot; snprintf(szAbsoluteFilePath, MAX_PATH, "%sSlotData_%d.dat", szDest, nSlot3); ReadFile: SetLastError(ERROR_SUCCESS); hFile = CreateFileA(szAbsoluteFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); pSave->hFile = hFile; if ( hFile != (HANDLE)INVALID_HANDLE_VALUE ) { nSlot = pSave->nSlot; if ( nSlot == -2 ) { bytesIO = pSave->nBytesIO_SystemData; } else if ( nSlot == -1 ) { bytesIO = pSave->nBytesIO_GameData; } else { bytesIO = pSave->nBytesIO_SaveData; } pBuffer = pSave->pBuffer; pSave->nBytesToIO = bytesIO; HIDWORD(pSave->qwFlags) = 2 - (ReadFile(hFile, pBuffer, pSave->nBytesToIO, &pSave->nBytesIO, &pSave->overlapped) != 0); return; } GetLastError(); if ( GetLastError() - 2 <= 1 ) { SaveFileIO_CloseFileHandle(pSave, 4); return; } CleanupFailure: HIDWORD(pSave->qwFlags) = 2; return; } szFileName = "%sGameData.dat"; } snprintf(szAbsoluteFilePath, MAX_PATH, szFileName, szDest); goto ReadFile; } v3 = v2 - 1; if ( !v3 ) { if ( LODWORD(pSave->overlapped.Internal) == STATUS_PENDING ) return; if ( pSave->nBytesIO != pSave->nBytesToIO ) { HIDWORD(pSave->qwFlags) = 2; goto CloseFileHandle; } nSlot2 = pSave->nSlot; if ( nSlot2 == -2 ) // SystemData { pData = pSave->pSystemDataBuffer; size = pSave->qwSizeSystemDataBuffer; } else { if ( nSlot2 != -1 ) // GameData { pFileBuffer = (char *)pSave->pBuffer; pData = pSave->pGameDataBuffer; size = pSave->qwSizeGameDataBuffer; pFileBufferData = pFileBuffer + 12; pSteamId = (CSteamID *)(pFileBuffer + 4); CopyData: memcpy(pData, pFileBufferData, size); pInterfaces = (SteamInterface_t *)SteamInternal_ContextInit(pSteam_InitInterface_WrapperFn); if ( pSteamId->m_steamid.m_unAll64Bits == *(_QWORD *)(*(__int64 (__fastcall **)(ISteamUser *, char *))(*(_QWORD *)pInterfaces->pISteamUser + 16i64))(pInterfaces->pISteamUser, &v24) ) v15 = 0; else v15 = 7; SaveFileIO_CloseFileHandle(pSave, v15); CloseFileHandle: hFile2 = pSave->hFile; if ( hFile2 != (HANDLE)INVALID_HANDLE_VALUE ) { CloseHandle(hFile2); pSave->hFile = (HANDLE)INVALID_HANDLE_VALUE; } return; } pData = pSave->pSlotDataBuffer; size = pSave->qwSizeSlotDataBuffer; } pSteamId = (CSteamID *)pSave->pBuffer; pFileBufferData = &pSteamId[1]; // = (void*)(pSave->pBuffer + 8) goto CopyData; } v4 = v3 - 1; if ( v4 ) { if ( v4 != 1 ) return; v5 = pSave->qwUnk0xC8; if ( v5 ) { if ( !*(_DWORD *)(v5 + 8) ) return; if ( v5 ) *(_DWORD *)(v5 + 12) = 1; } pSave->qwUnk0xC8 = 0i64; hFile3 = pSave->hFile; if ( hFile3 != (HANDLE)INVALID_HANDLE_VALUE ) { CloseHandle(hFile3); pSave->hFile = (HANDLE)INVALID_HANDLE_VALUE; } pSave->field_1C = 3; pSave->qwFlags = 0i64; } else { hFile4 = pSave->hFile; if ( hFile4 != (HANDLE)INVALID_HANDLE_VALUE ) { CloseHandle(hFile4); pSave->hFile = (HANDLE)INVALID_HANDLE_VALUE; } pSave->field_1C = 3; pSave->qwFlags = 0i64; } }
Last edited by martymoose21; May 7, 2018 @ 7:21pm
Drakaenae Aug 12, 2018 @ 10:48pm 
Any idea where intel information is stored, specifically fishing data in SaveSlot_#.dat?
76561197963075328 Aug 13, 2018 @ 11:40am 
Originally posted by Drakaenae:
Any idea where intel information is stored, specifically fishing data in SaveSlot_#.dat?

Me personaly not a clue.
Drakaenae Aug 17, 2018 @ 10:52pm 
Alright, I found most of the addresses for fish intel.

For intel (at least fish intel) it looks like each bit is a boolean and some bytes store this data.
So 10000001 means 2/8 fish in the byte are unlocked

000385d8 - 000385db: unlocks 32 fishes (too lazy to find which ones)
000385de: one of the bits unlock Broken Firearm, not sure about the rest
000385df: unlocks another 8 fishes (too lazy to find which ones)

000385e0 - 000385e3: corresponds to the first 32 fishes, determines if the intel has been read
000385e6: probably corresponds to 000385de (did not check)
000385e7: probably corresponds to 000385df

I'm planning on writing a save editor, but this seems like a lot of work. Maybe I'll just write one for the fishes. But if I do, i'll figure out which bits correspond to which fish then.
Drakaenae Aug 18, 2018 @ 11:53pm 
based on https://steamcommunity.com/app/524220/discussions/0/1327844097113668255/
The plugin-chips are saved from around address 0x00032698 to 0x000349BF as 24 bytes values
https://github.com/CensoredUsername/Nier-Automata-editor
I guess you're in luck, creds to _xxk for the link. Also, you're welcome to join our modding discord for NieR:Automata. https://discord.gg/v8Yyrrg
Last edited by martymoose21; Sep 3, 2018 @ 7:08am
76561197963075328 Sep 9, 2018 @ 12:27am 
also there is a csv (comma seperated values) file somewhere called FishParam.csv

Originally posted by Drakaenae:
based on https://steamcommunity.com/app/524220/discussions/0/1327844097113668255/
The plugin-chips are saved from around address 0x00032698 to 0x000349BF as 24 bytes values


Originally posted by Drakaenae:
based on https://steamcommunity.com/app/524220/discussions/0/1327844097113668255/
The plugin-chips are saved from around address 0x00032698 to 0x000349BF as 24 bytes values
< >
Showing 1-11 of 11 comments
Per page: 1530 50

All Discussions > General > Topic Details