void HeroChart::setHero(int hero)
{
    if (dota)
    {
        Dota::Hero* h = dota->getHero(hero);
        if (h->tavern >= 0 && h->tavern < numTaverns)
        {
            curTavern = h->tavern;
            notify(WM_SETTAVERN, h->tavern, 0);
            notify(WM_SETHERO, hero, 0);
            InvalidateRect(hWnd, NULL, true);
        }
    }
}
Application::Application(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MPQInit();
    instance = this;
    resources = NULL;
    imageLibrary = NULL;
    dotaLibrary = NULL;
    mainWindow = NULL;
    cache = NULL;
    hInstance = _hInstance;
    _loaded = false;

    root = String::getPath(getAppPath());
    cfg.read();

    warLoader = new MPQLoader("Custom_V1");
    warLoader->loadArchive(String::buildFullName(cfg.warPath, "war3.mpq"));
    warLoader->loadArchive(String::buildFullName(cfg.warPath, "war3x.mpq"));
    warLoader->loadArchive(String::buildFullName(cfg.warPath, "war3xlocal.mpq"));
    warLoader->loadArchive(String::buildFullName(cfg.warPath, "war3patch.mpq"));

    if (logCommand(lpCmdLine))
        return;

    ScriptType::initTypes();
    UpdateDialog::init(hInstance);

    INITCOMMONCONTROLSEX iccex;
    iccex.dwSize = sizeof iccex;
    iccex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS |
                  ICC_BAR_CLASSES | ICC_TREEVIEW_CLASSES | ICC_LISTVIEW_CLASSES |
                  ICC_TAB_CLASSES | ICC_UPDOWN_CLASS | ICC_DATE_CLASSES;
    InitCommonControlsEx(&iccex);
    LoadLibrary("Riched20.dll");
    OleInitialize(NULL);

    String path = String::getPath(getAppPath());
    String resPath = String::buildFullName(path, "resources.mpq");
    String patchPath = String::buildFullName(path, "install.mpq");
    File* tOpen = File::open(resPath, File::READ);
    if (tOpen == NULL)
    {
        tOpen = File::open(patchPath, File::READ);
        if (tOpen)
        {
            delete tOpen;
            MoveFile(patchPath, resPath);
        }
    }
    else
        delete tOpen;
    resources = MPQArchive::open(resPath);
    MPQArchive* patch = MPQArchive::open(patchPath, File::READ);
    if (patch)
    {
        for (uint32 i = 0; i < patch->getHashSize(); i++)
        {
            char const* name = patch->getFileName(i);
            if (name)
            {
                MPQFile* source = patch->openFile(i, File::READ);
                if (source)
                {
                    MPQFile* dest = resources->openFile(name, File::REWRITE);
                    if (dest)
                    {
                        static uint8 buf[1024];
                        while (int length = source->read(buf, sizeof buf))
                            dest->write(buf, length);
                        delete dest;
                    }
                    delete source;
                }
            }
        }
        delete patch;
        DeleteFile(patchPath);
    }

    imageLibrary = new ImageLibrary(resources);

    cache = new CacheManager();
    dotaLibrary = new DotaLibrary();

#if 0
    File* dlog = File::open("diff.txt", File::REWRITE);
    for (int pt = 0; pt < 120; pt++)
    {
        String prev = "";
        bool different = false;
        for (int ver = 1; ver <= 80 && !different; ver++)
        {
            Dota* dota = dotaLibrary->getDota(makeVersion(6, ver));
            if (dota)
            {
                Dota::Hero* hero = dota->getHero(pt);
                if (hero)
                {
                    if (prev == "")
                        prev = hero->name;
                    else if (prev.icompare(hero->name))
                        different = true;
                }
            }
        }
        if (different)
        {
            dlog->printf("  Pt=%d\r\n", pt);
            prev = "";
            for (int ver = 1; ver <= 80; ver++)
            {
                Dota* dota = dotaLibrary->getDota(makeVersion(6, ver));
                if (dota)
                {
                    Dota::Hero* hero = dota->getHero(pt);
                    if (hero)
                    {
                        if (prev.icompare(hero->name))
                        {
                            dlog->printf("6.%02d = %s\r\n", ver, hero->name);
                            prev = hero->name;
                        }
                    }
                }
            }
        }
    }
    delete dlog;
#endif
#if 0
    dotaLibrary->getDota(parseVersion("6.79e"),
                         "K:\\Progs\\DotAReplay\\maps\\DotA v6.79e.w3x");
    WIN32_FIND_DATA data;
    String enumPath = "K:\\Progs\\DotAReplay\\maps";
    HANDLE hFind = FindFirstFile(String::buildFullName(enumPath, "*"), &data);
    BOOL success = (hFind != INVALID_HANDLE_VALUE);
    while (success)
    {
        String file(data.cFileName);
        if (String::getExtension(file).icompare(".w3x") == 0)
        {
            file.toLower();
            Array<String> sub;
            if (file.rfind("dota{{_| }allstars}?{_| }v(\\d)\\.(\\d\\d)([b-z]?)[^b-z]", 0, &sub) >= 0)
            {
                int major = sub[1].toInt();
                int minor = sub[2].toInt();
                int build = 0;
                if (!sub[3].isEmpty())
                    build = int(sub[3][0] - 'a');
                uint32 version = makeVersion(major, minor, build);

                dotaLibrary->getDota(version, String::buildFullName(enumPath, file));
            }
        }
        success = FindNextFile(hFind, &data);
    }
    FindClose(hFind);
#endif

    mainWindow = new MainWnd();

    mainWindow->postLoad();
    _loaded = true;

    if (lpCmdLine[0])
    {
        COPYDATASTRUCT cd;
        cd.dwData = MAINWND_OPEN_REPLAY;
        cd.cbData = strlen(lpCmdLine) + 1;
        cd.lpData = lpCmdLine;
        PostMessage(getMainWindow(), WM_COPYDATA_FAKE, NULL, (LPARAM) &cd);
    }
}
uint32 HeroChartFrame::onMessage(uint32 message, uint32 wParam, uint32 lParam)
{
    switch (message)
    {
    case WM_NOTIFY:
    {
        NMHDR* pnm = (NMHDR*) lParam;
        if (pnm->hwndFrom == heroes->getHandle() && pnm->code == LVN_ITEMCHANGED)
        {
            int sel = ListView_GetNextItem(heroes->getHandle(), -1, LVNI_SELECTED);
            if (sel >= 0)
            {
                files.clear();
                games->clear();
                EnumData ed;
                ed.hero = heroes->getItemParam(sel);
                ed.files = &files;
                ed.list = games;
                getApp()->getCache()->enumCache(cacheEnumerator, &ed);
                for (int i = 0; i < 5; i++)
                    games->setColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
            }
        }
        else if (pnm->hwndFrom == games->getHandle() && pnm->code == LVN_ITEMACTIVATE)
        {
            int sel = ListView_GetNextItem(games->getHandle(), -1, LVNI_SELECTED);
            if (sel >= 0 && sel < files.length())
                SendMessage(getApp()->getMainWindow(), WM_PUSHVIEW,
                            (WPARAM) new ReplayViewItem(files[sel]), 0);
        }
    }
    break;
    case WM_SETTAVERN:
    {
        heroes->clear();
        Dota* dota = chart->getDota();
        for (int i = 0; i < MAX_HERO_POINT; i++)
        {
            Dota::Hero* hero = dota->getHero(i);
            if (hero && hero->tavern == wParam)
            {
                int pos = heroes->addItem(hero->name,
                                          getApp()->getImageLibrary()->getListIndex(hero->icon), hero->point);
                heroes->setItemText(pos, 1, String(chart->getHeroCount(i)));
            }
        }
        for (int i = 0; i < 2; i++)
            heroes->setColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
    }
    break;
    case WM_SETHERO:
        for (int i = 0; i < heroes->getCount(); i++)
        {
            if (heroes->getItemParam(i) == wParam)
            {
                ListView_SetItemState(heroes->getHandle(), i, LVIS_SELECTED, LVIS_SELECTED);
                break;
            }
        }
        if (viewItem)
            viewItem->setHero(wParam);
        break;
    default:
        return M_UNHANDLED;
    }
    return 0;
}
void HeroChart::rebuild()
{
    if (dota)
        dota->release();
    delete[] taverns;
    delete[] textTags;

    DotaLibrary* lib = getApp()->getDotaLibrary();
    dota = lib->getDota();
    numTaverns = 12;
    numTags = 8;
    taverns = new TavernInfo[numTaverns];
    textTags = new TextTag[numTags];

    HFONT hFontLarge = FontSys::getFont(18, "Georgia", FONT_BOLD);
    HFONT hFontMed = FontSys::getFont(16, "Georgia", FONT_BOLD);
    counterFont = FontSys::getFont(10, "Arial");

    textTags[0].set("Sentinel", getSlotColor(0), hFontLarge, 0, 0, CHART_WIDTH, TEAM_HEIGHT);
    textTags[1].set("Agility", getSlotColor(0), hFontMed, 0, textTags[0].rc.bottom, CHART_WIDTH, NAME_HEIGHT);
    textTags[2].set("Strength", getSlotColor(0), hFontMed,
                    0, textTags[1].rc.bottom + TAVERN_HEIGHT, CHART_WIDTH, NAME_HEIGHT);
    textTags[3].set("Intelligence", getSlotColor(0), hFontMed,
                    0, textTags[2].rc.bottom + TAVERN_HEIGHT, CHART_WIDTH, NAME_HEIGHT);
    textTags[4].set("Scourge", getSlotColor(6), hFontLarge,
                    0, textTags[3].rc.bottom + TAVERN_HEIGHT, CHART_WIDTH, TEAM_HEIGHT);
    textTags[5].set("Agility", getSlotColor(6), hFontMed,
                    0, textTags[4].rc.bottom, CHART_WIDTH, NAME_HEIGHT);
    textTags[6].set("Strength", getSlotColor(6), hFontMed,
                    0, textTags[5].rc.bottom + TAVERN_HEIGHT, CHART_WIDTH, NAME_HEIGHT);
    textTags[7].set("Intelligence", getSlotColor(6), hFontMed,
                    0, textTags[6].rc.bottom + TAVERN_HEIGHT, CHART_WIDTH, NAME_HEIGHT);

    for (int i = 0; i < 12; i++)
    {
        taverns[i].rc.left = 10;
        if (i % 2)
            taverns[i].rc.left += (32 * 4 + 2 * 3 + 20);
        taverns[i].rc.top = textTags[1 + (i / 6) + (i / 2)].rc.bottom;
        taverns[i].rc.right = taverns[i].rc.left + 32 * 4 + 2 * 3;
        taverns[i].color = getSlotColor((i / 6) * 6);
        for (int h = 0; h < 12; h++)
            taverns[i].heroes[h] = NULL;
        int maxY = 0;
        for (int h = 0; h < MAX_HERO_POINT; h++)
        {
            Dota::Hero* hero = dota->getHero(h);
            if (hero && hero->tavern == i)
            {
                taverns[i].heroes[hero->tavernSlot] = hero;
                int y = hero->tavernSlot / 4;
                if (y > maxY)
                    maxY = y;
            }
        }
        taverns[i].rc.bottom = taverns[i].rc.top + 32 + maxY * (32 + 2);
    }
    recount();

    contentHeight = textTags[numTags - 1].rc.bottom + TAVERN_HEIGHT + 15;
    onMessage(WM_SIZE, 0, 0);
}