Beispiel #1
0
void DBManager::SetToonStats(unsigned int charid, StatMap const& stats)
{
    assert(charid != 0);
    TRACE("Updating stats on character id " << charid);

    m_db->Begin();
    m_db->Exec(STREAM2STR("DELETE FROM tToonStats WHERE charid = " << charid));
    for (StatMap::const_iterator it = stats.begin(); it != stats.end(); ++it)
    {
        unsigned int statid = it->first;
        unsigned int statvalue = it->second;
        m_db->Exec(STREAM2STR("INSERT OR IGNORE INTO tToonStats VALUES (" << charid << ", " << statid << ", " << statvalue << ")"));
    }
    m_db->Commit();
}
Beispiel #2
0
    ITablePtr Db::ExecTable(std::string const& sql) const
    {
        TablePtr pRes;
        char** result;
        int nrow;
        int ncol;

        int err = sqlite3_get_table(m_pDb,
                                    sql.c_str(),
                                    &result,          /* Result written to a char *[]  that this points to */
                                    &nrow,            /* Number of result rows written here */
                                    &ncol,            /* Number of result columns written here */
                                    NULL);

        if (SQLITE_OK != err)
        {
            assert(false);
            m_log << "Db::ExecTable: Failed to execute query [" << sql << "]. Error: " << err << ", " <<
                sqlite3_errmsg(m_pDb) << std::endl;
            std::tstring msg = STREAM2STR("Query Failed with error code " << err);
            throw QueryFailedException(msg);
        }

        pRes = TablePtr(new Table(nrow, ncol, result));
        sqlite3_free_table(result);

        return pRes;
    }
