Mercurial > audlegacy-plugins
comparison src/Input/adplug/core/database.cxx @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
| author | nenolod |
|---|---|
| date | Mon, 18 Sep 2006 01:11:49 -0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:13389e613d67 |
|---|---|
| 1 /* | |
| 2 * AdPlug - Replayer for many OPL2/OPL3 audio file formats. | |
| 3 * Copyright (c) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al. | |
| 4 * | |
| 5 * This library is free software; you can redistribute it and/or | |
| 6 * modify it under the terms of the GNU Lesser General Public | |
| 7 * License as published by the Free Software Foundation; either | |
| 8 * version 2.1 of the License, or (at your option) any later version. | |
| 9 * | |
| 10 * This library is distributed in the hope that it will be useful, | |
| 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 13 * Lesser General Public License for more details. | |
| 14 * | |
| 15 * You should have received a copy of the GNU Lesser General Public | |
| 16 * License along with this library; if not, write to the Free Software | |
| 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| 18 * | |
| 19 * database.cpp - AdPlug database class | |
| 20 * Copyright (c) 2002 Riven the Mage <riven@ok.ru> | |
| 21 * Copyright (c) 2002, 2003, 2006 Simon Peter <dn.tlp@gmx.net> | |
| 22 */ | |
| 23 | |
| 24 #include <binio.h> | |
| 25 #include <binfile.h> | |
| 26 #include <string.h> | |
| 27 | |
| 28 #include "database.h" | |
| 29 | |
| 30 #define DB_FILEID_V10 "AdPlug Module Information Database 1.0\x10" | |
| 31 | |
| 32 /***** CAdPlugDatabase *****/ | |
| 33 | |
| 34 const unsigned short CAdPlugDatabase::hash_radix = 0xfff1; // should be prime | |
| 35 | |
| 36 CAdPlugDatabase::CAdPlugDatabase() | |
| 37 : linear_index(0), linear_logic_length(0), linear_length(0) | |
| 38 { | |
| 39 db_linear = new DB_Bucket * [hash_radix]; | |
| 40 db_hashed = new DB_Bucket * [hash_radix]; | |
| 41 memset(db_linear, 0, sizeof(DB_Bucket *) * hash_radix); | |
| 42 memset(db_hashed, 0, sizeof(DB_Bucket *) * hash_radix); | |
| 43 } | |
| 44 | |
| 45 CAdPlugDatabase::~CAdPlugDatabase() | |
| 46 { | |
| 47 unsigned long i; | |
| 48 | |
| 49 for(i = 0; i < linear_length; i++) | |
| 50 delete db_linear[i]; | |
| 51 | |
| 52 delete [] db_linear; | |
| 53 delete [] db_hashed; | |
| 54 } | |
| 55 | |
| 56 bool CAdPlugDatabase::load(std::string db_name) | |
| 57 { | |
| 58 binifstream f(db_name); | |
| 59 if(f.error()) return false; | |
| 60 return load(f); | |
| 61 } | |
| 62 | |
| 63 bool CAdPlugDatabase::load(binistream &f) | |
| 64 { | |
| 65 unsigned int idlen = strlen(DB_FILEID_V10); | |
| 66 char *id = new char [idlen]; | |
| 67 unsigned long length; | |
| 68 | |
| 69 // Open database as little endian with IEEE floats | |
| 70 f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE); | |
| 71 | |
| 72 f.readString(id,idlen); | |
| 73 if(memcmp(id,DB_FILEID_V10,idlen)) { | |
| 74 delete [] id; | |
| 75 return false; | |
| 76 } | |
| 77 delete [] id; | |
| 78 length = f.readInt(4); | |
| 79 | |
| 80 // read records | |
| 81 for(unsigned long i = 0; i < length; i++) | |
| 82 insert(CRecord::factory(f)); | |
| 83 | |
| 84 return true; | |
| 85 } | |
| 86 | |
| 87 bool CAdPlugDatabase::save(std::string db_name) | |
| 88 { | |
| 89 binofstream f(db_name); | |
| 90 if(f.error()) return false; | |
| 91 return save(f); | |
| 92 } | |
| 93 | |
| 94 bool CAdPlugDatabase::save(binostream &f) | |
| 95 { | |
| 96 unsigned long i; | |
| 97 | |
| 98 // Save database as little endian with IEEE floats | |
| 99 f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE); | |
| 100 | |
| 101 f.writeString(DB_FILEID_V10); | |
| 102 f.writeInt(linear_logic_length, 4); | |
| 103 | |
| 104 // write records | |
| 105 for(i = 0; i < linear_length; i++) | |
| 106 if(!db_linear[i]->deleted) | |
| 107 db_linear[i]->record->write(f); | |
| 108 | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 CAdPlugDatabase::CRecord *CAdPlugDatabase::search(CKey const &key) | |
| 113 { | |
| 114 if(lookup(key)) return get_record(); else return 0; | |
| 115 } | |
| 116 | |
| 117 bool CAdPlugDatabase::lookup(CKey const &key) | |
| 118 { | |
| 119 unsigned long index = make_hash(key); | |
| 120 if(!db_hashed[index]) return false; | |
| 121 | |
| 122 // immediate hit ? | |
| 123 DB_Bucket *bucket = db_hashed[index]; | |
| 124 | |
| 125 if(!bucket->deleted && bucket->record->key == key) { | |
| 126 linear_index = bucket->index; | |
| 127 return true; | |
| 128 } | |
| 129 | |
| 130 // in-chain hit ? | |
| 131 bucket = db_hashed[index]->chain; | |
| 132 | |
| 133 while(bucket) { | |
| 134 if(!bucket->deleted && bucket->record->key == key) { | |
| 135 linear_index = bucket->index; | |
| 136 return true; | |
| 137 } | |
| 138 | |
| 139 bucket = bucket->chain; | |
| 140 } | |
| 141 | |
| 142 return false; | |
| 143 } | |
| 144 | |
| 145 bool CAdPlugDatabase::insert(CRecord *record) | |
| 146 { | |
| 147 long index; | |
| 148 | |
| 149 // sanity checks | |
| 150 if(!record) return false; // null-pointer given | |
| 151 if(linear_length == hash_radix) return false; // max. db size exceeded | |
| 152 if(lookup(record->key)) return false; // record already in db | |
| 153 | |
| 154 // make bucket | |
| 155 DB_Bucket *bucket = new DB_Bucket(linear_length, record); | |
| 156 if(!bucket) return false; | |
| 157 | |
| 158 // add to linear list | |
| 159 db_linear[linear_length] = bucket; | |
| 160 linear_logic_length++; linear_length++; | |
| 161 | |
| 162 // add to hashed list | |
| 163 index = make_hash(record->key); | |
| 164 | |
| 165 if(!db_hashed[index]) // First entry in hashtable | |
| 166 db_hashed[index] = bucket; | |
| 167 else { // Add entry in chained list | |
| 168 DB_Bucket *chain = db_hashed[index]; | |
| 169 | |
| 170 while(chain->chain) chain = chain->chain; | |
| 171 chain->chain = bucket; | |
| 172 } | |
| 173 | |
| 174 return true; | |
| 175 } | |
| 176 | |
| 177 void CAdPlugDatabase::wipe(CRecord *record) | |
| 178 { | |
| 179 if(!lookup(record->key)) return; | |
| 180 wipe(); | |
| 181 } | |
| 182 | |
| 183 void CAdPlugDatabase::wipe() | |
| 184 { | |
| 185 if(!linear_length) return; | |
| 186 | |
| 187 DB_Bucket *bucket = db_linear[linear_index]; | |
| 188 | |
| 189 if(!bucket->deleted) { | |
| 190 delete bucket->record; | |
| 191 linear_logic_length--; | |
| 192 bucket->deleted = true; | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 CAdPlugDatabase::CRecord *CAdPlugDatabase::get_record() | |
| 197 { | |
| 198 if(!linear_length) return 0; | |
| 199 return db_linear[linear_index]->record; | |
| 200 } | |
| 201 | |
| 202 bool CAdPlugDatabase::go_forward() | |
| 203 { | |
| 204 if(linear_index + 1 < linear_length) { | |
| 205 linear_index++; | |
| 206 return true; | |
| 207 } else | |
| 208 return false; | |
| 209 } | |
| 210 | |
| 211 bool CAdPlugDatabase::go_backward() | |
| 212 { | |
| 213 if(!linear_index) return false; | |
| 214 linear_index--; | |
| 215 return true; | |
| 216 } | |
| 217 | |
| 218 void CAdPlugDatabase::goto_begin() | |
| 219 { | |
| 220 if(linear_length) linear_index = 0; | |
| 221 } | |
| 222 | |
| 223 void CAdPlugDatabase::goto_end() | |
| 224 { | |
| 225 if(linear_length) linear_index = linear_length - 1; | |
| 226 } | |
| 227 | |
| 228 inline unsigned long CAdPlugDatabase::make_hash(CKey const &key) | |
| 229 { | |
| 230 return (key.crc32 + key.crc16) % hash_radix; | |
| 231 } | |
| 232 | |
| 233 /***** CAdPlugDatabase::DB_Bucket *****/ | |
| 234 | |
| 235 CAdPlugDatabase::DB_Bucket::DB_Bucket(unsigned long nindex, CRecord *newrecord, DB_Bucket *newchain) | |
| 236 : index(nindex), deleted(false), chain(newchain), record(newrecord) | |
| 237 { | |
| 238 } | |
| 239 | |
| 240 CAdPlugDatabase::DB_Bucket::~DB_Bucket() | |
| 241 { | |
| 242 if(!deleted) delete record; | |
| 243 } | |
| 244 | |
| 245 /***** CAdPlugDatabase::CRecord *****/ | |
| 246 | |
| 247 CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(RecordType type) | |
| 248 { | |
| 249 switch(type) { | |
| 250 case Plain: return new CPlainRecord; | |
| 251 case SongInfo: return new CInfoRecord; | |
| 252 case ClockSpeed: return new CClockRecord; | |
| 253 default: return 0; | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(binistream &in) | |
| 258 { | |
| 259 RecordType type; | |
| 260 unsigned long size; | |
| 261 CRecord *rec; | |
| 262 | |
| 263 type = (RecordType)in.readInt(1); size = in.readInt(4); | |
| 264 rec = factory(type); | |
| 265 | |
| 266 if(rec) { | |
| 267 rec->key.crc16 = in.readInt(2); rec->key.crc32 = in.readInt(4); | |
| 268 rec->filetype = in.readString('\0'); rec->comment = in.readString('\0'); | |
| 269 rec->read_own(in); | |
| 270 return rec; | |
| 271 } else { | |
| 272 // skip this record, cause we don't know about it | |
| 273 in.seek(size, binio::Add); | |
| 274 return 0; | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 void CAdPlugDatabase::CRecord::write(binostream &out) | |
| 279 { | |
| 280 out.writeInt(type, 1); | |
| 281 out.writeInt(get_size() + filetype.length() + comment.length() + 8, 4); | |
| 282 out.writeInt(key.crc16, 2); out.writeInt(key.crc32, 4); | |
| 283 out.writeString(filetype); out.writeInt('\0', 1); | |
| 284 out.writeString(comment); out.writeInt('\0', 1); | |
| 285 | |
| 286 write_own(out); | |
| 287 } | |
| 288 | |
| 289 bool CAdPlugDatabase::CRecord::user_read(std::istream &in, std::ostream &out) | |
| 290 { | |
| 291 return user_read_own(in, out); | |
| 292 } | |
| 293 | |
| 294 bool CAdPlugDatabase::CRecord::user_write(std::ostream &out) | |
| 295 { | |
| 296 out << "Record type: "; | |
| 297 switch(type) { | |
| 298 case Plain: out << "Plain"; break; | |
| 299 case SongInfo: out << "SongInfo"; break; | |
| 300 case ClockSpeed: out << "ClockSpeed"; break; | |
| 301 default: out << "*** Unknown ***"; break; | |
| 302 } | |
| 303 out << std::endl; | |
| 304 out << "Key: " << std::hex << key.crc16 << ":" << key.crc32 << std::dec << std::endl; | |
| 305 out << "File type: " << filetype << std::endl; | |
| 306 out << "Comment: " << comment << std::endl; | |
| 307 | |
| 308 return user_write_own(out); | |
| 309 } | |
| 310 | |
| 311 /***** CAdPlugDatabase::CRecord::CKey *****/ | |
| 312 | |
| 313 CAdPlugDatabase::CKey::CKey(binistream &buf) | |
| 314 { | |
| 315 make(buf); | |
| 316 } | |
| 317 | |
| 318 bool CAdPlugDatabase::CKey::operator==(const CKey &key) | |
| 319 { | |
| 320 return ((crc16 == key.crc16) && (crc32 == key.crc32)); | |
| 321 } | |
| 322 | |
| 323 void CAdPlugDatabase::CKey::make(binistream &buf) | |
| 324 // Key is CRC16:CRC32 pair. CRC16 and CRC32 calculation routines (c) Zhengxi | |
| 325 { | |
| 326 static const unsigned short magic16 = 0xa001; | |
| 327 static const unsigned long magic32 = 0xedb88320; | |
| 328 | |
| 329 crc16 = 0; crc32 = ~0; | |
| 330 | |
| 331 while(!buf.eof()) | |
| 332 { | |
| 333 unsigned char byte = buf.readInt(1); | |
| 334 | |
| 335 for (int j=0;j<8;j++) | |
| 336 { | |
| 337 if ((crc16 ^ byte) & 1) | |
| 338 crc16 = (crc16 >> 1) ^ magic16; | |
| 339 else | |
| 340 crc16 >>= 1; | |
| 341 | |
| 342 if ((crc32 ^ byte) & 1) | |
| 343 crc32 = (crc32 >> 1) ^ magic32; | |
| 344 else | |
| 345 crc32 >>= 1; | |
| 346 | |
| 347 byte >>= 1; | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 crc16 &= 0xffff; | |
| 352 crc32 = ~crc32; | |
| 353 } | |
| 354 | |
| 355 /***** CInfoRecord *****/ | |
| 356 | |
| 357 CInfoRecord::CInfoRecord() | |
| 358 { | |
| 359 type = SongInfo; | |
| 360 } | |
| 361 | |
| 362 void CInfoRecord::read_own(binistream &in) | |
| 363 { | |
| 364 title = in.readString('\0'); | |
| 365 author = in.readString('\0'); | |
| 366 } | |
| 367 | |
| 368 void CInfoRecord::write_own(binostream &out) | |
| 369 { | |
| 370 out.writeString(title); out.writeInt('\0', 1); | |
| 371 out.writeString(author); out.writeInt('\0', 1); | |
| 372 } | |
| 373 | |
| 374 unsigned long CInfoRecord::get_size() | |
| 375 { | |
| 376 return title.length() + author.length() + 2; | |
| 377 } | |
| 378 | |
| 379 bool CInfoRecord::user_read_own(std::istream &in, std::ostream &out) | |
| 380 { | |
| 381 out << "Title: "; in >> title; | |
| 382 out << "Author: "; in >> author; | |
| 383 return true; | |
| 384 } | |
| 385 | |
| 386 bool CInfoRecord::user_write_own(std::ostream &out) | |
| 387 { | |
| 388 out << "Title: " << title << std::endl; | |
| 389 out << "Author: " << author << std::endl; | |
| 390 return true; | |
| 391 } | |
| 392 | |
| 393 /***** CClockRecord *****/ | |
| 394 | |
| 395 CClockRecord::CClockRecord() | |
| 396 : clock(0.0f) | |
| 397 { | |
| 398 type = ClockSpeed; | |
| 399 } | |
| 400 | |
| 401 void CClockRecord::read_own(binistream &in) | |
| 402 { | |
| 403 clock = in.readFloat(binio::Single); | |
| 404 } | |
| 405 | |
| 406 void CClockRecord::write_own(binostream &out) | |
| 407 { | |
| 408 out.writeFloat(clock, binio::Single); | |
| 409 } | |
| 410 | |
| 411 unsigned long CClockRecord::get_size() | |
| 412 { | |
| 413 return 4; | |
| 414 } | |
| 415 | |
| 416 bool CClockRecord::user_read_own(std::istream &in, std::ostream &out) | |
| 417 { | |
| 418 out << "Clockspeed: "; in >> clock; | |
| 419 return true; | |
| 420 } | |
| 421 | |
| 422 bool CClockRecord::user_write_own(std::ostream &out) | |
| 423 { | |
| 424 out << "Clock speed: " << clock << " Hz" << std::endl; | |
| 425 return true; | |
| 426 } |
