diff --git a/d2char.c b/d2char.c index 0fa9dd0..eb3a27c 100644 --- a/d2char.c +++ b/d2char.c @@ -227,14 +227,4 @@ int setProgress(D2CharHeader* c, D2S_ACT act, D2S_DIFFICULTY difficulty) { } c->charProgress = progress; return 0; -} - -int getAttribute(D2S_STAT attr) { - // TODO implement - return 0; -} - -int setAttribute(D2S_STAT attr, unsigned int value) { - // TODO implement - return 0; } \ No newline at end of file diff --git a/d2char.h b/d2char.h index af617d5..c4f7963 100644 --- a/d2char.h +++ b/d2char.h @@ -55,25 +55,6 @@ typedef enum D2S_CHARCLASS { D2S_CHARCLASS_ASSASSIN = 6 } D2S_CHARCLASS; -typedef enum D2S_STAT { - D2S_STAT_STRENGTH = 0x00, - D2S_STAT_ENERGY = 0x01, - D2S_STAT_DEXTERITY = 0x02, - D2S_STAT_VITALITY = 0x03, - D2S_STAT_STATPTS = 0x04, - D2S_STAT_SKILLPTS = 0x05, - D2S_STAT_LIFE = 0x06, - D2S_STAT_MAXLIFE = 0x07, - D2S_STAT_MANA = 0x08, - D2S_STAT_MAXMANA = 0x09, - D2S_STAT_STAMINA = 0x0a, - D2S_STAT_MAXSTAMINA = 0x0b, - D2S_STAT_LEVEL = 0x0c, - D2S_STAT_EXPERIENCE = 0x0d, - D2S_STAT_GOLD = 0x0e, - D2S_STAT_GOLDSTASH = 0x0f, -} D2S_STAT; - typedef enum D2S_ACT { D2S_ACT_UNKNOWN = -1, D2S_ACT1 = 0, @@ -119,8 +100,6 @@ typedef struct __attribute__((packed)){ D2WaypointsData waypointsData; uint8_t unknown7; // TODO. Apparently this is always 0x01 uint8_t NPCIntroductions[D2S_NPCDATA_LENGTH]; // TODO: Not implemented - // TODO Add stats - // TODO Add skills } D2CharHeader; @@ -161,7 +140,4 @@ D2S_ACT getCurrentAct(D2CharHeader* c); void getProgress(D2CharHeader* c, D2S_ACT* act, D2S_DIFFICULTY* difficulty); int setProgress(D2CharHeader* c, D2S_ACT act, D2S_DIFFICULTY difficulty); -int getAttribute(D2S_STAT attr); -int setAttribute(D2S_STAT attr, unsigned int value); - #endif \ No newline at end of file diff --git a/d2stat.c b/d2stat.c new file mode 100644 index 0000000..411521f --- /dev/null +++ b/d2stat.c @@ -0,0 +1,56 @@ +#include "d2stat.h" + +#include +#include + +// Internal function, gives the offset ***IN BITS, NOT BYTES*** of the specified stat +unsigned long long int _searchStat(D2S_STAT stat, void* charData, size_t dataLen) { + if(!strncmp(charData, D2S_STAT_HEADER, D2S_STAT_HEADER_LENGTH)) { + fprintf(stderr,"libd2char error: Stat header not present in charData\n"); + return 0; + } + unsigned long long int byteOffset = D2S_STAT_HEADER_LENGTH; + unsigned long long int bitOffset = 0; + while(byteOffset < dataLen) { + uint16_t statID = 0; + memcpy(&statID, charData + byteOffset, sizeof(statID)); + statID >>= bitOffset; + statID &= 0x1FF; + if(statID == D2S_STAT_FOOTER) { + break; + } else if(statID == stat) { + return (byteOffset * 8) + bitOffset; + } + byteOffset += statCost[statID] / 8; + bitOffset += statCost[statID] % 8; + } + return 0; +} + +int getStat(D2S_STAT stat, unsigned int* value, void* charData, size_t dataLen) { + unsigned long long int statOffset = _searchStat(stat,charData,dataLen); + if(!statOffset) { + fprintf(stderr,"libd2char error: Unable to find attribute: %d\n", stat); + return -1; + } + uint64_t statValue = 0; + memcpy(&statValue, charData + (statOffset / 8), sizeof(statValue)); + statValue >>= (statOffset % 8); + statValue &= statCostMask[stat]; + *value = (unsigned int)statValue; + return 0; +} + +int setStat(D2S_STAT stat, unsigned int value, void* charData, size_t dataLen) { + unsigned long long int statOffset = _searchStat(stat,charData,dataLen); + if(!statOffset) { + fprintf(stderr,"libd2char error: Unable to find attribute: %d\n", stat); + return -1; + } + uint64_t statValue = 0; + memcpy(&statValue, charData + (statOffset / 8), sizeof(statValue)); + statValue &= (~statCostMask[stat] << (statOffset % 8)); + statValue |= (value << (statOffset % 8)); + memcpy(charData + (statOffset / 8), &statValue, sizeof(statValue)); + return 0; +} \ No newline at end of file diff --git a/d2stat.h b/d2stat.h new file mode 100644 index 0000000..da75b6f --- /dev/null +++ b/d2stat.h @@ -0,0 +1,73 @@ +#ifndef D2STAT_H +#define D2STAT_H + +#include +#include + +#define D2S_STAT_HEADER "gf" +#define D2S_STAT_HEADER_LENGTH 2 +#define D2S_STAT_FOOTER 0x1FF + +typedef enum D2S_STAT { + D2S_STAT_STRENGTH = 0x00, + D2S_STAT_ENERGY = 0x01, + D2S_STAT_DEXTERITY = 0x02, + D2S_STAT_VITALITY = 0x03, + D2S_STAT_STATPTS = 0x04, + D2S_STAT_SKILLPTS = 0x05, + D2S_STAT_LIFE = 0x06, + D2S_STAT_MAXLIFE = 0x07, + D2S_STAT_MANA = 0x08, + D2S_STAT_MAXMANA = 0x09, + D2S_STAT_STAMINA = 0x0a, + D2S_STAT_MAXSTAMINA = 0x0b, + D2S_STAT_LEVEL = 0x0c, + D2S_STAT_EXPERIENCE = 0x0d, + D2S_STAT_GOLD = 0x0e, + D2S_STAT_GOLDSTASH = 0x0f, +} D2S_STAT; + +// Internal use. Length of each stat in bits +const int statCost[] = { + 10, //D2S_STAT_STRENGTH + 10, //D2S_STAT_ENERGY + 10, //D2S_STAT_DEXTERITY + 10, //D2S_STAT_VITALITY + 10, //D2S_STAT_STATPTS + 8, //D2S_STAT_SKILLPTS + 21, //D2S_STAT_LIFE + 21, //D2S_STAT_MAXLIFE + 21, //D2S_STAT_MANA + 21, //D2S_STAT_MAXMANA + 21, //D2S_STAT_STAMINA + 21, //D2S_STAT_MAXSTAMINA + 7, //D2S_STAT_LEVEL + 32, //D2S_STAT_EXPERIENCE + 25, //D2S_STAT_GOLD + 25 //D2S_STAT_GOLDSTASH +}; + +// Internal use. Mask to apply to the bit offset (when properly shifted) to get the stat value +const uint64_t statCostMask[] = { + 0x00000000000003FF, //D2S_STAT_STRENGTH (10 bits) + 0x00000000000003FF, //D2S_STAT_ENERGY (10 bits) + 0x00000000000003FF, //D2S_STAT_DEXTERITY (10 bits) + 0x00000000000003FF, //D2S_STAT_VITALITY (10 bits) + 0x00000000000003FF, //D2S_STAT_STATPTS (10 bits) + 0x00000000000000FF, //D2S_STAT_SKILLPTS (8 bits) + 0x00000000001FFFFF, //D2S_STAT_LIFE (21 bits) + 0x00000000001FFFFF, //D2S_STAT_MAXLIFE (21 bits) + 0x00000000001FFFFF, //D2S_STAT_MANA (21 bits) + 0x00000000001FFFFF, //D2S_STAT_MAXMANA (21 bits) + 0x00000000001FFFFF, //D2S_STAT_STAMINA (21 bits) + 0x00000000001FFFFF, //D2S_STAT_MAXSTAMINA (21 bits) + 0x000000000000007F, //D2S_STAT_LEVEL (7 bits) + 0x00000000FFFFFFFF, //D2S_STAT_EXPERIENCE (32 bits) + 0x0000000001FFFFFF, //D2S_STAT_GOLD (25 bits) + 0x0000000001FFFFFF //D2S_STAT_GOLDSTASH (25 bits) +}; + +int getStat(D2S_STAT stat, unsigned int* value, void* charData, size_t dataLen); +int setStat(D2S_STAT stat, unsigned int value, void* charData, size_t dataLen); + +#endif \ No newline at end of file