// vim: set ts=4 sw=4 tw=99 noet: // // AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). // Copyright (C) The AMX Mod X Development Team. // // This software is licensed under the GNU General Public License, version 3 or higher. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: // https://alliedmods.net/amxmodx-license // // NVault Module // #include #include "amxxmodule.h" #include "NVault.h" #include "Binary.h" #include /** * :TODO: This beast calls strcpy()/new() way too much by creating new strings on the stack. * That's easily remedied and it should be fixed? * ---bail */ NVault::NVault(const char *file) { m_File = file; m_Journal = NULL; m_Open = false; FILE *fp = fopen(m_File.chars(), "rb"); if (!fp) { fp = fopen(m_File.chars(), "wb"); if (!fp) { this->m_Valid = false; return; } } this->m_Valid = true; fclose(fp); } NVault::~NVault() { Close(); } VaultError NVault::_ReadFromFile() { FILE *fp = fopen(m_File.chars(), "rb"); if (!fp) { return Vault_NoFile; } //this is a little more optimized than the other version in the journal <_< //I could optimize this more by embedding the position in the hash table but... // the hash function can be changed. this could be fixed by storing a string and its // resulting hash, and the entries could be ignored therein, but not right now. //Also the string class could have embedded binary reading (like it does for text files) BinaryReader br(fp); uint8_t oldkeylen=0; uint16_t oldvallen=0; uint8_t keylen; uint16_t vallen; time_t stamp; char *key = NULL; char *val = NULL; // try // { uint32_t magic; if (!br.ReadUInt32(magic)) goto fail; if (magic != VAULT_MAGIC) return Vault_BadFile; uint16_t version; if (!br.ReadUInt16(version)) goto fail; if (version != VAULT_VERSION) return Vault_OldFile; int32_t entries; if (!br.ReadInt32(entries)) goto fail; int32_t temp; for (int32_t i=0; i(temp); if (!br.ReadUInt8(keylen)) goto fail; if (!br.ReadUInt16(vallen)) goto fail; if (!keylen || keylen > oldkeylen) { if (key) delete [] key; key = new char[keylen + 1]; oldkeylen = keylen; } if (!vallen || vallen > oldvallen) { if (val) delete [] val; val = new char[vallen + 1]; oldvallen = vallen; } if (!br.ReadChars(key, keylen)) goto fail; if (!br.ReadChars(val, vallen)) goto fail; key[keylen] = '\0'; val[vallen] = '\0'; ArrayInfo info; info.value = val; info.stamp = stamp; m_Hash.replace(key, info); } // } catch (...) { goto success; fail: if (key) { delete [] key; key = NULL; } if (val) { delete [] val; val = NULL; } fclose(fp); return Vault_Read; // } success: fclose(fp); return Vault_Ok; } bool NVault::_SaveToFile() { FILE *fp = fopen(m_File.chars(), "wb"); if (!fp) { return false; } BinaryWriter bw(fp); // try // { uint32_t magic = VAULT_MAGIC; uint16_t version = VAULT_VERSION; time_t stamp; ke::AString key; ke::AString val; StringHashMap::iterator iter = m_Hash.iter(); if (!bw.WriteUInt32(magic)) goto fail; if (!bw.WriteUInt16(version)) goto fail; if (!bw.WriteUInt32( m_Hash.elements() )) goto fail; while (!iter.empty()) { key = (*iter).key; val = (*iter).value.value; stamp = (*iter).value.stamp; if (!bw.WriteInt32(static_cast(stamp))) goto fail;; if (!bw.WriteUInt8( key.length() )) goto fail; if (!bw.WriteUInt16( val.length() )) goto fail; if (!bw.WriteChars( key.chars(), key.length() )) goto fail; if (!bw.WriteChars( val.chars(), val.length() )) goto fail; iter.next(); } goto success; // } catch (...) { fail: fclose(fp); return false; // } success: fclose(fp); return true; } const char *NVault::GetValue(const char *key) { StringHashMap::Result r = m_Hash.find(key); if (!r.found()) { return ""; } return r->value.value.chars(); } bool NVault::Open() { _ReadFromFile(); char *journal_name = new char[m_File.length() + 10]; strcpy(journal_name, m_File.chars()); char *pos = strstr(journal_name, ".vault"); if (pos) { strcpy(pos, ".journal"); } else { strcat(journal_name, ".journal"); } m_Journal = new Journal(journal_name); delete [] journal_name; m_Journal->Replay(&m_Hash); m_Journal->Erase(); if (!m_Journal->Begin()) { delete m_Journal; m_Journal = NULL; } m_Open = true; return true; } bool NVault::Close() { if (!m_Open) return false; _SaveToFile(); if (m_Journal) { m_Journal->End(); m_Journal->Erase(); } m_Open = false; return true; } void NVault::SetValue(const char *key, const char *val) { if (m_Journal) m_Journal->Write_Insert(key, val, time(NULL)); ArrayInfo info; info.value = val; info.stamp = time(NULL); m_Hash.replace(key, info); } void NVault::SetValue(const char *key, const char *val, time_t stamp) { if (m_Journal) m_Journal->Write_Insert(key, val, stamp); ArrayInfo info; info.value = val; info.stamp = stamp; m_Hash.replace(key, info); } void NVault::Remove(const char *key) { if (m_Journal) m_Journal->Write_Remove(key); m_Hash.remove(ke::AString(key).chars()); } void NVault::Clear() { if (m_Journal) m_Journal->Write_Clear(); m_Hash.clear(); } size_t NVault::Items() { return m_Hash.elements(); } size_t NVault::Prune(time_t start, time_t end) { if (m_Journal) m_Journal->Write_Prune(start, end); size_t removed = 0; for (StringHashMap::iterator iter = m_Hash.iter(); !iter.empty(); iter.next()) { time_t stamp = iter->value.stamp; bool remove = false; if (stamp != 0) { if (start == 0 && end == 0) remove = true; else if (start == 0 && stamp < end) remove = true; else if (end == 0 && stamp > start) remove = true; else if (stamp > start && stamp < end) remove = true; if (remove) { iter.erase(); removed++; } } } return removed; } void NVault::Touch(const char *key, time_t stamp) { StringHashMap::Insert i = m_Hash.findForAdd(key); if (!i.found()) { if (!m_Hash.add(i, key)) { return; } SetValue(key, "", time(NULL)); } i->value.stamp = stamp; } bool NVault::GetValue(const char *key, time_t &stamp, char buffer[], size_t len) { ArrayInfo result; if (!m_Hash.retrieve(key, &result)) { buffer[0] = '\0'; return false; } stamp = result.stamp; UTIL_Format(buffer, len, "%s", result.value.chars()); return true; } IVault *VaultMngr::OpenVault(const char *file) { NVault *pVault; //try //{ pVault = new NVault(file); // } catch (...) { if (!pVault->isValid()) { delete pVault; pVault = NULL; } return static_cast(pVault); }