]> 4ch.mooo.com Git - 16.git/blob - 16/adplug/adplug/src/imf.cpp
wwww~
[16.git] / 16 / adplug / adplug / src / imf.cpp
1 /*
2  * Adplug - Replayer for many OPL2/OPL3 audio file formats.
3  * Copyright (C) 1999 - 2008 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  * This player also handles a special footer format by Adam Nielsen,
40  * which has defined fields of information about the song, the author
41  * and more.
42  */
43
44 #include <string.h>
45
46 #include "imf.h"
47 #include "database.h"
48
49 /*** public methods *************************************/
50
51 CPlayer *CimfPlayer::factory(Copl *newopl)
52 {
53   return new CimfPlayer(newopl);
54 }
55
56 bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp)
57 {
58   binistream *f = fp.open(filename); if(!f) return false;
59   unsigned long fsize, flsize, mfsize = 0;
60   unsigned int i;
61
62   // file validation section
63   {
64     char        header[5];
65     int         version;
66
67     f->readString(header, 5);
68     version = f->readInt(1);
69
70     if(strncmp(header, "ADLIB", 5) || version != 1) {
71       if(!fp.extension(filename, ".imf") && !fp.extension(filename, ".wlf")) {
72         // It's no IMF file at all
73         fp.close(f);
74         return false;
75       } else
76         f->seek(0);     // It's a normal IMF file
77     } else {
78       // It's a IMF file with header
79       track_name = f->readString('\0');
80       game_name = f->readString('\0');
81       f->ignore(1);
82       mfsize = f->pos() + 2;
83     }
84   }
85
86   // load section
87   if(mfsize)
88     fsize = f->readInt(4);
89   else
90     fsize = f->readInt(2);
91   flsize = fp.filesize(f);
92   if(!fsize) {          // footerless file (raw music data)
93     if(mfsize)
94       f->seek(-4, binio::Add);
95     else
96       f->seek(-2, binio::Add);
97     size = (flsize - mfsize) / 4;
98   } else                // file has got a footer
99     size = fsize / 4;
100
101   data = new Sdata[size];
102   for(i = 0; i < size; i++) {
103     data[i].reg = f->readInt(1); data[i].val = f->readInt(1);
104     data[i].time = f->readInt(2);
105   }
106
107   // read footer, if any
108   if(fsize && (fsize < flsize - 2 - mfsize)) {
109     if(f->readInt(1) == 0x1a) {
110       // Adam Nielsen's footer format
111       track_name = f->readString();
112       author_name = f->readString();
113       remarks = f->readString();
114     } else {
115       // Generic footer
116       unsigned long footerlen = flsize - fsize - 2 - mfsize;
117
118       footer = new char[footerlen + 1];
119       f->readString(footer, footerlen);
120       footer[footerlen] = '\0'; // Make ASCIIZ string
121     }
122   }
123
124   rate = getrate(filename, fp, f);
125   fp.close(f);
126   rewind(0);
127   return true;
128 }
129
130 bool CimfPlayer::update()
131 {
132         do {
133                 opl->write(data[pos].reg,data[pos].val);
134                 del = data[pos].time;
135                 pos++;
136         } while(!del && pos < size);
137
138         if(pos >= size) {
139                 pos = 0;
140                 songend = true;
141         }
142         else timer = rate / (float)del;
143
144         return !songend;
145 }
146
147 void CimfPlayer::rewind(int subsong)
148 {
149         pos = 0; del = 0; timer = rate; songend = false;
150         opl->init(); opl->write(1,32);  // go to OPL2 mode
151 }
152
153 std::string CimfPlayer::gettitle()
154 {
155   std::string   title;
156
157   title = track_name;
158
159   if(!track_name.empty() && !game_name.empty())
160     title += " - ";
161
162   title += game_name;
163
164   return title;
165 }
166
167 std::string CimfPlayer::getdesc()
168 {
169   std::string   desc;
170
171   if(footer)
172     desc = std::string(footer);
173
174   if(!remarks.empty() && footer)
175     desc += "\n\n";
176
177   desc += remarks;
178
179   return desc;
180 }
181
182 /*** private methods *************************************/
183
184 float CimfPlayer::getrate(const std::string &filename, const CFileProvider &fp, binistream *f)
185 {
186   if(db) {      // Database available
187     f->seek(0, binio::Set);
188     CClockRecord *record = (CClockRecord *)db->search(CAdPlugDatabase::CKey(*f));
189     if (record && record->type == CAdPlugDatabase::CRecord::ClockSpeed)
190       return record->clock;
191   }
192
193   // Otherwise the database is either unavailable, or there's no entry for this file
194   if (fp.extension(filename, ".imf")) return 560.0f;
195   if (fp.extension(filename, ".wlf")) return 700.0f;
196   return 700.0f; // default speed for unknown files that aren't .IMF or .WLF
197 }