1 /*
2     The common sound font reader interface. Used by GUS, Timidity++ and WildMidi
3     backends for reading sound font configurations.
4 
5     The FileInterface is also used by streaming sound formats.
6 
7     Copyright (C) 2019 Christoph Oelckers
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU Lesser General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 */
23 
24 
25 #pragma once
26 #include <stdio.h>
27 #include <string.h>
28 #include <vector>
29 #include <string>
30 
31 #if defined _WIN32 && !defined _WINDOWS_	// only define this if windows.h is not included.
32 	// I'd rather not include Windows.h for just this. This header is not supposed to pollute everything it touches.
33 extern "C" __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned CodePage, unsigned long  dwFlags, const char* lpMultiByteStr, int cbMultiByte, const wchar_t* lpWideCharStr, int cchWideChar);
34 enum
35 {
36 	CP_UTF8 = 65001
37 };
38 #endif
39 
40 namespace MusicIO
41 {
42 
43 //==========================================================================
44 //
45 // This class defines a common file wrapper interface which allows these
46 // libraries to work with any kind of file access API, e.g. stdio (provided below),
47 // Win32, POSIX, iostream or custom implementations (like GZDoom's FileReader.)
48 //
49 //==========================================================================
50 
51 struct FileInterface
52 {
53 	std::string filename;
54 	long length = -1;
55 
56 	// It's really too bad that the using code requires long instead of size_t.
57 	// Fortunately 2GB files are unlikely to come by here.
58 protected:
59 	//
~FileInterfaceFileInterface60 	virtual ~FileInterface() {}
61 public:
62 	virtual char* gets(char* buff, int n) = 0;
63 	virtual long read(void* buff, int32_t size) = 0;
64 	virtual long seek(long offset, int whence) = 0;
65 	virtual long tell() = 0;
closeFileInterface66 	virtual void close()
67 	{
68 		delete this;
69 	}
70 
filelengthFileInterface71 	long filelength()
72 	{
73 		if (length == -1)
74 		{
75 			long pos = tell();
76 			seek(0, SEEK_END);
77 			length = tell();
78 			seek(pos, SEEK_SET);
79 		}
80 		return length;
81 	}
82 };
83 
84 //==========================================================================
85 //
86 // Inplementation of the FileInterface for stdio's FILE*.
87 //
88 //==========================================================================
89 
90 struct StdioFileReader : public FileInterface
91 {
92 	FILE* f = nullptr;
93 
~StdioFileReaderStdioFileReader94 	~StdioFileReader()
95 	{
96 		if (f) fclose(f);
97 	}
getsStdioFileReader98 	char* gets(char* buff, int n) override
99 	{
100 		if (!f) return nullptr;
101 		return fgets(buff, n, f);
102 	}
readStdioFileReader103 	long read(void* buff, int32_t size) override
104 	{
105 		if (!f) return 0;
106 		return (long)fread(buff, 1, size, f);
107 	}
seekStdioFileReader108 	long seek(long offset, int whence) override
109 	{
110 		if (!f) return 0;
111 		return fseek(f, offset, whence);
112 	}
tellStdioFileReader113 	long tell() override
114 	{
115 		if (!f) return 0;
116 		return ftell(f);
117 	}
118 };
119 
120 
121 //==========================================================================
122 //
123 // Inplementation of the FileInterface for a block of memory
124 //
125 //==========================================================================
126 
127 struct MemoryReader : public FileInterface
128 {
129 	const uint8_t *mData;
130 	long mLength;
131 	long mPos;
132 
MemoryReaderMemoryReader133 	MemoryReader(const uint8_t *data, long length)
134 	 : mData(data), mLength(length), mPos(0)
135 	{
136 	}
137 
getsMemoryReader138 	char* gets(char* strbuf, int len) override
139 	{
140 		if (len > mLength - mPos) len = mLength - mPos;
141 		if (len <= 0) return NULL;
142 
143 		char *p = strbuf;
144 		while (len > 1)
145 		{
146 			if (mData[mPos] == 0)
147 			{
148 				mPos++;
149 				break;
150 			}
151 			if (mData[mPos] != '\r')
152 			{
153 				*p++ = mData[mPos];
154 				len--;
155 				if (mData[mPos] == '\n')
156 				{
157 					mPos++;
158 					break;
159 				}
160 			}
161 			mPos++;
162 		}
163 		if (p == strbuf) return nullptr;
164 		*p++ = 0;
165 		return strbuf;
166 	}
readMemoryReader167 	long read(void* buff, int32_t size) override
168 	{
169 		long len = long(size);
170 		if (len > mLength - mPos) len = mLength - mPos;
171 		if (len < 0) len = 0;
172 		memcpy(buff, mData + mPos, len);
173 		mPos += len;
174 		return len;
175 	}
seekMemoryReader176 	long seek(long offset, int whence) override
177 	{
178 		switch (whence)
179 		{
180 		case SEEK_CUR:
181 			offset += mPos;
182 			break;
183 
184 		case SEEK_END:
185 			offset += mLength;
186 			break;
187 
188 		}
189 		if (offset < 0 || offset > mLength) return -1;
190 		mPos = offset;
191 		return 0;
192 	}
tellMemoryReader193 	long tell() override
194 	{
195 		return mPos;
196 	}
197 protected:
MemoryReaderMemoryReader198 	MemoryReader() {}
199 };
200 
201 //==========================================================================
202 //
203 // Inplementation of the FileInterface for an std::vector owned by the reader
204 //
205 //==========================================================================
206 
207 struct VectorReader : public MemoryReader
208 {
209 	std::vector<uint8_t> mVector;
210 
211 	template <class getFunc>
VectorReaderVectorReader212 	VectorReader(getFunc getter)	// read contents to a buffer and return a reader to it
213 	{
214 		getter(mVector);
215 		mData = mVector.data();
216 		mLength = (long)mVector.size();
217 		mPos = 0;
218 	}
VectorReaderVectorReader219 	VectorReader(const uint8_t* data, size_t size)
220 	{
221 		mVector.resize(size);
222 		memcpy(mVector.data(), data, size);
223 	}
224 };
225 
226 
227 //==========================================================================
228 //
229 // The following two functions are needed to allow using UTF-8 in the file interface.
230 // fopen on Windows is only safe for ASCII.
231 //
232 //==========================================================================
233 
234 #ifdef _WIN32
wideString(const char * filename)235 inline std::wstring wideString(const char *filename)
236 {
237 	std::wstring filePathW;
238 	auto len = strlen(filename);
239 	filePathW.resize(len);
240 	int newSize = MultiByteToWideChar(CP_UTF8, 0, filename, (int)len, const_cast<wchar_t*>(filePathW.c_str()), (int)len);
241 	filePathW.resize(newSize);
242 	return filePathW;
243 }
244 #endif
245 
utf8_fopen(const char * filename,const char * mode)246 inline FILE* utf8_fopen(const char* filename, const char *mode)
247 {
248 #ifndef _WIN32
249 	return fopen(filename, mode);
250 #else
251 	auto fn = wideString(filename);
252 	auto mo = wideString(mode);
253 	return _wfopen(fn.c_str(), mo.c_str());
254 #endif
255 
256 }
257 
fileExists(const char * fn)258 inline bool fileExists(const char *fn)
259 {
260 	FILE *f = utf8_fopen(fn, "rb");
261 	if (!f) return false;
262 	fclose(f);
263 	return true;
264 }
265 
266 //==========================================================================
267 //
268 // This class providea a framework for reading sound fonts.
269 // This is needed when the sound font data is not read from
270 // the file system. e.g. zipped GUS patch sets.
271 //
272 //==========================================================================
273 
274 class SoundFontReaderInterface
275 {
276 protected:
~SoundFontReaderInterface()277 	virtual ~SoundFontReaderInterface() {}
278 public:
279 	virtual struct FileInterface* open_file(const char* fn) = 0;
280 	virtual void add_search_path(const char* path) = 0;
close()281 	virtual void close() { delete this; }
282 };
283 
284 
285 //==========================================================================
286 //
287 // A basic sound font reader for reading data from the file system.
288 //
289 //==========================================================================
290 
291 class FileSystemSoundFontReader : public SoundFontReaderInterface
292 {
293 protected:
294 	std::vector<std::string> mPaths;
295 	std::string mBaseFile;
296 	bool mAllowAbsolutePaths;
297 
IsAbsPath(const char * name)298 	bool IsAbsPath(const char *name)
299 	{
300 		if (name[0] == '/' || name[0] == '\\') return true;
301 		#ifdef _WIN32
302 			/* [A-Za-z]: (for Windows) */
303 			if (isalpha(name[0]) && name[1] == ':') return true;
304 		#endif /* _WIN32 */
305 		return 0;
306 	}
307 
308 public:
309 	FileSystemSoundFontReader(const char *configfilename, bool allowabs = false)
310 	{
311 		// Note that this does not add the directory the base file is in to the search path!
312 		// The caller of this has to do it themselves!
313 		mBaseFile = configfilename;
314 		mAllowAbsolutePaths = allowabs;
315 	}
316 
open_file(const char * fn)317 	struct FileInterface* open_file(const char* fn) override
318 	{
319 		FILE *f = nullptr;
320 		std::string fullname;
321 		if (!fn)
322 		{
323 			f = utf8_fopen(mBaseFile.c_str(), "rt");
324 			fullname = mBaseFile;
325 		}
326 		else
327 		{
328 			if (!IsAbsPath(fn))
329 			{
330 				for(int i = (int)mPaths.size()-1; i>=0; i--)
331 				{
332 					fullname = mPaths[i] + fn;
333 					f = utf8_fopen(fullname.c_str(), "rt");
334 					break;
335 				}
336 			}
337 			if (!f) f = fopen(fn, "rt");
338 		}
339 		if (!f) return nullptr;
340 		auto tf = new StdioFileReader;
341 		tf->f = f;
342 		tf->filename = fullname;
343 		return tf;
344 	}
345 
add_search_path(const char * path)346 	void add_search_path(const char* path) override
347 	{
348 		std::string p  = path;
349 		if (p.back() != '/' && p.back() != '\\') p += '/';	// always let it end with a slash.
350 		mPaths.push_back(p);
351 	}
352 };
353 
354 //==========================================================================
355 //
356 // This reader exists to trick Timidity config readers into accepting
357 // a loose SF2 file by providing a fake config pointing to the given file.
358 //
359 //==========================================================================
360 
361 class SF2Reader : public FileSystemSoundFontReader
362 {
363 	std::string mMainConfigForSF2;
364 
365 public:
SF2Reader(const char * filename)366     SF2Reader(const char *filename)
367 		: FileSystemSoundFontReader(filename)
368 	{
369 		mMainConfigForSF2 = "soundfont \"" + mBaseFile + "\"\n";
370 	}
371 
open_file(const char * fn)372 	struct FileInterface* open_file(const char* fn) override
373 	{
374 		if (fn == nullptr)
375 		{
376 			return new MemoryReader((uint8_t*)mMainConfigForSF2.c_str(), (long)mMainConfigForSF2.length());
377 		}
378 		else return FileSystemSoundFontReader::open_file(fn);
379 	}
380 };
381 
382 MusicIO::SoundFontReaderInterface* ClientOpenSoundFont(const char* name, int type);
383 
384 }
385 
386