Mercurial > audlegacy
annotate Plugins/Input/adplug/core/database.cpp @ 387:7f0e78f42032 trunk
[svn] Disable adplug if the includes are missing (or in a very lame place). Stop blowing holes in my ship.
author | chainsaw |
---|---|
date | Tue, 03 Jan 2006 14:25:32 -0800 |
parents | eb41901d38f5 |
children | 2b06eb5e472d |
rev | line source |
---|---|
359 | 1 /* |
2 * AdPlug - Replayer for many OPL2/OPL3 audio file formats. | |
3 * Copyright (c) 1999 - 2003 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., 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> | |
21 * Copyright (c) 2002, 2003 Simon Peter <dn.tlp@gmx.net> | |
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 | |
98 f.writeString(DB_FILEID_V10); | |
99 f.writeInt(linear_logic_length, 4); | |
100 | |
101 // write records | |
102 for(i = 0; i < linear_length; i++) | |
103 if(!db_linear[i]->deleted) | |
104 db_linear[i]->record->write(f); | |
105 | |
106 return true; | |
107 } | |
108 | |
109 CAdPlugDatabase::CRecord *CAdPlugDatabase::search(CKey const &key) | |
110 { | |
111 if(lookup(key)) return get_record(); else return 0; | |
112 } | |
113 | |
114 bool CAdPlugDatabase::lookup(CKey const &key) | |
115 { | |
116 unsigned long index = make_hash(key); | |
117 if(!db_hashed[index]) return false; | |
118 | |
119 // immediate hit ? | |
120 DB_Bucket *bucket = db_hashed[index]; | |
121 | |
122 if(!bucket->deleted && bucket->record->key == key) { | |
123 linear_index = bucket->index; | |
124 return true; | |
125 } | |
126 | |
127 // in-chain hit ? | |
128 bucket = db_hashed[index]->chain; | |
129 | |
130 while(bucket) { | |
131 if(!bucket->deleted && bucket->record->key == key) { | |
132 linear_index = bucket->index; | |
133 return true; | |
134 } | |
135 | |
136 bucket = bucket->chain; | |
137 } | |
138 | |
139 return false; | |
140 } | |
141 | |
142 bool CAdPlugDatabase::insert(CRecord *record) | |
143 { | |
144 long index; | |
145 | |
146 // sanity checks | |
147 if(!record) return false; // null-pointer given | |
148 if(linear_length == hash_radix) return false; // max. db size exceeded | |
149 if(lookup(record->key)) return false; // record already in db | |
150 | |
151 // make bucket | |
152 DB_Bucket *bucket = new DB_Bucket(linear_length, record); | |
153 if(!bucket) return false; | |
154 | |
155 // add to linear list | |
156 db_linear[linear_length] = bucket; | |
157 linear_logic_length++; linear_length++; | |
158 | |
159 // add to hashed list | |
160 index = make_hash(record->key); | |
161 | |
162 if(!db_hashed[index]) // First entry in hashtable | |
163 db_hashed[index] = bucket; | |
164 else { // Add entry in chained list | |
165 DB_Bucket *chain = db_hashed[index]; | |
166 | |
167 while(chain->chain) chain = chain->chain; | |
168 chain->chain = bucket; | |
169 } | |
170 | |
171 return true; | |
172 } | |
173 | |
174 void CAdPlugDatabase::wipe(CRecord *record) | |
175 { | |
176 if(!lookup(record->key)) return; | |
177 wipe(); | |
178 } | |
179 | |
180 void CAdPlugDatabase::wipe() | |
181 { | |
182 if(!linear_length) return; | |
183 | |
184 DB_Bucket *bucket = db_linear[linear_index]; | |
185 | |
186 if(!bucket->deleted) { | |
187 delete bucket->record; | |
188 linear_logic_length--; | |
189 bucket->deleted = true; | |
190 } | |
191 } | |
192 | |
193 CAdPlugDatabase::CRecord *CAdPlugDatabase::get_record() | |
194 { | |
195 if(!linear_length) return 0; | |
196 return db_linear[linear_index]->record; | |
197 } | |
198 | |
199 bool CAdPlugDatabase::go_forward() | |
200 { | |
201 if(linear_index + 1 < linear_length) { | |
202 linear_index++; | |
203 return true; | |
204 } else | |
205 return false; | |
206 } | |
207 | |
208 bool CAdPlugDatabase::go_backward() | |
209 { | |
210 if(!linear_index) return false; | |
211 linear_index--; | |
212 return true; | |
213 } | |
214 | |
215 void CAdPlugDatabase::goto_begin() | |
216 { | |
217 if(linear_length) linear_index = 0; | |
218 } | |
219 | |
220 void CAdPlugDatabase::goto_end() | |
221 { | |
222 if(linear_length) linear_index = linear_length - 1; | |
223 } | |
224 | |
225 inline unsigned long CAdPlugDatabase::make_hash(CKey const &key) | |
226 { | |
227 return (key.crc32 + key.crc16) % hash_radix; | |
228 } | |
229 | |
230 /***** CAdPlugDatabase::DB_Bucket *****/ | |
231 | |
232 CAdPlugDatabase::DB_Bucket::DB_Bucket(unsigned long nindex, CRecord *newrecord, DB_Bucket *newchain) | |
233 : index(nindex), deleted(false), chain(newchain), record(newrecord) | |
234 { | |
235 } | |
236 | |
237 CAdPlugDatabase::DB_Bucket::~DB_Bucket() | |
238 { | |
239 if(!deleted) delete record; | |
240 } | |
241 | |
242 /***** CAdPlugDatabase::CRecord *****/ | |
243 | |
244 CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(RecordType type) | |
245 { | |
246 switch(type) { | |
247 case Plain: return new CPlainRecord; | |
248 case SongInfo: return new CInfoRecord; | |
249 case ClockSpeed: return new CClockRecord; | |
250 default: return 0; | |
251 } | |
252 } | |
253 | |
254 CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(binistream &in) | |
255 { | |
256 RecordType type; | |
257 unsigned long size; | |
258 CRecord *rec; | |
259 | |
260 type = (RecordType)in.readInt(1); size = in.readInt(4); | |
261 rec = factory(type); | |
262 | |
263 if(rec) { | |
264 rec->key.crc16 = in.readInt(2); rec->key.crc32 = in.readInt(4); | |
265 rec->filetype = in.readString('\0'); rec->comment = in.readString('\0'); | |
266 rec->read_own(in); | |
267 return rec; | |
268 } else { | |
269 // skip this record, cause we don't know about it | |
270 in.seek(size, binio::Add); | |
271 return 0; | |
272 } | |
273 } | |
274 | |
275 void CAdPlugDatabase::CRecord::write(binostream &out) | |
276 { | |
277 out.writeInt(type, 1); | |
278 out.writeInt(get_size() + filetype.length() + comment.length() + 8, 4); | |
279 out.writeInt(key.crc16, 2); out.writeInt(key.crc32, 4); | |
280 out.writeString(filetype); out.writeInt('\0', 1); | |
281 out.writeString(comment); out.writeInt('\0', 1); | |
282 | |
283 write_own(out); | |
284 } | |
285 | |
286 bool CAdPlugDatabase::CRecord::user_read(std::istream &in, std::ostream &out) | |
287 { | |
288 return user_read_own(in, out); | |
289 } | |
290 | |
291 bool CAdPlugDatabase::CRecord::user_write(std::ostream &out) | |
292 { | |
293 out << "Record type: "; | |
294 switch(type) { | |
295 case Plain: out << "Plain"; break; | |
296 case SongInfo: out << "SongInfo"; break; | |
297 case ClockSpeed: out << "ClockSpeed"; break; | |
298 default: out << "*** Unknown ***"; break; | |
299 } | |
300 out << std::endl; | |
301 out << "Key: " << std::hex << key.crc16 << ":" << key.crc32 << std::dec << std::endl; | |
302 out << "File type: " << filetype << std::endl; | |
303 out << "Comment: " << comment << std::endl; | |
304 | |
305 return user_write_own(out); | |
306 } | |
307 | |
308 /***** CAdPlugDatabase::CRecord::CKey *****/ | |
309 | |
310 CAdPlugDatabase::CKey::CKey(binistream &buf) | |
311 { | |
312 make(buf); | |
313 } | |
314 | |
315 bool CAdPlugDatabase::CKey::operator==(const CKey &key) | |
316 { | |
317 return ((crc16 == key.crc16) && (crc32 == key.crc32)); | |
318 } | |
319 | |
320 void CAdPlugDatabase::CKey::make(binistream &buf) | |
321 // Key is CRC16:CRC32 pair. CRC16 and CRC32 calculation routines (c) Zhengxi | |
322 { | |
323 static const unsigned short magic16 = 0xa001; | |
324 static const unsigned long magic32 = 0xedb88320; | |
325 | |
326 crc16 = 0; crc32 = ~0; | |
327 | |
328 while(!buf.eof()) | |
329 { | |
330 unsigned char byte = buf.readInt(1); | |
331 | |
332 for (int j=0;j<8;j++) | |
333 { | |
334 if ((crc16 ^ byte) & 1) | |
335 crc16 = (crc16 >> 1) ^ magic16; | |
336 else | |
337 crc16 >>= 1; | |
338 | |
339 if ((crc32 ^ byte) & 1) | |
340 crc32 = (crc32 >> 1) ^ magic32; | |
341 else | |
342 crc32 >>= 1; | |
343 | |
344 byte >>= 1; | |
345 } | |
346 } | |
347 | |
348 crc16 &= 0xffff; | |
349 crc32 = ~crc32; | |
350 } | |
351 | |
352 /***** CInfoRecord *****/ | |
353 | |
354 CInfoRecord::CInfoRecord() | |
355 { | |
356 type = SongInfo; | |
357 } | |
358 | |
359 void CInfoRecord::read_own(binistream &in) | |
360 { | |
361 title = in.readString('\0'); | |
362 author = in.readString('\0'); | |
363 } | |
364 | |
365 void CInfoRecord::write_own(binostream &out) | |
366 { | |
367 out.writeString(title); out.writeInt('\0', 1); | |
368 out.writeString(author); out.writeInt('\0', 1); | |
369 } | |
370 | |
371 unsigned long CInfoRecord::get_size() | |
372 { | |
373 return title.length() + author.length() + 2; | |
374 } | |
375 | |
376 bool CInfoRecord::user_read_own(std::istream &in, std::ostream &out) | |
377 { | |
378 out << "Title: "; in >> title; | |
379 out << "Author: "; in >> author; | |
380 return true; | |
381 } | |
382 | |
383 bool CInfoRecord::user_write_own(std::ostream &out) | |
384 { | |
385 out << "Title: " << title << std::endl; | |
386 out << "Author: " << author << std::endl; | |
387 return true; | |
388 } | |
389 | |
390 /***** CClockRecord *****/ | |
391 | |
392 CClockRecord::CClockRecord() | |
393 : clock(0.0f) | |
394 { | |
395 type = ClockSpeed; | |
396 } | |
397 | |
398 void CClockRecord::read_own(binistream &in) | |
399 { | |
400 clock = in.readFloat(binio::Single); | |
401 } | |
402 | |
403 void CClockRecord::write_own(binostream &out) | |
404 { | |
405 out.writeFloat(clock, binio::Single); | |
406 } | |
407 | |
408 unsigned long CClockRecord::get_size() | |
409 { | |
410 return 4; | |
411 } | |
412 | |
413 bool CClockRecord::user_read_own(std::istream &in, std::ostream &out) | |
414 { | |
415 out << "Clockspeed: "; in >> clock; | |
416 return true; | |
417 } | |
418 | |
419 bool CClockRecord::user_write_own(std::ostream &out) | |
420 { | |
421 out << "Clock speed: " << clock << " Hz" << std::endl; | |
422 return true; | |
423 } |