static int load_config(const char *config_file, const char *conf_dir) {
    uint32_t config_size = 0;
    char *config_buffer = NULL;
    const char *dir_end = NULL;
    char *config_dir = NULL;
    uint32_t config_ptr = 0;
    uint32_t line_start_ptr = 0;
    uint16_t patchid = 0;
    struct _patch * tmp_patch;
    char **line_tokens = NULL;
    int token_count = 0;

    config_buffer = (char *) _WM_BufferFile(config_file, &config_size);
    if (!config_buffer) {
        WM_FreePatches();
        return (-1);
    }

    if (conf_dir) {
        if (!(config_dir = wm_strdup(conf_dir))) {
            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, errno);
            WM_FreePatches();
            free(config_buffer);
            return (-1);
        }
    } else {
        dir_end = FIND_LAST_DIRSEP(config_file);
        if (dir_end) {
            config_dir = malloc((dir_end - config_file + 2));
            if (config_dir == NULL) {
                _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, errno);
                WM_FreePatches();
                free(config_buffer);
                return (-1);
            }
            strncpy(config_dir, config_file, (dir_end - config_file + 1));
            config_dir[dir_end - config_file + 1] = '\0';
        }
    }

    config_ptr = 0;
    line_start_ptr = 0;

    /* handle files without a newline at the end: this relies on
     * _WM_BufferFile() allocating the buffer with one extra byte */
    config_buffer[config_size] = '\n';

    while (config_ptr <= config_size) {
        if (config_buffer[config_ptr] == '\r' ||
            config_buffer[config_ptr] == '\n')
        {
            config_buffer[config_ptr] = '\0';

            if (config_ptr != line_start_ptr) {
                _WM_Global_ErrorI = 0; /* because WM_LC_Tokenize_Line() can legitimately return NULL */
                line_tokens = WM_LC_Tokenize_Line(&config_buffer[line_start_ptr]);
                if (line_tokens) {
                    if (wm_strcasecmp(line_tokens[0], "dir") == 0) {
                        free(config_dir);
                        if (!line_tokens[1]) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(missing name in dir line)", 0);
                            WM_FreePatches();
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        } else if (!(config_dir = wm_strdup(line_tokens[1]))) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, errno);
                            WM_FreePatches();
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        }
                        if (!IS_DIR_SEPARATOR(config_dir[strlen(config_dir) - 1])) {
                            config_dir[strlen(config_dir) + 1] = '\0';
                            config_dir[strlen(config_dir)] = DIR_SEPARATOR_CHAR;
                        }
                    } else if (wm_strcasecmp(line_tokens[0], "source") == 0) {
                        char *new_config = NULL;
                        if (!line_tokens[1]) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(missing name in source line)", 0);
                            WM_FreePatches();
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) {
                            new_config = malloc(strlen(config_dir) + strlen(line_tokens[1]) + 1);
                            if (new_config == NULL) {
                                _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, errno);
                                WM_FreePatches();
                                free(config_dir);
                                free(line_tokens);
                                free(config_buffer);
                                return (-1);
                            }
                            strcpy(new_config, config_dir);
                            strcpy(&new_config[strlen(config_dir)], line_tokens[1]);
                        } else {
                            if (!(new_config = wm_strdup(line_tokens[1]))) {
                                _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, errno);
                                WM_FreePatches();
                                free(line_tokens);
                                free(config_buffer);
                                return (-1);
                            }
                        }
                        if (load_config(new_config, config_dir) == -1) {
                            free(new_config);
                            free(line_tokens);
                            free(config_buffer);
                            free(config_dir);
                            return (-1);
                        }
                        free(new_config);
                    } else if (wm_strcasecmp(line_tokens[0], "bank") == 0) {
                        if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in bank line)", 0);
                            WM_FreePatches();
                            free(config_dir);
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        }
                        patchid = (atoi(line_tokens[1]) & 0xFF) << 8;
                    } else if (wm_strcasecmp(line_tokens[0], "drumset") == 0) {
                        if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in drumset line)", 0);
                            WM_FreePatches();
                            free(config_dir);
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        }
                        patchid = ((atoi(line_tokens[1]) & 0xFF) << 8) | 0x80;
                    } else if (wm_strcasecmp(line_tokens[0], "reverb_room_width") == 0) {
                        if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in reverb_room_width line)", 0);
                            WM_FreePatches();
                            free(config_dir);
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        }
                        _WM_reverb_room_width = (float) atof(line_tokens[1]);
                        if (_WM_reverb_room_width < 1.0f) {
                            _WM_DEBUG_MSG("%s: reverb_room_width < 1 meter, setting to minimum of 1 meter", config_file);
                            _WM_reverb_room_width = 1.0f;
                        } else if (_WM_reverb_room_width > 100.0f) {
                            _WM_DEBUG_MSG("%s: reverb_room_width > 100 meters, setting to maximum of 100 meters", config_file);
                            _WM_reverb_room_width = 100.0f;
                        }
                    } else if (wm_strcasecmp(line_tokens[0], "reverb_room_length") == 0) {
                        if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in reverb_room_length line)", 0);
                            WM_FreePatches();
                            free(config_dir);
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        }
                        _WM_reverb_room_length = (float) atof(line_tokens[1]);
                        if (_WM_reverb_room_length < 1.0f) {
                            _WM_DEBUG_MSG("%s: reverb_room_length < 1 meter, setting to minimum of 1 meter", config_file);
                            _WM_reverb_room_length = 1.0f;
                        } else if (_WM_reverb_room_length > 100.0f) {
                            _WM_DEBUG_MSG("%s: reverb_room_length > 100 meters, setting to maximum of 100 meters", config_file);
                            _WM_reverb_room_length = 100.0f;
                        }
                    } else if (wm_strcasecmp(line_tokens[0], "reverb_listener_posx") == 0) {
                        if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in reverb_listen_posx line)", 0);
                            WM_FreePatches();
                            free(config_dir);
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        }
                        _WM_reverb_listen_posx = (float) atof(line_tokens[1]);
                        if ((_WM_reverb_listen_posx > _WM_reverb_room_width)
                                || (_WM_reverb_listen_posx < 0.0f)) {
                            _WM_DEBUG_MSG("%s: reverb_listen_posx set outside of room", config_file);
                            _WM_reverb_listen_posx = _WM_reverb_room_width / 2.0f;
                        }
                    } else if (wm_strcasecmp(line_tokens[0],
                            "reverb_listener_posy") == 0) {
                        if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in reverb_listen_posy line)", 0);
                            WM_FreePatches();
                            free(config_dir);
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        }
                        _WM_reverb_listen_posy = (float) atof(line_tokens[1]);
                        if ((_WM_reverb_listen_posy > _WM_reverb_room_width)
                                || (_WM_reverb_listen_posy < 0.0f)) {
                            _WM_DEBUG_MSG("%s: reverb_listen_posy set outside of room", config_file);
                            _WM_reverb_listen_posy = _WM_reverb_room_length * 0.75f;
                        }
                    } else if (wm_strcasecmp(line_tokens[0], "guspat_editor_author_cant_read_so_fix_release_time_for_me") == 0) {
                        _WM_fix_release = 1;
                    } else if (wm_strcasecmp(line_tokens[0], "auto_amp") == 0) {
                        _WM_auto_amp = 1;
                    } else if (wm_strcasecmp(line_tokens[0], "auto_amp_with_amp") == 0) {
                        _WM_auto_amp = 1;
                        _WM_auto_amp_with_amp = 1;
                    } else if (wm_isdigit(line_tokens[0][0])) {
                        patchid = (patchid & 0xFF80)
                                | (atoi(line_tokens[0]) & 0x7F);
                        if (_WM_patch[(patchid & 0x7F)] == NULL) {
                            _WM_patch[(patchid & 0x7F)] = malloc(sizeof(struct _patch));
                            if (_WM_patch[(patchid & 0x7F)] == NULL) {
                                _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, errno);
                                WM_FreePatches();
                                free(config_dir);
                                free(line_tokens);
                                free(config_buffer);
                                return (-1);
                            }
                            tmp_patch = _WM_patch[(patchid & 0x7F)];
                            tmp_patch->patchid = patchid;
                            tmp_patch->filename = NULL;
                            tmp_patch->amp = 1024;
                            tmp_patch->note = 0;
                            tmp_patch->next = NULL;
                            tmp_patch->first_sample = NULL;
                            tmp_patch->loaded = 0;
                            tmp_patch->inuse_count = 0;
                        } else {
                            tmp_patch = _WM_patch[(patchid & 0x7F)];
                            if (tmp_patch->patchid == patchid) {
                                free(tmp_patch->filename);
                                tmp_patch->filename = NULL;
                                tmp_patch->amp = 1024;
                                tmp_patch->note = 0;
                            } else {
                                if (tmp_patch->next) {
                                    while (tmp_patch->next) {
                                        if (tmp_patch->next->patchid == patchid)
                                            break;
                                        tmp_patch = tmp_patch->next;
                                    }
                                    if (tmp_patch->next == NULL) {
                                        if ((tmp_patch->next = malloc(sizeof(struct _patch))) == NULL) {
                                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, 0);
                                            WM_FreePatches();
                                            free(config_dir);
                                            free(line_tokens);
                                            free(config_buffer);
                                            return (-1);
                                        }
                                        tmp_patch = tmp_patch->next;
                                        tmp_patch->patchid = patchid;
                                        tmp_patch->filename = NULL;
                                        tmp_patch->amp = 1024;
                                        tmp_patch->note = 0;
                                        tmp_patch->next = NULL;
                                        tmp_patch->first_sample = NULL;
                                        tmp_patch->loaded = 0;
                                        tmp_patch->inuse_count = 0;
                                    } else {
                                        tmp_patch = tmp_patch->next;
                                        free(tmp_patch->filename);
                                        tmp_patch->filename = NULL;
                                        tmp_patch->amp = 1024;
                                        tmp_patch->note = 0;
                                    }
                                } else {
                                    tmp_patch->next = malloc(
                                            sizeof(struct _patch));
                                    if (tmp_patch->next == NULL) {
                                        _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, errno);
                                        WM_FreePatches();
                                        free(config_dir);
                                        free(line_tokens);
                                        free(config_buffer);
                                        return (-1);
                                    }
                                    tmp_patch = tmp_patch->next;
                                    tmp_patch->patchid = patchid;
                                    tmp_patch->filename = NULL;
                                    tmp_patch->amp = 1024;
                                    tmp_patch->note = 0;
                                    tmp_patch->next = NULL;
                                    tmp_patch->first_sample = NULL;
                                    tmp_patch->loaded = 0;
                                    tmp_patch->inuse_count = 0;
                                }
                            }
                        }
                        if (!line_tokens[1]) {
                            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(missing name in patch line)", 0);
                            WM_FreePatches();
                            free(config_dir);
                            free(line_tokens);
                            free(config_buffer);
                            return (-1);
                        } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) {
                            tmp_patch->filename = malloc(strlen(config_dir) + strlen(line_tokens[1]) + 5);
                            if (tmp_patch->filename == NULL) {
                                _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, 0);
                                WM_FreePatches();
                                free(config_dir);
                                free(line_tokens);
                                free(config_buffer);
                                return (-1);
                            }
                            strcpy(tmp_patch->filename, config_dir);
                            strcat(tmp_patch->filename, line_tokens[1]);
                        } else {
                            if (!(tmp_patch->filename = wm_strdup(line_tokens[1]))) {
                                _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, config_file, 0);
                                WM_FreePatches();
                                free(config_dir);
                                free(line_tokens);
                                free(config_buffer);
                                return (-1);
                            }
                        }
                        if (wm_strncasecmp(&tmp_patch->filename[strlen(tmp_patch->filename) - 4], ".pat", 4) != 0) {
                            strcat(tmp_patch->filename, ".pat");
                        }
                        tmp_patch->env[0].set = 0x00;
                        tmp_patch->env[1].set = 0x00;
                        tmp_patch->env[2].set = 0x00;
                        tmp_patch->env[3].set = 0x00;
                        tmp_patch->env[4].set = 0x00;
                        tmp_patch->env[5].set = 0x00;
                        tmp_patch->keep = 0;
                        tmp_patch->remove = 0;

                        token_count = 0;
                        while (line_tokens[token_count]) {
                            if (wm_strncasecmp(line_tokens[token_count], "amp=", 4) == 0) {
                                if (!wm_isdigit(line_tokens[token_count][4])) {
                                    _WM_DEBUG_MSG("%s: syntax error in patch line for %s", config_file, "amp=");
                                } else {
                                    tmp_patch->amp = (atoi(&line_tokens[token_count][4]) << 10) / 100;
                                }
                            } else if (wm_strncasecmp(line_tokens[token_count], "note=", 5) == 0) {
                                if (!wm_isdigit(line_tokens[token_count][5])) {
                                    _WM_DEBUG_MSG("%s: syntax error in patch line for %s", config_file, "note=");
                                } else {
                                    tmp_patch->note = atoi(&line_tokens[token_count][5]);
                                }
                            } else if (wm_strncasecmp(line_tokens[token_count], "env_time", 8) == 0) {
                                if ((!wm_isdigit(line_tokens[token_count][8])) ||
                                    (!wm_isdigit(line_tokens[token_count][10])) ||
                                    (line_tokens[token_count][9] != '=')) {
                                    _WM_DEBUG_MSG("%s: syntax error in patch line for %s", config_file, "env_time");
                                } else {
                                    uint32_t env_no = atoi(&line_tokens[token_count][8]);
                                    if (env_no > 5) {
                                        _WM_DEBUG_MSG("%s: syntax error in patch line for %s", config_file, "env_time");
                                    } else {
                                        tmp_patch->env[env_no].time = (float) atof(&line_tokens[token_count][10]);
                                        if ((tmp_patch->env[env_no].time > 45000.0f) ||
                                            (tmp_patch->env[env_no].time < 1.47f)) {
                                            _WM_DEBUG_MSG("%s: range error in patch line %s", config_file, "env_time");
                                            tmp_patch->env[env_no].set &= 0xFE;
                                        } else {
                                            tmp_patch->env[env_no].set |= 0x01;
                                        }
                                    }
                                }
                            } else if (wm_strncasecmp(line_tokens[token_count], "env_level", 9) == 0) {
                                if ((!wm_isdigit(line_tokens[token_count][9])) ||
                                    (!wm_isdigit(line_tokens[token_count][11])) ||
                                    (line_tokens[token_count][10] != '=')) {
                                    _WM_DEBUG_MSG("%s: syntax error in patch line for %s", config_file, "env_level");
                                } else {
                                    uint32_t env_no = atoi(&line_tokens[token_count][9]);
                                    if (env_no > 5) {
                                        _WM_DEBUG_MSG("%s: syntax error in patch line for %s", config_file, "env_level");
                                    } else {
                                        tmp_patch->env[env_no].level = (float) atof(&line_tokens[token_count][11]);
                                        if ((tmp_patch->env[env_no].level > 1.0f) ||
                                            (tmp_patch->env[env_no].level < 0.0f)) {
                                            _WM_DEBUG_MSG("%s: range error in patch line for %s", config_file, "env_level");
                                            tmp_patch->env[env_no].set &= 0xFD;
                                        } else {
                                            tmp_patch->env[env_no].set |= 0x02;
                                        }
                                    }
                                }
                            } else if (wm_strcasecmp(line_tokens[token_count], "keep=loop") == 0) {
                                tmp_patch->keep |= SAMPLE_LOOP;
                            } else if (wm_strcasecmp(line_tokens[token_count], "keep=env") == 0) {
                                tmp_patch->keep |= SAMPLE_ENVELOPE;
                            } else if (wm_strcasecmp(line_tokens[token_count], "remove=sustain") == 0) {
                                tmp_patch->remove |= SAMPLE_SUSTAIN;
                            } else if (wm_strcasecmp(line_tokens[token_count], "remove=clamped") == 0) {
                                tmp_patch->remove |= SAMPLE_CLAMPED;
                            }
                            token_count++;
                        }
                    }
                }
                else if (_WM_Global_ErrorI) { /* malloc() failure in WM_LC_Tokenize_Line() */
                    WM_FreePatches();
                    free(line_tokens);
                    free(config_buffer);
                    return (-1);
                }
                /* free up tokens */
                free(line_tokens);
            }
            line_start_ptr = config_ptr + 1;
        }
        config_ptr++;
    }

    free(config_buffer);
    free(config_dir);

    return (0);
}