|
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 * imf.cpp - IMF Player by Simon Peter <dn.tlp@gmx.net>
|
|
|
20 *
|
|
|
21 * FILE FORMAT:
|
|
|
22 * There seem to be 2 different flavors of IMF formats out there. One version
|
|
|
23 * contains just the raw IMF music data. In this case, the first word of the
|
|
|
24 * file is always 0 (because the music data starts this way). This is already
|
|
|
25 * the music data! So read in the entire file and play it.
|
|
|
26 *
|
|
|
27 * If this word is greater than 0, it specifies the size of the following
|
|
|
28 * song data in bytes. In this case, the file has a footer that contains
|
|
|
29 * arbitrary infos about it. Mostly, this is plain ASCII text with some words
|
|
|
30 * of the author. Read and play the specified amount of song data and display
|
|
|
31 * the remaining data as ASCII text.
|
|
|
32 *
|
|
|
33 * NOTES:
|
|
|
34 * This player handles the two above mentioned formats, as well as a third
|
|
|
35 * type, invented by Martin Fernandez <mfernan@cnba.uba.ar>, that's got a
|
|
|
36 * proper header to add title/game name information. After the header starts
|
|
|
37 * the normal IMF file in one of the two above mentioned formats.
|
|
|
38 */
|
|
|
39
|
|
|
40 #include <string.h>
|
|
|
41
|
|
|
42 #include "imf.h"
|
|
|
43 #include "database.h"
|
|
|
44
|
|
|
45 /*** public methods *************************************/
|
|
|
46
|
|
|
47 CPlayer *CimfPlayer::factory(Copl *newopl)
|
|
|
48 {
|
|
|
49 return new CimfPlayer(newopl);
|
|
|
50 }
|
|
|
51
|
|
|
52 bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp)
|
|
|
53 {
|
|
|
54 binistream *f = fp.open(filename); if(!f) return false;
|
|
|
55 unsigned long fsize, flsize, mfsize = 0;
|
|
|
56 unsigned int i;
|
|
|
57
|
|
|
58 // file validation section
|
|
|
59 {
|
|
|
60 char header[5];
|
|
|
61 int version;
|
|
|
62
|
|
|
63 f->readString(header, 5);
|
|
|
64 version = f->readInt(1);
|
|
|
65
|
|
|
66 if(strncmp(header, "ADLIB", 5) || version != 1) {
|
|
|
67 if(!fp.extension(filename, ".imf") && !fp.extension(filename, ".wlf")) {
|
|
|
68 // It's no IMF file at all
|
|
|
69 fp.close(f);
|
|
|
70 return false;
|
|
|
71 } else
|
|
|
72 f->seek(0); // It's a normal IMF file
|
|
|
73 } else {
|
|
|
74 // It's a IMF file with header
|
|
|
75 track_name = f->readString('\0');
|
|
|
76 game_name = f->readString('\0');
|
|
|
77 f->ignore(1);
|
|
|
78 mfsize = f->pos() + 2;
|
|
|
79 }
|
|
|
80 }
|
|
|
81
|
|
|
82 // load section
|
|
|
83 if(mfsize)
|
|
|
84 fsize = f->readInt(4);
|
|
|
85 else
|
|
|
86 fsize = f->readInt(2);
|
|
|
87 flsize = fp.filesize(f);
|
|
|
88 if(!fsize) { // footerless file (raw music data)
|
|
|
89 if(mfsize)
|
|
|
90 f->seek(-4, binio::Add);
|
|
|
91 else
|
|
|
92 f->seek(-2, binio::Add);
|
|
|
93 size = flsize / 4;
|
|
|
94 } else // file has got a footer
|
|
|
95 size = fsize / 4;
|
|
|
96
|
|
|
97 data = new Sdata[size];
|
|
|
98 for(i = 0; i < size; i++) {
|
|
|
99 data[i].reg = f->readInt(1); data[i].val = f->readInt(1);
|
|
|
100 data[i].time = f->readInt(2);
|
|
|
101 }
|
|
|
102
|
|
|
103 // read footer, if any
|
|
|
104 if(fsize && (fsize < flsize - 2 - mfsize)) {
|
|
|
105 unsigned long footerlen = flsize - fsize - 2 - mfsize;
|
|
|
106
|
|
|
107 footer = new char[footerlen + 1];
|
|
|
108 f->readString(footer, footerlen);
|
|
|
109 footer[footerlen] = '\0'; // Make ASCIIZ string
|
|
|
110 }
|
|
|
111
|
|
|
112 rate = getrate(f);
|
|
|
113 fp.close(f);
|
|
|
114 rewind(0);
|
|
|
115 return true;
|
|
|
116 }
|
|
|
117
|
|
|
118 bool CimfPlayer::update()
|
|
|
119 {
|
|
|
120 do {
|
|
|
121 opl->write(data[pos].reg,data[pos].val);
|
|
|
122 del = data[pos].time;
|
|
|
123 pos++;
|
|
|
124 } while(!del && pos < size);
|
|
|
125
|
|
|
126 if(pos >= size) {
|
|
|
127 pos = 0;
|
|
|
128 songend = true;
|
|
|
129 }
|
|
|
130 else timer = rate / (float)del;
|
|
|
131
|
|
|
132 return !songend;
|
|
|
133 }
|
|
|
134
|
|
|
135 void CimfPlayer::rewind(int subsong)
|
|
|
136 {
|
|
|
137 pos = 0; del = 0; timer = rate; songend = false;
|
|
|
138 opl->init(); opl->write(1,32); // go to OPL2 mode
|
|
|
139 }
|
|
|
140
|
|
|
141 std::string CimfPlayer::gettitle()
|
|
|
142 {
|
|
|
143 std::string title;
|
|
|
144
|
|
|
145 title = track_name;
|
|
|
146
|
|
|
147 if(!track_name.empty() && !game_name.empty())
|
|
|
148 title += " - ";
|
|
|
149
|
|
|
150 title += game_name;
|
|
|
151
|
|
|
152 return title;
|
|
|
153 }
|
|
|
154
|
|
|
155 /*** private methods *************************************/
|
|
|
156
|
|
|
157 float CimfPlayer::getrate(binistream *f)
|
|
|
158 {
|
|
|
159 if(!db) return 700.0f; // Database offline
|
|
|
160
|
|
|
161 f->seek(0, binio::Set);
|
|
|
162
|
|
|
163 CClockRecord *record = (CClockRecord *)db->search(CAdPlugDatabase::CKey(*f));
|
|
|
164
|
|
|
165 if(!record || record->type != CAdPlugDatabase::CRecord::ClockSpeed)
|
|
|
166 return 700.0f;
|
|
|
167 else
|
|
|
168 return record->clock;
|
|
|
169 }
|