diff Plugins/Input/adplug/core/database.cpp @ 359:8df427a314a8 trunk

[svn] Adlib synthesizer (AdPlug) support.
author chainsaw
date Fri, 30 Dec 2005 16:31:39 -0800
parents
children eb41901d38f5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/adplug/core/database.cpp	Fri Dec 30 16:31:39 2005 -0800
@@ -0,0 +1,423 @@
+/*
+ * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (c) 1999 - 2003 Simon Peter <dn.tlp@gmx.net>, et al.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * database.cpp - AdPlug database class
+ * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
+ * Copyright (c) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <binio.h>
+#include <binfile.h>
+#include <string.h>
+
+#include "database.h"
+
+#define DB_FILEID_V10	"AdPlug Module Information Database 1.0\x10"
+
+/***** CAdPlugDatabase *****/
+
+const unsigned short CAdPlugDatabase::hash_radix = 0xfff1;	// should be prime
+
+CAdPlugDatabase::CAdPlugDatabase()
+  : linear_index(0), linear_logic_length(0), linear_length(0)
+{
+  db_linear = new DB_Bucket * [hash_radix];
+  db_hashed = new DB_Bucket * [hash_radix];
+  memset(db_linear, 0, sizeof(DB_Bucket *) * hash_radix);
+  memset(db_hashed, 0, sizeof(DB_Bucket *) * hash_radix);
+}
+
+CAdPlugDatabase::~CAdPlugDatabase()
+{
+  unsigned long i;
+
+  for(i = 0; i < linear_length; i++)
+    delete db_linear[i];
+
+  delete [] db_linear;
+  delete [] db_hashed;
+}
+
+bool CAdPlugDatabase::load(std::string db_name)
+{
+  binifstream f(db_name);
+  if(f.error()) return false;
+  return load(f);
+}
+
+bool CAdPlugDatabase::load(binistream &f)
+{
+  unsigned int idlen = strlen(DB_FILEID_V10);
+  char *id = new char [idlen];
+  unsigned long length;
+
+  // Open database as little endian with IEEE floats
+  f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE);
+
+  f.readString(id,idlen);
+  if(memcmp(id,DB_FILEID_V10,idlen)) {
+    delete [] id;
+    return false;
+  }
+  delete [] id;
+  length = f.readInt(4);
+
+  // read records
+  for(unsigned long i = 0; i < length; i++)
+    insert(CRecord::factory(f));
+
+  return true;
+}
+
+bool CAdPlugDatabase::save(std::string db_name)
+{
+  binofstream f(db_name);
+  if(f.error()) return false;
+  return save(f);
+}
+
+bool CAdPlugDatabase::save(binostream &f)
+{
+  unsigned long i;
+
+  f.writeString(DB_FILEID_V10);
+  f.writeInt(linear_logic_length, 4);
+
+  // write records
+  for(i = 0; i < linear_length; i++)
+    if(!db_linear[i]->deleted)
+      db_linear[i]->record->write(f);
+
+  return true;
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::search(CKey const &key)
+{
+  if(lookup(key)) return get_record(); else return 0;
+}
+
+bool CAdPlugDatabase::lookup(CKey const &key)
+{
+  unsigned long index = make_hash(key);
+  if(!db_hashed[index]) return false;
+
+  // immediate hit ?
+  DB_Bucket *bucket = db_hashed[index];
+
+  if(!bucket->deleted && bucket->record->key == key) {
+    linear_index = bucket->index;
+    return true;
+  }
+
+  // in-chain hit ?
+  bucket = db_hashed[index]->chain;
+
+  while(bucket) {
+    if(!bucket->deleted && bucket->record->key == key) {
+      linear_index = bucket->index;
+      return true;
+    }
+
+    bucket = bucket->chain;
+  }
+
+  return false;
+}
+
+bool CAdPlugDatabase::insert(CRecord *record)
+{
+  long index;
+
+  // sanity checks
+  if(!record) return false;			// null-pointer given
+  if(linear_length == hash_radix) return false;	// max. db size exceeded
+  if(lookup(record->key)) return false;		// record already in db
+
+  // make bucket
+  DB_Bucket *bucket = new DB_Bucket(linear_length, record);
+  if(!bucket) return false;
+
+  // add to linear list
+  db_linear[linear_length] = bucket;
+  linear_logic_length++; linear_length++;
+
+  // add to hashed list
+  index = make_hash(record->key);
+
+  if(!db_hashed[index])	// First entry in hashtable
+    db_hashed[index] = bucket;
+  else {		// Add entry in chained list
+    DB_Bucket *chain = db_hashed[index];
+
+    while(chain->chain) chain = chain->chain;
+    chain->chain = bucket;
+  }
+
+  return true;
+}
+
+void CAdPlugDatabase::wipe(CRecord *record)
+{
+  if(!lookup(record->key)) return;
+  wipe();
+}
+
+void CAdPlugDatabase::wipe()
+{
+  if(!linear_length) return;
+
+  DB_Bucket *bucket = db_linear[linear_index];
+
+  if(!bucket->deleted) {
+    delete bucket->record;
+    linear_logic_length--;
+    bucket->deleted = true;
+  }
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::get_record()
+{
+  if(!linear_length) return 0;
+  return db_linear[linear_index]->record;
+}
+
+bool CAdPlugDatabase::go_forward()
+{
+  if(linear_index + 1 < linear_length) {
+    linear_index++;
+    return true;
+  } else
+    return false;
+}
+
+bool CAdPlugDatabase::go_backward()
+{
+  if(!linear_index) return false;
+  linear_index--;
+  return true;
+}
+
+void CAdPlugDatabase::goto_begin()
+{	
+  if(linear_length) linear_index = 0;
+}
+
+void CAdPlugDatabase::goto_end()
+{
+  if(linear_length) linear_index = linear_length - 1;
+}
+
+inline unsigned long CAdPlugDatabase::make_hash(CKey const &key)
+{
+  return (key.crc32 + key.crc16) % hash_radix;
+}
+
+/***** CAdPlugDatabase::DB_Bucket *****/
+
+CAdPlugDatabase::DB_Bucket::DB_Bucket(unsigned long nindex, CRecord *newrecord, DB_Bucket *newchain)
+  : index(nindex), deleted(false), chain(newchain), record(newrecord)
+{
+}
+
+CAdPlugDatabase::DB_Bucket::~DB_Bucket()
+{
+  if(!deleted) delete record;
+}
+
+/***** CAdPlugDatabase::CRecord *****/
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(RecordType type)
+{
+  switch(type) {
+  case Plain: return new CPlainRecord;
+  case SongInfo: return new CInfoRecord;
+  case ClockSpeed: return new CClockRecord;
+  default: return 0;
+  }
+}
+
+CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(binistream &in)
+{
+  RecordType	type;
+  unsigned long	size;
+  CRecord	*rec;
+
+  type = (RecordType)in.readInt(1); size = in.readInt(4);
+  rec = factory(type);
+
+  if(rec) {
+    rec->key.crc16 = in.readInt(2); rec->key.crc32 = in.readInt(4);
+    rec->filetype = in.readString('\0'); rec->comment = in.readString('\0');
+    rec->read_own(in);
+    return rec;
+  } else {
+    // skip this record, cause we don't know about it
+    in.seek(size, binio::Add);
+    return 0;
+  }
+}
+
+void CAdPlugDatabase::CRecord::write(binostream &out)
+{
+  out.writeInt(type, 1);
+  out.writeInt(get_size() + filetype.length() + comment.length() + 8, 4);
+  out.writeInt(key.crc16, 2); out.writeInt(key.crc32, 4);
+  out.writeString(filetype); out.writeInt('\0', 1);
+  out.writeString(comment); out.writeInt('\0', 1);
+
+  write_own(out);
+}
+
+bool CAdPlugDatabase::CRecord::user_read(std::istream &in, std::ostream &out)
+{
+  return user_read_own(in, out);
+}
+
+bool CAdPlugDatabase::CRecord::user_write(std::ostream &out)
+{
+  out << "Record type: ";
+  switch(type) {
+  case Plain: out << "Plain"; break;
+  case SongInfo: out << "SongInfo"; break;
+  case ClockSpeed: out << "ClockSpeed"; break;
+  default: out << "*** Unknown ***"; break;
+  }
+  out << std::endl;
+  out << "Key: " << std::hex << key.crc16 << ":" << key.crc32 << std::dec << std::endl;
+  out << "File type: " << filetype << std::endl;
+  out << "Comment: " << comment << std::endl;
+
+  return user_write_own(out);
+}
+
+/***** CAdPlugDatabase::CRecord::CKey *****/
+
+CAdPlugDatabase::CKey::CKey(binistream &buf)
+{
+  make(buf);
+}
+
+bool CAdPlugDatabase::CKey::operator==(const CKey &key)
+{
+  return ((crc16 == key.crc16) && (crc32 == key.crc32));
+}
+
+void CAdPlugDatabase::CKey::make(binistream &buf)
+// Key is CRC16:CRC32 pair. CRC16 and CRC32 calculation routines (c) Zhengxi
+{
+  static const unsigned short magic16 = 0xa001;
+  static const unsigned long  magic32 = 0xedb88320;
+
+  crc16 = 0; crc32 = ~0;
+
+  while(!buf.eof())
+    {
+      unsigned char byte = buf.readInt(1);
+
+      for (int j=0;j<8;j++)
+	{
+	  if ((crc16 ^ byte) & 1)
+	    crc16 = (crc16 >> 1) ^ magic16;
+	  else
+	    crc16 >>= 1;
+
+	  if ((crc32 ^ byte) & 1)
+	    crc32 = (crc32 >> 1) ^ magic32;
+	  else
+	    crc32 >>= 1;
+
+	  byte >>= 1;
+	}
+    }
+
+  crc16 &= 0xffff;
+  crc32  = ~crc32;
+}
+
+/***** CInfoRecord *****/
+
+CInfoRecord::CInfoRecord()
+{
+  type = SongInfo;
+}
+
+void CInfoRecord::read_own(binistream &in)
+{
+  title = in.readString('\0');
+  author = in.readString('\0');
+}
+
+void CInfoRecord::write_own(binostream &out)
+{
+  out.writeString(title); out.writeInt('\0', 1);
+  out.writeString(author); out.writeInt('\0', 1);
+}
+
+unsigned long CInfoRecord::get_size()
+{
+  return title.length() + author.length() + 2;
+}
+
+bool CInfoRecord::user_read_own(std::istream &in, std::ostream &out)
+{
+  out << "Title: "; in >> title;
+  out << "Author: "; in >> author;
+  return true;
+}
+
+bool CInfoRecord::user_write_own(std::ostream &out)
+{
+  out << "Title: " << title << std::endl;
+  out << "Author: " << author << std::endl;
+  return true;
+}
+
+/***** CClockRecord *****/
+
+CClockRecord::CClockRecord()
+  : clock(0.0f)
+{
+  type = ClockSpeed;
+}
+
+void CClockRecord::read_own(binistream &in)
+{
+  clock = in.readFloat(binio::Single);
+}
+
+void CClockRecord::write_own(binostream &out)
+{
+  out.writeFloat(clock, binio::Single);
+}
+
+unsigned long CClockRecord::get_size()
+{
+  return 4;
+}
+
+bool CClockRecord::user_read_own(std::istream &in, std::ostream &out)
+{
+  out << "Clockspeed: "; in >> clock;
+  return true;
+}
+
+bool CClockRecord::user_write_own(std::ostream &out)
+{
+  out << "Clock speed: " << clock << " Hz" << std::endl;
+  return true;
+}