1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/ultima8/misc/pent_include.h"
24
25 #include "ultima/ultima8/audio/music_flex.h"
26 #include "common/memstream.h"
27
28 namespace Ultima {
29 namespace Ultima8 {
30
MusicFlex(Common::SeekableReadStream * rs)31 MusicFlex::MusicFlex(Common::SeekableReadStream *rs) : Archive(rs) {
32 memset(_info, 0, sizeof(SongInfo *) * 128);
33 _songs = new XMidiData *[_count];
34 memset(_songs, 0, sizeof(XMidiData *) * _count);
35 loadSongInfo();
36 }
37
~MusicFlex()38 MusicFlex::~MusicFlex() {
39 uint32 i;
40 for (i = 0; i < 128; i++) {
41 delete _info[i];
42 }
43 for (i = 0; i < _count; i++) {
44 delete _songs[i];
45 }
46 delete [] _songs;
47 }
48
SongInfo()49 MusicFlex::SongInfo::SongInfo() : _numMeasures(0), _loopJump(0) {
50 memset(_filename, 0, 17);
51 memset(_transitions, 0, 128 * sizeof(int *));
52 }
53
~SongInfo()54 MusicFlex::SongInfo::~SongInfo() {
55 for (int i = 0; i < 128; i++) {
56 delete [] _transitions[i];
57 }
58 }
59
getXMidi(uint32 index)60 MusicFlex::XMidiData *MusicFlex::getXMidi(uint32 index) {
61 if (index >= _count)
62 return nullptr;
63 cache(index);
64 return _songs[index];
65 }
66
getSongInfo(uint32 index) const67 const MusicFlex::SongInfo *MusicFlex::getSongInfo(uint32 index) const {
68 if (index > 127)
69 return nullptr;
70 return _info[index];
71 }
72
cache(uint32 index)73 void MusicFlex::cache(uint32 index) {
74 if (index >= _count) return;
75 uint32 size;
76 uint8 *data = getRawObject(index, &size);
77 if (!data) {
78 // Note: multiple sorcerer scenes (such as MALCHIR::03F2)
79 // request track 122, which is blank in the Gold Edition
80 // music flex.
81 warning("Unable to cache song %d from sound/music.flx", index);
82 return;
83 }
84 _songs[index] = new XMidiData(data, size);
85 }
86
uncache(uint32 index)87 void MusicFlex::uncache(uint32 index) {
88 if (index >= _count) return;
89 delete _songs[index];
90 _songs[index] = nullptr;
91 }
92
isCached(uint32 index) const93 bool MusicFlex::isCached(uint32 index) const {
94 if (index >= _count) return false;
95 return (_songs[index] != nullptr);
96 }
97
getAdlibTimbres()98 Common::SeekableReadStream *MusicFlex::getAdlibTimbres() {
99 uint32 size;
100 const uint8 *data = getRawObject(259, &size);
101 return new Common::MemoryReadStream(data, size, DisposeAfterUse::YES);
102 }
103
loadSongInfo()104 void MusicFlex::loadSongInfo() {
105 uint32 size;
106 const uint8 *buf = getRawObject(0, &size);
107
108 if (!buf || !size) {
109 error("Unable to load song info from sound/music.flx");
110 }
111 Common::MemoryReadStream ds(buf, size);
112 Std::string line;
113
114 // Read first section till we hit a #
115 for (;;) {
116 line = ds.readLine();
117
118 // We have hit the end of the section
119 if (line.at(0) == '#') break;
120
121 Std::string::size_type begIdx, endIdx;
122
123 // Find the first not space, which will get us the name
124 begIdx = line.findFirstNotOf(' ');
125 endIdx = line.findFirstOf(' ', begIdx);
126 Std::string name = line.substr(begIdx, endIdx - begIdx);
127
128 // Now find the first not space after the name, which will get us the num
129 begIdx = line.findFirstNotOf(' ', endIdx);
130 endIdx = line.findFirstOf(' ', begIdx);
131 int num = line.at(begIdx);
132
133 // Now number of measures
134 begIdx = line.findFirstNotOf(' ', endIdx);
135 endIdx = line.findFirstOf(' ', begIdx);
136 int measures = atoi(line.substr(begIdx, endIdx - begIdx).c_str());
137
138 // Now finally _loopJump
139 begIdx = line.findFirstNotOf(' ', endIdx);
140 endIdx = line.findFirstOf(' ', begIdx);
141 int loopJump = atoi(line.substr(begIdx, endIdx - begIdx).c_str());
142
143 // Uh oh
144 if (num < 0 || num > 127)
145 error("Invalid Section 1 song _info data. num out of range");
146
147 if (_info[num])
148 error("Invalid Section 1 song _info data. num already defined");
149
150 _info[num] = new SongInfo();
151
152 strncpy(_info[num]->_filename, name.c_str(), 16);
153 _info[num]->_numMeasures = measures;
154 _info[num]->_loopJump = loopJump;
155 };
156
157 // Read 'Section2', or more like skip it, since it's only trans.xmi
158 // Read first section till we hit a #
159 for (;;) {
160 line = ds.readLine();
161
162 // We have hit the end of the section
163 if (line.at(0) == '#') break;
164 }
165
166 // Skip 'Section3'
167 for (;;) {
168 line = ds.readLine();
169
170 // We have hit the end of the section
171 if (line.at(0) == '#') break;
172 }
173
174 // Read 'Section4' (trans _info)
175 for (;;) {
176 line = ds.readLine();
177
178 // We have hit the end of the section
179 if (line.at(0) == '#') break;
180
181 Std::string::size_type begIdx, endIdx;
182
183 // Get 'from' name
184 begIdx = line.findFirstNotOf(' ');
185 endIdx = line.findFirstOf(' ', begIdx);
186 Std::string from = line.substr(begIdx, endIdx - begIdx);
187
188 // Get 'to' name
189 begIdx = line.findFirstNotOf(' ', endIdx);
190 endIdx = line.findFirstOf(' ', begIdx);
191 Std::string to = line.substr(begIdx, endIdx - begIdx);
192
193 // Find index of from name
194 int fi;
195 for (fi = 0; fi < 128; fi++) {
196 if (_info[fi] && from == _info[fi]->_filename) break;
197 }
198
199 if (fi == 128)
200 error("Invalid Section 4 song _info data. Unable to find 'from' index (%s)", from.c_str());
201
202 // Find index of to name
203 int ti;
204 for (ti = 0; ti < 128; ti++) {
205 if (_info[ti] && to == _info[ti]->_filename) break;
206 }
207
208 if (ti == 128)
209 error("Invalid Section 4 song _info data. Unable to find 'to' index (%s)", to.c_str());
210
211 // Allocate Transition _info
212 _info[fi]->_transitions[ti] = new int[_info[fi]->_numMeasures];
213
214 // Now attempt to read the trans _info for the
215 for (int m = 0; m < _info[fi]->_numMeasures; m++) {
216 // Get trans _info name
217 begIdx = line.findFirstNotOf(' ', endIdx);
218 endIdx = line.findFirstOf(' ', begIdx);
219
220 if (begIdx == Std::string::npos)
221 error("Invalid Section 4 song _info data. Unable to read _transitions for all measures");
222
223 Std::string trans = line.substr(begIdx, endIdx - begIdx);
224 const char *str = trans.c_str();
225
226 int num = 0;
227
228 // Overlayed
229 if (*str == '!')
230 num = 0 - atoi(str + 1);
231 else
232 num = atoi(str);
233
234 _info[fi]->_transitions[ti][m] = num;
235 }
236 }
237
238 // Skip all remaining sections
239
240 delete[] buf;
241 }
242
243 } // End of namespace Ultima8
244 } // End of namespace Ultima
245