Beispiel #3
0
void DBManager::SetToonDimension(unsigned int charid, unsigned int dimensionid)
{
    assert(charid != 0);
    assert(dimensionid != 0);
    m_db->Begin();
    m_db->Exec(STREAM2STR("UPDATE OR IGNORE tToons SET dimensionid = " << dimensionid << " WHERE charid = " << charid));
    m_db->Commit();
}
Beispiel #4
0
std::tstring ContainerManager::MakeContainerName(unsigned int charid, unsigned int containerid) const
{
    __int64 key = ((__int64)charid) << 32;
    key += containerid;

    // Return cached value if exist
    if (m_containerDBCache.find(key) != m_containerDBCache.end())
    {
        return m_containerDBCache[key];
    }

    std::tstring result;

    // Lets find the item description for the specified container.
    g_DBManager.Lock();
    sqlite::ITablePtr pT2 = m_db->ExecTable( STREAM2STR( "SELECT keylow from tItems WHERE children = " << containerid ));
    g_DBManager.UnLock();

    if (pT2 != NULL && pT2->Rows())
    {
        try {
            unsigned int keylow = boost::lexical_cast<unsigned int>(pT2->Data(0,0));
            std::map<std::tstring, std::tstring> item = GetAOItemInfo(keylow);
            result = item[_T("name")];
        }
        catch(boost::bad_lexical_cast &/*e*/) {
            assert(false); // This should not happen!
            result = STREAM2STR( "Backpack: " << containerid );
        }      
    }
    else
    {
        result = STREAM2STR( "Backpack: " << containerid );
    }

    m_containerDBCache[key] = result;

    return result;
}
Beispiel #5
0
std::tstring DBManager::GetToonName(unsigned int charid) const
{
    std::tstring result;

    sqlite::ITablePtr pT = m_db->ExecTable(STREAM2STR("SELECT charid, charname FROM tToons WHERE charid = " << charid));

    if (pT != NULL && pT->Rows())
    {
        if (!pT->Data(0, 1).empty())
        {
            result = from_ascii_copy(pT->Data(0, 1));
        }
    }

    return result;
}
Beispiel #6
0
unsigned int DBManager::GetShopOwner(unsigned int shopid)
{
    assert(shopid != 0);
    unsigned int result = 0;

    sqlite::ITablePtr pT = m_db->ExecTable(STREAM2STR("SELECT charid FROM tToons WHERE shopid = " << shopid));
    try
    {
        if (pT != NULL && pT->Rows())
        {
            result = boost::lexical_cast<unsigned int>(pT->Data(0, 0));
        }
    }
    catch (boost::bad_lexical_cast &/*e*/)
    {
    }

    return result;
}
Beispiel #7
0
void DBManager::DeleteItems( std::set<unsigned int> const& ids ) const
{
    if (ids.empty())
    {
        return;
    }

    std::list<std::tstring> idStrings;
    for (std::set<unsigned int>::const_iterator it = ids.begin(); it != ids.end(); ++it)
    {
        idStrings.push_back(STREAM2STR(*it));
    }
    std::tstring idList = boost::join(idStrings, _T(","));

    std::tstringstream sql;
    sql << _T("DELETE FROM tItems WHERE itemidx IN(") << idList << _T(")");

    m_db->Exec(sql.str());
}
Beispiel #8
0
void DBManager::createDBScheme() const
{
    LOG("DBManager::createDBScheme: Creating a new database scheme from scratch.");

    m_db->Begin();
    m_db->Exec("CREATE TABLE tItems (itemidx INTEGER NOT NULL PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT UNIQUE DEFAULT '1', keylow INTEGER, keyhigh INTEGER, ql INTEGER, flags INTEGER DEFAULT '0', stack INTEGER DEFAULT '1', parent INTEGER NOT NULL DEFAULT '2', slot INTEGER, children INTEGER, owner INTEGER NOT NULL)");
    m_db->Exec("CREATE INDEX idx_titems_keylow ON tItems (keylow ASC)");
    m_db->Exec("CREATE INDEX idx_titems_owner ON tItems (owner)");
    m_db->Exec("CREATE INDEX idx_titems_parent ON tItems (parent)");
    m_db->Exec("CREATE VIEW vBankItems AS SELECT * FROM tItems WHERE parent=1");
    m_db->Exec("CREATE VIEW vContainers AS SELECT * FROM tItems WHERE children > 0");
    m_db->Exec("CREATE VIEW vInvItems AS SELECT * FROM tItems WHERE parent=2");
    m_db->Exec("CREATE TABLE tToons (charid INTEGER NOT NULL PRIMARY KEY UNIQUE, charname VARCHAR, shopid INTEGER DEFAULT '0', dimensionid INTEGER DEFAULT '0')");
    m_db->Exec("CREATE UNIQUE INDEX iCharId ON tToons (charid)");
    m_db->Exec("CREATE TABLE tDimensions (dimensionid INTEGER NOT NULL PRIMARY KEY UNIQUE, dimensionname VARCHAR)");
    m_db->Exec("INSERT INTO tDimensions (dimensionid, dimensionname) VALUES (13, 'Rubi-Ka')");
    m_db->Exec("INSERT INTO tDimensions (dimensionid, dimensionname) VALUES (15, 'TestLive')");
    m_db->Exec("CREATE TABLE tToonStats (charid INTEGER NOT NULL, statid INTEGER NOT NULL, statvalue INTEGER NOT NULL, FOREIGN KEY (charid) REFERENCES tToons (charid))");
    m_db->Exec("CREATE UNIQUE INDEX charstatindex ON tToonStats (charid ASC, statid ASC)");
    m_db->Exec(STREAM2STR(_T("CREATE VIEW vSchemeVersion AS SELECT '") << CURRENT_DB_VERSION << _T("' AS Version")));
    m_db->Commit();
}
Beispiel #9
0
unsigned int DBManager::GetToonDimension(unsigned int charid) const
{
    assert(charid != 0);

    unsigned int result = 0;

    sqlite::ITablePtr pT = m_db->ExecTable(STREAM2STR("SELECT dimensionid FROM tToons WHERE charid = " << charid));
    if (pT != NULL && pT->Rows())
    {
        try
        {
            result = boost::lexical_cast<unsigned int>(pT->Data(0,0));
        }
        catch (boost::bad_lexical_cast &/*e*/)
        {
            LOG("Error in getToonDimension(). Bad lexical cast.");
            // Wierd.. lets debug!
            assert(false);
        }
    }

    return result;
}
Beispiel #10
0
unsigned int DBManager::FindNextAvailableContainerSlot(unsigned int charId, unsigned int containerId)
{
    assert(charId != 0);
    assert(containerId != 0);

    unsigned short posSlot = 0;

    if (containerId == 2)
        posSlot = 64; //start at slot 64 for inventory!

    sqlite::ITablePtr pT = m_db->ExecTable(STREAM2STR("SELECT slot FROM tItems WHERE parent = " << containerId << " AND slot >= " << posSlot
                                           << " AND owner = " << charId << " ORDER by slot"));

    if (pT == NULL || !pT->Rows())
        return 0; //empty backpack

    unsigned int count = pT->Rows();

    for (unsigned i=0; i<count; i++)
    {
        try
        {
            if (posSlot < boost::lexical_cast<unsigned short>(pT->Data(i,0)))
            {
                return posSlot; //we found a free slot in-between
            }
        }
        catch (boost::bad_lexical_cast &/*e*/)
        {
            //return 0xff; //Can't really imagine this to be possible
        }

        posSlot++;
    }

    return posSlot; //we return the next available slot
}
Beispiel #11
0
bool DBManager::GetDimensions(std::map<unsigned int, std::tstring> &dimensions) const
{
    sqlite::ITablePtr pT = m_db->ExecTable(STREAM2STR("SELECT dimensionid, dimensionname FROM tDimensions"));

    if (pT != NULL && pT->Rows())
    {
        for (unsigned int i = 0; i < pT->Rows(); ++i)
        {
            try
            {
                std::tstring name = from_ascii_copy(pT->Data(i, 1));
                dimensions[boost::lexical_cast<unsigned int>(pT->Data(i, 0))] = name;
            }
            catch (boost::bad_lexical_cast &/*e*/)
            {
                LOG("Error in getDimensions(). Bad lexical cast.");
                // Wierd.. lets debug!
                assert(false);
            }
        }
        return true;
    }
    return false;
}
Beispiel #12
0
std::tstring ContainerManager::GetContainerName(unsigned int charid, unsigned int containerid) const
{
    if (containerid == 1) {
        return _T("Bank");
    }
    else if (containerid == 2) {
        return _T("Inventory/Equip");
    }
    else if (containerid == 3) {
        return _T("Shop");
    }

    std::tstring result;

    __int64 key = ((__int64)charid) << 32;
    key += containerid;

    FILETIME lastWrite;
    lastWrite.dwHighDateTime = lastWrite.dwLowDateTime = 0;

    std::tstring filename;
    for (unsigned int i = 0; i < m_accounts.size(); i++)
    {
        filename = STREAM2STR( AOManager::instance().getAOPrefsFolder() << _T("\\") << m_accounts[i] << _T("\\Char") << charid << _T("\\Containers\\Container_51017x") << containerid << _T(".xml") );
        if (PathFileExists(filename.c_str()))
        {
            WIN32_FILE_ATTRIBUTE_DATA atribs; 
            if (GetFileAttributesEx(filename.c_str(), GetFileExInfoStandard, &atribs))
            {
                lastWrite = atribs.ftLastWriteTime;
                break;
            }
        }
    }

    bool wasInCache = m_containerFileCache.find(key) != m_containerFileCache.end();

    // Clear invalid cache
    if ((filename.empty() || (lastWrite.dwHighDateTime == 0 && lastWrite.dwHighDateTime == 0)) && wasInCache)
    {
        m_containerFileCache.erase(m_containerFileCache.find(key));
    }

    // Create cache from file
    if (!filename.empty())
    {
        bool update = true;

        // If already in cache, check timestamps
        if (m_containerFileCache.find(key) != m_containerFileCache.end())
        {
            FILETIME stamp = m_containerFileCache[key].second;
            if (stamp.dwHighDateTime == lastWrite.dwHighDateTime &&
                stamp.dwLowDateTime == lastWrite.dwLowDateTime)
            {
                update = false;
                result = m_containerFileCache[key].first;
            }
        }

        if (update)
        {
            TiXmlDocument document;
            if (document.LoadFile(to_ascii_copy(filename), TIXML_ENCODING_UTF8))
            {
                TiXmlHandle docHandle( &document );
                TiXmlElement* element = docHandle.FirstChild( "Archive" ).FirstChild( "String" ).Element();

                while (element)
                {
                    if (StrCmpA(element->Attribute("name"), "container_name") == 0)
                    {
                        result = from_utf8_copy(element->Attribute("value"));
                        boost::algorithm::replace_all(result, _T("&amp;"), _T("&"));    // Fixes wierd encoding in the AO xml.
                        m_containerFileCache[key] = std::pair<std::tstring, FILETIME>(result, lastWrite);
                        break;
                    }
                    element = element->NextSiblingElement();
                }
            }
        }
    }

    if (result.empty())
    {
        result = MakeContainerName(charid, containerid);
    }

    return result;
}
Beispiel #13
0
/**
* Check to see if we have a local database already.
* If it is, then check if it is up to date.
* If local database is missing or obsolete then recreate it.
* Return true if application has a local items database to run with, false otherwise.
*/
bool DBManager::syncLocalItemsDB(std::tstring const& localfile, std::tstring const& aofolder)
{
    bool hasLocalDB = false;
    std::time_t lastUpdateTime;

    bfs::path local(to_ascii_copy (localfile));
    bfs::path original(to_ascii_copy (aofolder));
    original = original / "cd_image/data/db/ResourceDatabase.dat";

    if (bfs::exists(local) && bfs::is_regular(local))
    {
        hasLocalDB = true;
        lastUpdateTime = bfs::last_write_time(local);
    }

    if (!exists(original))
    {
        Logger::instance().log(_T("Could not locate the original AO database."));
        return hasLocalDB;
    }

    if (hasLocalDB && getAODBSchemeVersion(localfile) == CURRENT_AODB_VERSION)
    {
        std::time_t lastOriginalUpdateTime = bfs::last_write_time(original);
        if (lastOriginalUpdateTime <= lastUpdateTime)
        {
            return true;
        }

        // Ask user if he wants to continue using the old (but compatible) DB or update it now.
        int answer = ::MessageBox(NULL,
                                  _T("You items database is out of date. Do you wish to update it now?\r\nAnswering 'NO' will continue using the old one."),
                                  _T("Question - AO Item Assistant++"), MB_ICONQUESTION | MB_YESNOCANCEL);
        if (answer == IDCANCEL)
        {
            exit(0);
        }
        else if (answer == IDNO)
        {
            return true;
        }
    }

    // If we come this far we need to update the DB.

    bfs::path tmpfile("tmp_" + local.string());
    bfs::remove(tmpfile);

    try
    {
        std::set<ResourceType> resource_types = boost::assign::list_of(AODB_TYP_ITEM)(AODB_TYP_NANO);
        AODatabaseIndex indexer(to_ascii_copy(aofolder) + "/cd_image/data/db/ResourceDatabase.idx", resource_types);
        std::vector<unsigned int> item_offsets = indexer.GetOffsets(AODB_TYP_ITEM);
        std::vector<unsigned int> nano_offsets = indexer.GetOffsets(AODB_TYP_NANO);
        unsigned int itemCount = item_offsets.size();
        unsigned int nanoCount = nano_offsets.size();

        std::vector<std::string> original_files = boost::assign::list_of(original.string())(original.string()+".001")(original.string()+".002");
        AODatabaseParser aodb(original_files);
        AODatabaseWriter writer(tmpfile.string(), Logger::instance().stream());

        CProgressDialog dlg(itemCount + nanoCount, itemCount);
        dlg.SetWindowText(_T("Progress Dialog - Item Assistant"));
        dlg.setText(0, _T("Extracting data from the AO DB..."));
        dlg.setText(1, STREAM2STR("Finished " << 0 << " out of " << itemCount << " items."));
        dlg.setText(2, _T("Overall progress: 0%"));

        // Extract items
        boost::shared_ptr<ao_item> item;
        unsigned int count = 0;
        writer.BeginWrite();
        for (std::vector<unsigned int>::iterator item_it = item_offsets.begin(); item_it != item_offsets.end(); ++item_it)
        {
            item = aodb.GetItem(*item_it);
            count++;
            if (!item)
            {
                LOG(_T("Parsing item ") << count << _T(" at offset ") << *item_it << _T(" failed!"));
                continue;
            }
            writer.WriteItem(item);
            if (count % 1000 == 0) {
                if (dlg.userCanceled()) {
                    return false;
                }
                dlg.setTaskProgress(count, itemCount);
                dlg.setText(1, STREAM2STR("Finished " << count << " out of " << itemCount << " items."));
                dlg.setOverallProgress(count, itemCount + nanoCount);
                dlg.setText(2, STREAM2STR("Overall progress: " << (count * 100) / max(1, itemCount + nanoCount) << "%"));
            }
            if (count % 10000 == 0) {
                writer.CommitItems();
                writer.BeginWrite();
            }
        }
        item.reset();
        dlg.setTaskProgress(count, itemCount);
        dlg.setText(1, STREAM2STR("Finished " << count << " out of " << itemCount << " items."));
        dlg.setOverallProgress(count, itemCount + nanoCount);
        dlg.setText(2, STREAM2STR("Overall progress: " << (count * 100) / max(1, itemCount + nanoCount) << "%"));
        writer.CommitItems();

        if (dlg.userCanceled())
        {
            return false;
        }

        // Extract nano programs
        boost::shared_ptr<ao_item> nano;
        count = 0;
        writer.BeginWrite();
        for (std::vector<unsigned int>::iterator nano_it = nano_offsets.begin(); nano_it != nano_offsets.end(); ++nano_it)
        {
            nano = aodb.GetItem(*nano_it);
            count++;
            if (!nano)
            {
                LOG(_T("Parsing nano ") << count << _T(" at offset ") << *nano_it << _T(" failed!"));
                continue;
            }
            writer.WriteItem(nano);
            if (count % 1000 == 0)
            {
                if (dlg.userCanceled())
                {
                    return false;
                }
                dlg.setTaskProgress(count, nanoCount);
                dlg.setText(1, STREAM2STR("Finished " << count << " out of " << nanoCount << " nanos."));
                dlg.setOverallProgress(itemCount + count, itemCount + nanoCount);
                dlg.setText(2, STREAM2STR("Overall progress: " << ((itemCount + count) * 100) / max(1, itemCount +
                                          nanoCount) << "%"));
            }
            if (count % 10000 == 0)
            {
                writer.CommitItems();
                writer.BeginWrite();
            }
        }
        nano.reset();
        dlg.setTaskProgress(count, nanoCount);
        dlg.setText(1, STREAM2STR("Finished " << count << " out of " << nanoCount << " nanos."));
        dlg.setOverallProgress(itemCount + count, itemCount + nanoCount);
        dlg.setText(2, STREAM2STR("Overall progress: " << ((itemCount + count) * 100) / max(1, itemCount + nanoCount) << "%"));
        writer.CommitItems();

        if (dlg.userCanceled())
        {
            return false;
        }

        writer.PostProcessData();
    }
    catch (std::bad_alloc& e)
    {
        assert(false);
        LOG(_T("Error creating item database. ") << e.what());
        MessageBox(NULL,
                   _T("Unable to parse the AO database.\n\rMore details might be found in the log-file (if enabled)."),
                   _T("Error - AO Item Assistant++"), MB_OK | MB_ICONERROR);
        return false;
    }
    catch (AODatabaseParser::Exception& e)
    {
        assert(false);
        LOG(_T("Error creating item database. ") << e.what());
        MessageBox(NULL,
                   _T("Unable to parse the AO database.\n\rMore details might be found in the log-file (if enabled)."),
                   _T("Error - AO Item Assistant++"), MB_OK | MB_ICONERROR);
        return false;
    }
    catch (std::exception& e)
    {
        assert(false);
        LOG(_T("Error creating item database. ") << e.what());
        MessageBox(NULL,
                   _T("Unable to parse the AO database.\n\rMore details might be found in the log-file (if enabled)."),
                   _T("Error - AO Item Assistant++"), MB_OK | MB_ICONERROR);
        return false;
    }

    remove(local);
    rename(tmpfile, local);

    return true;
}
Beispiel #14
0
void DBManager::updateDBVersion(unsigned int fromVersion) const
{
    switch (fromVersion)
    {
    case 0:
    {
        m_db->Begin();
        m_db->Exec(_T("CREATE TABLE tToons2 (charid, charname)"));
        m_db->Exec(_T("INSERT INTO tToons2 (charid, charname) SELECT charid, charname FROM tToons"));
        m_db->Exec(_T("DROP TABLE tToons"));
        m_db->Exec(_T("CREATE TABLE tToons (charid, charname)"));
        m_db->Exec(_T("CREATE UNIQUE INDEX iCharId ON tToons (charid)"));
        m_db->Exec(_T("INSERT INTO tToons (charid, charname) SELECT charid, charname FROM tToons2"));
        m_db->Exec(_T("DROP TABLE tToons2"));
        m_db->Commit();
    }
    // Dropthrough

    case 1:
    {
        m_db->Begin();
        m_db->Exec(_T("CREATE TABLE tToons2 (charid INTEGER NOT NULL PRIMARY KEY UNIQUE, charname VARCHAR)"));
        m_db->Exec(_T("INSERT INTO tToons2 (charid, charname) SELECT charid, charname FROM tToons"));
        m_db->Exec(_T("DROP TABLE tToons"));
        m_db->Exec(_T("CREATE TABLE tToons (charid INTEGER NOT NULL PRIMARY KEY UNIQUE, charname VARCHAR)"));
        m_db->Exec(_T("INSERT INTO tToons (charid, charname) SELECT charid, charname FROM tToons2"));
        m_db->Exec(_T("DROP TABLE tToons2"));
        m_db->Exec(_T("CREATE UNIQUE INDEX iCharId ON tToons (charid)"));
        m_db->Commit();
    }
    // Dropthrough

    case 2: // Update from v2 is the added shopid column in the toons table
    {
        m_db->Begin();
        m_db->Exec(_T("CREATE TABLE tToons2 (charid INTEGER NOT NULL PRIMARY KEY UNIQUE, charname VARCHAR, shopid INTEGER DEFAULT '0')"));
        m_db->Exec(_T("INSERT INTO tToons2 (charid, charname) SELECT charid, charname FROM tToons"));
        m_db->Exec(_T("DROP TABLE tToons"));
        m_db->Exec(_T("CREATE TABLE tToons (charid INTEGER NOT NULL PRIMARY KEY UNIQUE, charname VARCHAR, shopid INTEGER DEFAULT '0')"));
        m_db->Exec(_T("INSERT INTO tToons (charid, charname) SELECT charid, charname FROM tToons2"));
        m_db->Exec(_T("DROP TABLE tToons2"));
        m_db->Exec(_T("CREATE UNIQUE INDEX iCharId ON tToons (charid)"));
        m_db->Commit();
    }
    // Dropthrough

    case 3: // Update from v3 is the added flags column in tItems as well as the new dimension table and a new dimensionid column in tToons.
    {
        m_db->Begin();
        m_db->Exec(_T("CREATE TABLE tItems2 (itemidx INTEGER NOT NULL PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT UNIQUE DEFAULT '1', keylow INTEGER, keyhigh INTEGER, ql INTEGER, flags INTEGER DEFAULT '0', stack INTEGER DEFAULT '1', parent INTEGER NOT NULL DEFAULT '2', slot INTEGER, children INTEGER, owner INTEGER NOT NULL)"));
        m_db->Exec(_T("INSERT INTO tItems2 (itemidx, keylow, keyhigh, ql, stack, parent, slot, children, owner) SELECT itemidx, keylow, keyhigh, ql, stack, parent, slot, children, owner FROM tItems"));
        m_db->Exec(_T("DROP TABLE tItems"));
        m_db->Exec(_T("CREATE TABLE tItems (itemidx INTEGER NOT NULL PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT UNIQUE DEFAULT '1', keylow INTEGER, keyhigh INTEGER, ql INTEGER, flags INTEGER DEFAULT '0', stack INTEGER DEFAULT '1', parent INTEGER NOT NULL DEFAULT '2', slot INTEGER, children INTEGER, owner INTEGER NOT NULL)"));
        m_db->Exec(_T("INSERT INTO tItems (itemidx, keylow, keyhigh, ql, stack, parent, slot, children, owner) SELECT itemidx, keylow, keyhigh, ql, stack, parent, slot, children, owner FROM tItems2"));
        m_db->Exec(_T("DROP TABLE tItems2"));
        m_db->Exec(_T("CREATE TABLE tDimensions (dimensionid INTEGER NOT NULL PRIMARY KEY UNIQUE, dimensionname VARCHAR)"));
        m_db->Exec(_T("INSERT INTO tDimensions (dimensionid, dimensionname) VALUES (13, 'Rubi-Ka')"));
        m_db->Exec(_T("INSERT INTO tDimensions (dimensionid, dimensionname) VALUES (15, 'TestLive')"));
        m_db->Exec(_T("CREATE TABLE tToons2 (charid INTEGER NOT NULL PRIMARY KEY UNIQUE, charname VARCHAR, shopid INTEGER DEFAULT '0', dimensionid INTEGER DEFAULT '0')"));
        m_db->Exec(_T("INSERT INTO tToons2 (charid, charname, shopid) SELECT charid, charname, shopid FROM tToons"));
        m_db->Exec(_T("DROP TABLE tToons"));
        m_db->Exec(_T("CREATE TABLE tToons (charid INTEGER NOT NULL PRIMARY KEY UNIQUE, charname VARCHAR, shopid INTEGER DEFAULT '0', dimensionid INTEGER DEFAULT '0')"));
        m_db->Exec(_T("INSERT INTO tToons (charid, charname, shopid) SELECT charid, charname, shopid FROM tToons2"));
        m_db->Exec(_T("DROP TABLE tToons2"));
        m_db->Exec(_T("CREATE UNIQUE INDEX iCharId ON tToons (charid)"));
        m_db->Commit();
    }
    // Dropthrough

    case 4: // Updates from v4: Added toon-stats table.
    {
        m_db->Begin();
        m_db->Exec(_T("CREATE TABLE tToonStats (charid INTEGER NOT NULL, statid INTEGER NOT NULL, statvalue INTEGER NOT NULL, FOREIGN KEY (charid) REFERENCES tToons (charid))"));
        m_db->Exec(_T("CREATE UNIQUE INDEX charstatindex ON tToonStats (charid ASC, statid ASC)"));
        m_db->Commit();
    }
    // Dropthrough

    case 5: // Updates from v5: Added index to tItems
    {
        m_db->Begin();
        m_db->Exec("CREATE INDEX idx_titems_keylow ON tItems (keylow ASC)");
        m_db->Commit();
    }
    // Dropthrough

    case 6: // Updates from v6: Redid missing indexes from previous updates.
    {
        m_db->Begin();
        m_db->Exec("DROP INDEX IF EXISTS iOwner");
        m_db->Exec("DROP INDEX IF EXISTS iParent");
        m_db->Exec("CREATE INDEX idx_titems_owner ON tItems (owner)");
        m_db->Exec("CREATE INDEX idx_titems_parent ON tItems (parent)");
        m_db->Commit();
    }
    // Dropthrough

    case 7: // Updates from v7: Include 'Account' in tToons
    {
        m_db->Begin();
        m_db->Exec("DELETE * FROM tDimensions");
        m_db->Exec("INSERT INTO tDimensions (dimensionid, dimensionname) VALUES (13, 'Rubi-Ka')");
        m_db->Exec("INSERT INTO tDimensions (dimensionid, dimensionname) VALUES (15, 'TestLive')");
        m_db->Commit();
    }
    // Dropthrough

    default:
        m_db->Exec("DROP VIEW IF EXISTS vSchemeVersion");
        m_db->Exec(STREAM2STR(_T("CREATE VIEW vSchemeVersion AS SELECT '") << CURRENT_DB_VERSION << _T("' AS Version")));
        break;
    }
}