d2skills.c 4.37 KB
#include "d2skills.h"
#include "d2stat.h"

#include <stdio.h>
#include <stdlib.h>

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;
}