Mercurial > audlegacy
annotate Plugins/Input/adplug/core/database.cpp @ 1280:6ad7eb96dd26 trunk
[svn] Sync with upstream. This adds Westwood ADL format support.
author | chainsaw |
---|---|
date | Sat, 17 Jun 2006 16:17:51 -0700 |
parents | 2b06eb5e472d |
children | f12d7e208b43 |
rev | line source |
---|---|
359 | 1 /* |
2 * AdPlug - Replayer for many OPL2/OPL3 audio file formats. | |
637
2b06eb5e472d
[svn] Sync with upstream. Drop hardware OPL2/3 support, it throws warnings and is not used on most modern machines. Added var inits where GCC 4.0 thought it was a good idea.
chainsaw
parents:
387
diff
changeset
|
3 * Copyright (c) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al. |
359 | 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 * | |
19 * database.cpp - AdPlug database class | |
20 * Copyright (c) 2002 Riven the Mage <riven@ok.ru> | |
637
2b06eb5e472d
[svn] Sync with upstream. Drop hardware OPL2/3 support, it throws warnings and is not used on most modern machines. Added var inits where GCC 4.0 thought it was a good idea.
chainsaw
parents:
387
diff
changeset
|
21 * Copyright (c) 2002, 2003, 2006 Simon Peter <dn.tlp@gmx.net> |
359 | 22 */ |
23 | |
387
7f0e78f42032
[svn] Disable adplug if the includes are missing (or in a very lame place). Stop blowing holes in my ship.
chainsaw
parents:
379
diff
changeset
|
24 #include <binio.h> |
7f0e78f42032
[svn] Disable adplug if the includes are missing (or in a very lame place). Stop blowing holes in my ship.
chainsaw
parents:
379
diff
changeset
|
25 #include <binfile.h> |
359 | 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 | |
637
2b06eb5e472d
[svn] Sync with upstream. Drop hardware OPL2/3 support, it throws warnings and is not used on most modern machines. Added var inits where GCC 4.0 thought it was a good idea.
chainsaw
parents:
387
diff
changeset
|
98 // Save database as little endian with IEEE floats |
2b06eb5e472d
[svn] Sync with upstream. Drop hardware OPL2/3 support, it throws warnings and is not used on most modern machines. Added var inits where GCC 4.0 thought it was a good idea.
chainsaw
parents:
387
diff
changeset
|
99 f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE); |
2b06eb5e472d
[svn] Sync with upstream. Drop hardware OPL2/3 support, it throws warnings and is not used on most modern machines. Added var inits where GCC 4.0 thought it was a good idea.
chainsaw
parents:
387
diff
changeset
|
100 |
359 | 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 } |