#include "d2skills.h" #include "d2stat.h" #include #include const char* getSkillName(D2S_SKILL skillID) { if(skillID > D2S_SKILL_NUMSKILLS || skillID < 0) { fprintf(stderr,"libd2char error: skillID %d doesn't exist\n",skillID); return NULL; } return skillNames[skillID]; } const D2S_SKILL* _getCharSkillTree(D2S_CHARCLASS class) { switch(class) { case D2S_CHARCLASS_AMAZON: return amazonSkills; break; case D2S_CHARCLASS_SORCERESS: return sorceressSkills; break; case D2S_CHARCLASS_NECROMANCER: return necromancerSkills; break; case D2S_CHARCLASS_PALADIN: return paladinSkills; break; case D2S_CHARCLASS_BARBARIAN: return barbarianSkills; break; case D2S_CHARCLASS_DRUID: return druidSkills; break; case D2S_CHARCLASS_ASSASSIN: return assassinSkills; break; case D2S_CHARCLASS_UNKNOWN: default: 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; }