1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name mikmod.cpp - MikMod support */
12 //
13 //      (c) Copyright 2004-2005 by Nehal Mistry
14 //
15 //      This program is free software; you can redistribute it and/or modify
16 //      it under the terms of the GNU General Public License as published by
17 //      the Free Software Foundation; only version 2 of the License.
18 //
19 //      This program is distributed in the hope that it will be useful,
20 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //      GNU General Public License for more details.
23 //
24 //      You should have received a copy of the GNU General Public License
25 //      along with this program; if not, write to the Free Software
26 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 //      02111-1307, USA.
28 //
29 
30 //@{
31 
32 /*----------------------------------------------------------------------------
33 --  Includes
34 ----------------------------------------------------------------------------*/
35 
36 #include "stratagus.h"
37 
38 #ifdef USE_MIKMOD // {
39 
40 #include "sound.h"
41 #include "sound_server.h"
42 #include "iolib.h"
43 
44 #include <mikmod.h>
45 
46 /*----------------------------------------------------------------------------
47 --  Declarations
48 ----------------------------------------------------------------------------*/
49 
50 struct MikModData {
51 	MODULE *MikModModule;
52 	CFile  *MikModFile;
53 };
54 
55 static CFile *CurrentFile;
56 
57 class CSampleMikMod : public CSample
58 {
59 public:
60 	~CSampleMikMod();
61 	int Read(void *buf, int len);
62 
63 	MikModData Data;
64 };
65 
66 class CSampleMikModStream : public CSample
67 {
68 public:
69 	~CSampleMikModStream();
70 	int Read(void *buf, int len);
71 
72 	MikModData Data;
73 };
74 
75 /*----------------------------------------------------------------------------
76 --  Functions
77 ----------------------------------------------------------------------------*/
78 
Seek(struct MREADER *,long off,int whence)79 static BOOL Seek(struct MREADER *, long off, int whence)
80 {
81 	return CurrentFile->seek(off, whence);
82 }
83 
Tell(struct MREADER *)84 static long Tell(struct MREADER *)
85 {
86 	return CurrentFile->tell();
87 }
88 
Read(struct MREADER *,void * buf,size_t len)89 static BOOL Read(struct MREADER *, void *buf, size_t len)
90 {
91 	return CurrentFile->read(buf, len);
92 }
93 
Get(struct MREADER *)94 static int Get(struct MREADER *)
95 {
96 	char c;
97 	CurrentFile->read(&c, 1);
98 	return c;
99 }
100 
Eof(struct MREADER *)101 static BOOL Eof(struct MREADER *)
102 {
103 	return 0;
104 }
105 
106 static MREADER MReader = { Seek, Tell, Read, Get, Eof };
107 
108 /**
109 **  Type member function to read from the module
110 **
111 **  @param buf     Buffer to write data to
112 **  @param len     Length of the buffer
113 **
114 **  @return        Number of bytes read
115 */
Read(void * buf,int len)116 int CSampleMikModStream::Read(void *buf, int len)
117 {
118 	int read;
119 
120 	// fill up the buffer
121 	read = 0;
122 	while (this->Len < SOUND_BUFFER_SIZE / 2 && Player_Active()) {
123 		memcpy(this->Buffer, this->Buffer + this->Pos, this->Len);
124 		this->Pos = 0;
125 		CurrentFile = this->Data.MikModFile;
126 		read = VC_WriteBytes((SBYTE *)this->Buffer + this->Pos,
127 							 SOUND_BUFFER_SIZE - (this->Pos + this->Len));
128 		this->Len += read;
129 	}
130 
131 	if (this->Len < len) {
132 		// EOF
133 		len = this->Len;
134 	}
135 
136 	memcpy(buf, this->Buffer + this->Pos, len);
137 	this->Len -= len;
138 	this->Pos += len;
139 
140 	return len;
141 }
142 
143 /**
144 **  Type member function to free sample
145 */
~CSampleMikModStream()146 CSampleMikModStream::~CSampleMikModStream()
147 {
148 	CurrentFile = this->Data.MikModFile;
149 
150 	Player_Stop();
151 	Player_Free(this->Data.MikModModule);
152 	MikMod_Exit();
153 	this->Data.MikModFile->close();
154 	delete this->Data.MikModFile;
155 	delete[] this->Buffer;
156 }
157 
158 /**
159 **  Type member function to read from the module
160 **
161 **  @param buf     Buffer to write data to
162 **  @param len     Length of the buffer
163 **
164 **  @return        Number of bytes read
165 */
Read(void * buf,int len)166 int CSampleMikMod::Read(void *buf, int len)
167 {
168 	if (this->Len < len) {
169 		len = this->Len;
170 	}
171 
172 	memcpy(buf, this->Buffer + this->Pos, len);
173 	this->Pos += len;
174 	this->Len -= len;
175 
176 	return len;
177 }
178 
179 /**
180 **  Type member function to free sample
181 */
~CSampleMikMod()182 CSampleMikMod::~CSampleMikMod()
183 {
184 	delete[] this->Buffer;
185 }
186 
187 
188 /**
189 **  Load MikMod.
190 **
191 **  @param name   Filename of the module.
192 **  @param flags  Unused.
193 **
194 **  @return       Returns the loaded sample.
195 */
LoadMikMod(const char * name,int flags)196 CSample *LoadMikMod(const char *name, int flags)
197 {
198 	CSample *sample;
199 	MikModData *data;
200 	MODULE *module;
201 	CFile *f;
202 	char s[256];
203 	static int registered = 0;
204 
205 	md_mode |= DMODE_STEREO | DMODE_INTERP | DMODE_SURROUND | DMODE_HQMIXER;
206 	MikMod_RegisterDriver(&drv_nos);
207 	if (!registered) {
208 		MikMod_RegisterAllLoaders();
209 		registered = 1;
210 	}
211 
212 	strcpy_s(s, sizeof(s), name);
213 	f = new CFile;
214 	if (f->open(name, CL_OPEN_READ) == -1) {
215 		delete f;
216 		return nullptr;
217 	}
218 	CurrentFile = f;
219 
220 	MikMod_Init((char *)"");
221 	module = Player_LoadGeneric(&MReader, 64, 0);
222 	if (!module) {
223 		MikMod_Exit();
224 		f->close();
225 		delete f;
226 		return nullptr;
227 	}
228 
229 	if (flags & PlayAudioStream) {
230 		CSampleMikModStream *sampleMikModStream = new CSampleMikModStream;
231 		sample = sampleMikModStream;
232 		data = &sampleMikModStream->Data;
233 	} else {
234 		CSampleMikMod *sampleMikMod = new CSampleMikMod;
235 		sample = sampleMikMod;
236 		data = &sampleMikMod->Data;
237 	}
238 	data->MikModFile = f;
239 	data->MikModModule = module;
240 	sample->Channels = 2;
241 	sample->SampleSize = 16;
242 	sample->Frequency = 44100;
243 	sample->Pos = 0;
244 	//Wyrmgus start
245 	sample->File = name;
246 	//Wyrmgus end
247 
248 	if (flags & PlayAudioStream) {
249 		sample->Len = 0;
250 		sample->Buffer = new unsigned char[SOUND_BUFFER_SIZE];
251 
252 		Player_Start(data->MikModModule);
253 	} else {
254 		int read;
255 		int pos;
256 
257 		// FIXME: need to find the correct length
258 		sample->Len = 55000000;
259 		sample->Buffer = new unsigned char[sample->Len];
260 
261 		pos = 0;
262 		Player_Start(data->MikModModule);
263 		while (Player_Active()) {
264 			read = VC_WriteBytes((SBYTE *)sample->Buffer + pos, sample->Len - pos);
265 			pos += read;
266 		}
267 
268 		Player_Stop();
269 		Player_Free(data->MikModModule);
270 		MikMod_Exit();
271 
272 		data->MikModFile->close();
273 		delete data->MikModFile;
274 	}
275 
276 	return sample;
277 }
278 
279 #endif  // } USE_MIKMOD
280 
281 //@}
282