Hey,
I'm currently working on a tool that lets you plan your whole character: items, stats and skills.
There's one functionnality that I would like to implement, but I might need a hand, especially since I don't have much free time. I hope it's ok to talk about this here since it's about game save files. If not, then I'm sorry and delete this thread.
I'm trying to read the .d2s file so you can upload your character on the website and test new builds (the website won't modify the save file, only read it). Though I'm wondering if it's worth working on that since you can only do it with single player characters...
Here's where I'm stuck: the ascii values aren't fixed in one position in the file, so I need to find a fixed point to get the values such as the level, hp etc. I know that items all start with "JM", so this shouldn't be an issue.
Sorry if I'm not clear enough and thanks for any feedback!
Hey,
I'm trying to read the .d2s file so you can upload your character on the website and test new builds (the website won't modify the save file, only read it). Though I'm wondering if it's worth working on that since you can only do it with single player characters...
I like the idea, even if it isn't available for B.net characters.
Quote from "Superceb" »
Here's where I'm stuck: the ascii values aren't fixed in one position in the file, so I need to find a fixed point to get the values such as the level, hp etc. I know that items all start with "JM", so this shouldn't be an issue.
Good news and bad news for you.
Good news, my brother has done this at one point, though neither of us are at home currently, so you'll have to wait until tomorrow in any case.
Bad news is he doesn't know whether he still has the code for it. Stay tuned
PlugY for Diablo II allows you to reset skills and stats, transfer items between characters in singleplayer, obtain all ladder runewords and do all Uberquests while offline. It is the only way to do all of the above. Please use it.
Supporting big shoulderpads and flashy armor since 2004.
Here's where I'm stuck: the ascii values aren't fixed in one position in the file, so I need to find a fixed point to get the values such as the level, hp etc. I know that items all start with "JM", so this shouldn't be an issue.
He doesn't have the code anymore. Please elaborate on what you mean here
PlugY for Diablo II allows you to reset skills and stats, transfer items between characters in singleplayer, obtain all ladder runewords and do all Uberquests while offline. It is the only way to do all of the above. Please use it.
Supporting big shoulderpads and flashy armor since 2004.
Didn't put much time on this feature lastly, I'm currently working on the item creator.
What I mean is that that stats aren't always at the same adress, unless I'm mistaken. It's my first time playing with files like this.
Say I want to get the hp of the character, on one save file the value will be at the 14th character while on another it will be at the 15th (random numbers here).
I tried saving a character after entering the game and doing nothing, then comparing the files before and after. A value changed. My guess is that the played time is registered there and is the value changing, and since it's at the beginning of the file, it shifts the rest of the values, making them impossible to track.
Hope I'm clear enough, sorry for my english and thanks for your help.
Thanks for the info, didn't know about the "gf" and "if". Yes, the .d2s file is pretty easy to read, but what I think I'm doing wrong is my javascript parser...
I'll give it a look and I'll keep you updated.
PlugY for Diablo II allows you to reset skills and stats, transfer items between characters in singleplayer, obtain all ladder runewords and do all Uberquests while offline. It is the only way to do all of the above. Please use it.
Supporting big shoulderpads and flashy armor since 2004.
Sorry was forgetting this, I'm putting some hard work on the skill calculator right now since... we'll maybe you'll know soon but I won't say anything since it's not sure yet.
Sorry for taking so long but as soon as I'm getting back to the character planner I'll give you some feedback.
This is an even better reason to implement this functionality, since what I'm planning to do is show specific breakpoints and stats that you wouldn't see in game. If people test characters in single player, they could easily upload it on the tool and compare it to other builds.
Superceb: Have you checked out Phrozen Keep (d2mods.com). There are quite a few coders there that may be able to help you out. Too bad Yohann is not active (developer of PlugY) he might have been the perfect one to contact in regards to this.
Rollback Post to RevisionRollBack
On Strike and supporting Fallout 4 Mod Makers
Some fallout 4 mod makers have had their mods stolen and uploaded and downloaded on Bethesda's site for the Xbox One.
Superceb: Have you checked out Phrozen Keep (d2mods.com). There are quite a few coders there that may be able to help you out. Too bad Yohann is not active (developer of PlugY) he might have been the perfect one to contact in regards to this.
Thanks, I'll look there if I'm stuck. I'll keep working on this when I'll get back to the Character Planner.
You can contact me if you're in need of help with parsing .D2S files (or any other technical information regarding Diablo II for that matter)
In regards to your specific issue, the bulk of the file is static except for the stats section and the items. The stats section is very straight-forward to parse; each entry begins with a 9-bit identifier followed by the value (of which's length can be gleamed from d2_patch.mpq/global/excel/ItemStatCost.txt)
Below is taken from a project of mine that you may find helpful.
/*
* D2SDecode.cpp
* =============
* Version: 1.0
* Author: Matt.J
*
*
* Description:
* Provides an interface for dealing with Diablo II save-files (.D2S)
*
*
*********************************************************************************************************************/
#include "D2SDecode.h"
using namespace D2S;
/////////////////////////////////////////////////////////////////////
// Internal Functions
/////////////////////////////////////////////////////////////////////
Dword STDCALL ReadBits(Void* pStream, Dword dwOffset, Dword dwSize);
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// General Routines
//
///////////////////////////////////////////////////////////////////////////////////////////////////
Void FASTCALL D2S::Decode(Game::Player* pPlayer, Byte* pD2S)
{
Dword dwOffset = 0;
Dword dwStatId;
Dword dwStatVal;
// Save class.
pPlayer->Character.wClass = ((D2SBase*) pD2S)->Class;
pPlayer->Character.wLevel = ((D2SBase*) pD2S)->Level;
// Traverse to the start of player stats.
pD2S += sizeof(D2SBase);
pD2S += ((D2SQuest*) pD2S)->wSize;
pD2S += sizeof(D2SWaypoints);
pD2S += ((D2SNPC*) pD2S)->wSize;
pD2S += 2; // Skip 'gf'
// Reset total stat count.
pPlayer->Character.wStatsUsed = 0;
// Decode stats and store in the given Player.
while((dwStatId = ReadBits(pD2S, dwOffset, 9)) != 511)
{
// Read the stat value.
dwStatVal = ReadBits(pD2S, (dwOffset += 9), SIT[dwStatId].Size);
dwOffset += SIT[dwStatId].Size;
// Adjust for Diablo II's bullshit.
dwStatVal = (SIT[dwStatId].Div ? (dwStatVal / SIT[dwStatId].Div) : dwStatVal);
// Save.
switch(dwStatId)
{
case 0: pPlayer->Character.wStr = (Word) dwStatVal; break;
case 1: pPlayer->Character.wNrg = (Word) dwStatVal; break;
case 2: pPlayer->Character.wDex = (Word) dwStatVal; break;
case 3: pPlayer->Character.wVit = (Word) dwStatVal; break;
case 4: pPlayer->Character.wStats = (Word) dwStatVal; break;
case 5: pPlayer->Character.wSkills = (Word) dwStatVal; break;
case 6: pPlayer->Character.dwCurLife = dwStatVal; break;
case 7: pPlayer->Character.dwMaxLife = dwStatVal; break;
case 8: pPlayer->Character.dwCurMana = dwStatVal; break;
case 9: pPlayer->Character.dwMaxMana = dwStatVal; break;
}
// Total stat counter.
if(dwStatId < 4) pPlayer->Character.wStatsUsed += ((Word) dwStatVal);
} dwOffset += 9;
// Traverse to skills, stats assumed to be padded to the next byte.
pD2S += (dwOffset / 8) + ((dwOffset % 8) ? 1 : 0);
// Verify header integrity.
if(((D2SSkills*) pD2S)->strTag[0] != 'i' || ((D2SSkills*) pD2S)->strTag[1] != 'f')
{
// ERROR:
Out("[ERROR] Failed to find 'if' header.");
return;
}
// Count total skill points used.
pPlayer->Character.wSkillsUsed = 0;
pPlayer->Character.wHighestSkill = 0;
for(Int i = 0; i < 30; i++)
{
pPlayer->Character.wSkillsUsed += ((D2SSkills*) pD2S)->Level[i];
pPlayer->Character.wHighestSkill = (((D2SSkills*) pD2S)->Level[i] > pPlayer->Character.wHighestSkill ? ((D2SSkills*) pD2S)->Level[i] : pPlayer->Character.wHighestSkill);
}
// Offset based on class.
dwOffset = (BST[pPlayer->Character.wClass].Str + BST[pPlayer->Character.wClass].Dex + BST[pPlayer->Character.wClass].Vit + BST[pPlayer->Character.wClass].Nrg);
pPlayer->Character.wStatsUsed = (Word) (pPlayer->Character.wStatsUsed > dwOffset ? (pPlayer->Character.wStatsUsed - dwOffset) : 0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Helper Functions
//
///////////////////////////////////////////////////////////////////////////////////////////////////
NAKED Dword STDCALL ReadBits(Void* pStream, Dword dwOffset, Dword dwSize)
{
__asm
{
// PROLOGUE:
push ebx
// Calculate the bytewise offset, and remainder.
mov eax, [esp+8]
mov edx, [esp+12]
mov ecx, 7
and ecx, edx
shr edx, 3
// Read in the containing dword.
mov ebx, [eax + edx]
// Compensate for the offset.
shr ebx, cl
// Generate a mask for the significant bits of the desired sequence.
mov ecx, [esp+16]
mov eax, 1
shl eax, cl
sub eax, 1
// Extract the result.
and eax, ebx
// EPILOGUE:
pop ebx
ret 12
}
}
Awesome Matt.J, that's exactly what I was looking for. I knew someone has done it before so I didn't want to keep trying to decode that for hours. I'll keep you updated once I get there.
Thank you!
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
I'm currently working on a tool that lets you plan your whole character: items, stats and skills.
There's one functionnality that I would like to implement, but I might need a hand, especially since I don't have much free time. I hope it's ok to talk about this here since it's about game save files. If not, then I'm sorry and delete this thread.
I'm trying to read the .d2s file so you can upload your character on the website and test new builds (the website won't modify the save file, only read it). Though I'm wondering if it's worth working on that since you can only do it with single player characters...
Here's where I'm stuck: the ascii values aren't fixed in one position in the file, so I need to find a fixed point to get the values such as the level, hp etc. I know that items all start with "JM", so this shouldn't be an issue.
Sorry if I'm not clear enough and thanks for any feedback!
Good news and bad news for you.
Good news, my brother has done this at one point, though neither of us are at home currently, so you'll have to wait until tomorrow in any case.
Bad news is he doesn't know whether he still has the code for it. Stay tuned
He doesn't have the code anymore. Please elaborate on what you mean here
What I mean is that that stats aren't always at the same adress, unless I'm mistaken. It's my first time playing with files like this.
Say I want to get the hp of the character, on one save file the value will be at the 14th character while on another it will be at the 15th (random numbers here).
I tried saving a character after entering the game and doing nothing, then comparing the files before and after. A value changed. My guess is that the played time is registered there and is the value changing, and since it's at the beginning of the file, it shifts the rest of the values, making them impossible to track.
Hope I'm clear enough, sorry for my english and thanks for your help.
I'll give it a look and I'll keep you updated.
BTW, welcome to the forums
Sorry for taking so long but as soon as I'm getting back to the character planner I'll give you some feedback.
Thanks, I'll look there if I'm stuck. I'll keep working on this when I'll get back to the Character Planner.
In regards to your specific issue, the bulk of the file is static except for the stats section and the items. The stats section is very straight-forward to parse; each entry begins with a 9-bit identifier followed by the value (of which's length can be gleamed from d2_patch.mpq/global/excel/ItemStatCost.txt)
Below is taken from a project of mine that you may find helpful.
Thank you!