1 /*
2  * ALURE  OpenAL utility library
3  * Copyright (c) 2009-2010 by Chris Robinson.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include "config.h"
25 
26 #include "main.h"
27 
28 #include <string.h>
29 #include <assert.h>
30 
31 #include <istream>
32 
33 #include <libmodplug/modplug.h>
34 
35 
36 #ifdef DYNLOAD
37 static void *mod_handle;
38 #define MAKE_FUNC(x) static typeof(x)* p##x
39 MAKE_FUNC(ModPlug_Load);
40 MAKE_FUNC(ModPlug_Unload);
41 MAKE_FUNC(ModPlug_Read);
42 MAKE_FUNC(ModPlug_SeekOrder);
43 #undef MAKE_FUNC
44 
45 #define ModPlug_Load pModPlug_Load
46 #define ModPlug_Unload pModPlug_Unload
47 #define ModPlug_Read pModPlug_Read
48 #define ModPlug_SeekOrder pModPlug_SeekOrder
49 #else
50 #define mod_handle 1
51 #endif
52 
53 
54 struct modStream : public alureStream {
55 private:
56     ModPlugFile *modFile;
57     int lastOrder;
58 
59 public:
60 #ifdef DYNLOAD
InitmodStream61     static void Init()
62     {
63 #ifdef _WIN32
64 #define MODPLUG_LIB "libmodplug.dll"
65 #elif defined(__APPLE__)
66 #define MODPLUG_LIB "libmodplug.1.dylib"
67 #else
68 #define MODPLUG_LIB "libmodplug.so.1"
69 #endif
70         mod_handle = OpenLib(MODPLUG_LIB);
71         if(!mod_handle) return;
72 
73         LOAD_FUNC(mod_handle, ModPlug_Load);
74         LOAD_FUNC(mod_handle, ModPlug_Unload);
75         LOAD_FUNC(mod_handle, ModPlug_Read);
76         LOAD_FUNC(mod_handle, ModPlug_SeekOrder);
77     }
DeinitmodStream78     static void Deinit()
79     {
80         if(mod_handle)
81             CloseLib(mod_handle);
82         mod_handle = NULL;
83     }
84 #else
85     static void Init() { }
86     static void Deinit() { }
87 #endif
88 
IsValidmodStream89     virtual bool IsValid()
90     { return modFile != NULL; }
91 
GetFormatmodStream92     virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
93     {
94         *fmt = AL_FORMAT_STEREO16;
95         *frequency = 44100;
96         *blockalign = 2 * sizeof(ALshort);
97         return true;
98     }
99 
GetDatamodStream100     virtual ALuint GetData(ALubyte *data, ALuint bytes)
101     {
102         int ret = ModPlug_Read(modFile, data, bytes);
103         if(ret < 0) return 0;
104         return ret;
105     }
106 
RewindmodStream107     virtual bool Rewind()
108     { return SetOrder(lastOrder); }
109 
SetOrdermodStream110     virtual bool SetOrder(ALuint order)
111     {
112         std::vector<char> data(16384);
113         ALuint total = 0;
114         while(1)
115         {
116             fstream->read(&data[total], data.size()-total);
117             if(fstream->gcount() == 0) break;
118             total += fstream->gcount();
119             data.resize(total*2);
120         }
121         data.resize(total);
122 
123         ModPlugFile *newMod = ModPlug_Load(&data[0], data.size());
124         if(!newMod)
125         {
126             SetError("Could not reload data");
127             return false;
128         }
129         ModPlug_Unload(modFile);
130         modFile = newMod;
131 
132         // There seems to be no way to tell if the seek succeeds
133         ModPlug_SeekOrder(modFile, order);
134         lastOrder = order;
135 
136         return true;
137     }
138 
modStreammodStream139     modStream(std::istream *_fstream)
140       : alureStream(_fstream), modFile(NULL), lastOrder(0)
141     {
142         if(!mod_handle) return;
143 
144         std::vector<char> data(1024);
145         ALuint total = 0;
146 
147         fstream->read(&data[total], data.size()-total);
148         total += fstream->gcount();
149         if(total < 32) return;
150 
151         if(memcmp(&data[0], "Extended Module: ", 17) == 0 || /* XM */
152            (data[28] == 0x1A && data[29] == 0x10) || /* S3M */
153            memcmp(&data[0], "IMPM", 4) == 0) /* IT */
154         {
155             while(1)
156             {
157                 data.resize(total*2);
158                 fstream->read(&data[total], data.size()-total);
159                 if(fstream->gcount() == 0) break;
160                 total += fstream->gcount();
161             }
162             data.resize(total);
163 
164             modFile = ModPlug_Load(&data[0], data.size());
165         }
166     }
167 
~modStreammodStream168     virtual ~modStream()
169     {
170         if(modFile)
171             ModPlug_Unload(modFile);
172         modFile = NULL;
173     }
174 };
175 // Priority = -1, because mod loading can find false-positives
176 static DecoderDecl<modStream,-1> modStream_decoder;
alure_init_modplug(void)177 Decoder &alure_init_modplug(void)
178 { return modStream_decoder; }
179