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