From 24639c0433774cae725aceaaffbeb7962acd7084 Mon Sep 17 00:00:00 2001 From: Imanol-Mikel Barba Sabariego Date: Wed, 29 Jul 2020 02:34:31 +0100 Subject: [PATCH] Added character skill parsing --- d2skills.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------- d2skills.h | 4 ++++ 2 files changed, 100 insertions(+), 13 deletions(-) diff --git a/d2skills.c b/d2skills.c index d275dfd..c53e410 100644 --- a/d2skills.c +++ b/d2skills.c @@ -1,4 +1,5 @@ #include "d2skills.h" +#include "d2stat.h" #include #include @@ -11,36 +12,118 @@ const char* getSkillName(D2S_SKILL skillID) { return skillNames[skillID]; } -D2S_SKILL getSkillIDFromCharOffset(D2S_CHARCLASS class, unsigned int offset) { - if(offset < 0 || offset > D2S_SKILL_MAXSKILLS_PER_CHAR) { - fprintf(stderr,"libd2char error: Invalid character skill offset: %d\n",offset); - return D2S_SKILL_UNKNOWN; - } +const D2S_SKILL* _getCharSkillTree(D2S_CHARCLASS class) { switch(class) { case D2S_CHARCLASS_AMAZON: - return amazonSkills[offset]; + return amazonSkills; break; case D2S_CHARCLASS_SORCERESS: - return sorceressSkills[offset]; + return sorceressSkills; break; case D2S_CHARCLASS_NECROMANCER: - return necromancerSkills[offset]; + return necromancerSkills; break; case D2S_CHARCLASS_PALADIN: - return paladinSkills[offset]; + return paladinSkills; break; case D2S_CHARCLASS_BARBARIAN: - return barbarianSkills[offset]; + return barbarianSkills; break; case D2S_CHARCLASS_DRUID: - return druidSkills[offset]; + return druidSkills; break; case D2S_CHARCLASS_ASSASSIN: - return assassinSkills[offset]; + return assassinSkills; break; case D2S_CHARCLASS_UNKNOWN: default: - return D2S_SKILL_UNKNOWN; + return NULL; break; } +} + +D2S_SKILL getSkillIDFromCharOffset(D2S_CHARCLASS class, unsigned int offset) { + if(offset < 0 || offset > D2S_SKILL_MAXSKILLS_PER_CHAR) { + fprintf(stderr,"libd2char error: Invalid character skill offset: %d\n",offset); + return D2S_SKILL_UNKNOWN; + } + const D2S_SKILL* skillTree = _getCharSkillTree(class); + if(!skillTree) { + fprintf(stderr,"libd2char error: Invalid character class: %d\n",class); + return D2S_SKILL_UNKNOWN; + } + return skillTree[offset]; +} + +void* _getCharSkillsOffset(void* charData, size_t dataLen) { + // Find skill header and look for previous 2 bytes + // possible good candidates: + // + // 0b0000000111111111 + // 0b000000111111111x + // 0b00000111111111xx + // 0b0000111111111xxx + // 0b000111111111xxxx + // 0b00111111111xxxxx + // 0b0111111111xxxxxx + // 0b111111111xxxxxxx + unsigned int offset = 0; + while(offset < dataLen) { + void* offsetAddr = charData + (offset++); + if(!memcmp(offsetAddr, D2S_SKILL_HEADER, D2S_SKILL_HEADER_LENGTH)) { + uint16_t prevBytes = 0; + memcpy(&prevBytes, offsetAddr, sizeof(prevBytes)); + // Shift a maximum of 7 positions, and check if at some point the candidate + // turns into the 9 bit D2S_STAT_FOOTER value + for(int i = 0; i < 7; ++i) { + if(prevBytes == D2S_STAT_FOOTER) { + return offsetAddr; + } + prevBytes >>= 1; + } + // If we reach here, candidate was not viable, let's find a new candidate + } + } + return NULL; +} + +int getSkillLevel(D2CharHeader* c, D2S_SKILL skillID, void* charData, size_t dataLen) { + const D2S_SKILL* skillTree = _getCharSkillTree(c->charClass); + if(!skillTree) { + fprintf(stderr,"libd2char error: Invalid character class: %d\n",c->charClass); + return D2S_SKILL_UNKNOWN; + } + for(int i = 0; i < D2S_SKILL_MAXSKILLS_PER_CHAR; ++i) { + if(skillTree[i] == skillID) { + void* skillOffset = _getCharSkillsOffset(charData, dataLen); + if(!skillOffset) { + fprintf(stderr,"libd2char error: Unable to find skill data offset in charData\n"); + return -1; + } + return ((uint8_t*)skillOffset)[i + D2S_SKILL_HEADER_LENGTH]; + } + } + fprintf(stderr,"libd2char error: The specified skillID is not in your class's skill tree\n"); + return -1; +} + +int setSkillLevel(D2CharHeader* c, D2S_SKILL skillID, unsigned int level, void* charData, size_t dataLen) { + const D2S_SKILL* skillTree = _getCharSkillTree(c->charClass); + if(!skillTree) { + fprintf(stderr,"libd2char error: Invalid character class: %d\n",c->charClass); + return D2S_SKILL_UNKNOWN; + } + for(int i = 0; i < D2S_SKILL_MAXSKILLS_PER_CHAR; ++i) { + if(skillTree[i] == skillID) { + void* skillOffset = _getCharSkillsOffset(charData, dataLen); + if(!skillOffset) { + fprintf(stderr,"libd2char error: Unable to find skill data offset in charData\n"); + return -1; + } + ((uint8_t*)skillOffset)[i + D2S_SKILL_HEADER_LENGTH] = (uint8_t)level; + return 0; + } + } + fprintf(stderr,"libd2char error: The specified skillID is not in your class's skill tree\n"); + return -1; } \ No newline at end of file diff --git a/d2skills.h b/d2skills.h index 117e197..102a7a8 100644 --- a/d2skills.h +++ b/d2skills.h @@ -8,6 +8,8 @@ #define D2S_SKILL_NUMSKILLS 357 #define D2S_SKILL_MAXSKILLS_PER_CHAR 30 +#define D2S_SKILL_HEADER "if" +#define D2S_SKILL_HEADER_LENGTH 2 typedef enum D2S_SKILL { D2S_SKILL_UNKNOWN = -1, @@ -964,5 +966,7 @@ const char* const skillNames[] = { // Returns static string from library memory, no need to free const char* getSkillName(D2S_SKILL skillID); D2S_SKILL getSkillIDFromCharOffset(D2S_CHARCLASS class, unsigned int offset); +int getSkillLevel(D2CharHeader* c, D2S_SKILL skillID, void* charData, size_t dataLen); +int setSkillLevel(D2CharHeader* c, D2S_SKILL skillID, unsigned int level, void* charData, size_t dataLen); #endif \ No newline at end of file -- libgit2 0.22.2