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 * VGMTrans (c) 2002-2019
24 * Licensed under the zlib license,
25 * refer to the included VGMTrans_LICENSE.txt file
26 */
27
28 #include "common/debug.h"
29 #include "common/scummsys.h"
30 #include "vab.h"
31 #include "psxspu.h"
32
33 using namespace std;
34
Vab(RawFile * file,uint32 offset)35 Vab::Vab(RawFile *file, uint32 offset) : VGMInstrSet(file, offset) {}
36
~Vab()37 Vab::~Vab() {}
38
GetHeaderInfo()39 bool Vab::GetHeaderInfo() {
40 uint32 nEndOffset = GetEndOffset();
41 uint32 nMaxLength = nEndOffset - _dwOffset;
42
43 if (nMaxLength < 0x20) {
44 return false;
45 }
46
47 _name = "VAB";
48
49 VGMHeader *vabHdr = AddHeader(_dwOffset, 0x20, "VAB Header");
50 vabHdr->AddSimpleItem(_dwOffset + 0x00, 4, "ID");
51 vabHdr->AddSimpleItem(_dwOffset + 0x04, 4, "Version");
52 vabHdr->AddSimpleItem(_dwOffset + 0x08, 4, "VAB ID");
53 vabHdr->AddSimpleItem(_dwOffset + 0x0c, 4, "Total Size");
54 vabHdr->AddSimpleItem(_dwOffset + 0x10, 2, "Reserved");
55 vabHdr->AddSimpleItem(_dwOffset + 0x12, 2, "Number of Programs");
56 vabHdr->AddSimpleItem(_dwOffset + 0x14, 2, "Number of Tones");
57 vabHdr->AddSimpleItem(_dwOffset + 0x16, 2, "Number of VAGs");
58 vabHdr->AddSimpleItem(_dwOffset + 0x18, 1, "Master Volume");
59 vabHdr->AddSimpleItem(_dwOffset + 0x19, 1, "Master Pan");
60 vabHdr->AddSimpleItem(_dwOffset + 0x1a, 1, "Bank Attributes 1");
61 vabHdr->AddSimpleItem(_dwOffset + 0x1b, 1, "Bank Attributes 2");
62 vabHdr->AddSimpleItem(_dwOffset + 0x1c, 4, "Reserved");
63
64 return true;
65 }
66
GetInstrPointers()67 bool Vab::GetInstrPointers() {
68 uint32 nEndOffset = GetEndOffset();
69
70 uint32 offProgs = _dwOffset + 0x20;
71 uint32 offToneAttrs = offProgs + (16 * 128);
72
73 uint16 numPrograms = GetShort(_dwOffset + 0x12);
74 uint16 numVAGs = GetShort(_dwOffset + 0x16);
75
76 uint32 offVAGOffsets = offToneAttrs + (32 * 16 * numPrograms);
77
78 VGMHeader *progsHdr = AddHeader(offProgs, 16 * 128, "Program Table");
79 VGMHeader *toneAttrsHdr = AddHeader(offToneAttrs, 32 * 16, "Tone Attributes Table");
80
81 if (numPrograms > 128) {
82 debug("Too many programs %x, offset %x", numPrograms, _dwOffset);
83 return false;
84 }
85 if (numVAGs > 255) {
86 debug("Too many VAGs %x, offset %x", numVAGs, _dwOffset);
87 return false;
88 }
89
90 // Load each instruments.
91 //
92 // Rule 1. Valid instrument pointers are not always sequentially located from 0 to (numProgs -
93 // 1). Number of tones can be 0. That's an empty instrument. We need to ignore it. See Clock
94 // Tower PSF for example.
95 //
96 // Rule 2. Do not load programs more than number of programs. Even if a program table value is
97 // provided. Otherwise an out-of-order access can be caused in Tone Attributes Table. See the
98 // swimming event BGM of Aitakute... ~your smiles in my heart~ for example. (github issue #115)
99 uint32 numProgramsLoaded = 0;
100 for (uint32 progIndex = 0; progIndex < 128 && numProgramsLoaded < numPrograms; progIndex++) {
101 uint32 offCurrProg = offProgs + (progIndex * 16);
102 uint32 offCurrToneAttrs = offToneAttrs + (uint32) (_aInstrs.size() * 32 * 16);
103
104 if (offCurrToneAttrs + (32 * 16) > nEndOffset) {
105 break;
106 }
107
108 uint8 numTonesPerInstr = GetByte(offCurrProg);
109 if (numTonesPerInstr > 32) {
110 debug("Program %x contains too many tones (%d)", progIndex, numTonesPerInstr);
111 } else if (numTonesPerInstr != 0) {
112 VabInstr *newInstr = new VabInstr(this, offCurrToneAttrs, 0x20 * 16, 0, progIndex);
113 _aInstrs.push_back(newInstr);
114 newInstr->_tones = GetByte(offCurrProg + 0);
115
116 VGMHeader *progHdr = progsHdr->AddHeader(offCurrProg, 0x10, "Program");
117 progHdr->AddSimpleItem(offCurrProg + 0x00, 1, "Number of Tones");
118 progHdr->AddSimpleItem(offCurrProg + 0x01, 1, "Volume");
119 progHdr->AddSimpleItem(offCurrProg + 0x02, 1, "Priority");
120 progHdr->AddSimpleItem(offCurrProg + 0x03, 1, "Mode");
121 progHdr->AddSimpleItem(offCurrProg + 0x04, 1, "Pan");
122 progHdr->AddSimpleItem(offCurrProg + 0x05, 1, "Reserved");
123 progHdr->AddSimpleItem(offCurrProg + 0x06, 2, "Attribute");
124 progHdr->AddSimpleItem(offCurrProg + 0x08, 4, "Reserved");
125 progHdr->AddSimpleItem(offCurrProg + 0x0c, 4, "Reserved");
126
127 newInstr->_masterVol = GetByte(offCurrProg + 0x01);
128
129 toneAttrsHdr->_unLength = offCurrToneAttrs + (32 * 16) - offToneAttrs;
130
131 numProgramsLoaded++;
132 }
133 }
134
135 if ((offVAGOffsets + 2 * 256) <= nEndOffset) {
136 char name[256];
137 Common::Array<SizeOffsetPair> vagLocations;
138 uint32 totalVAGSize = 0;
139 VGMHeader *vagOffsetHdr = AddHeader(offVAGOffsets, 2 * 256, "VAG Pointer Table");
140
141 uint32 vagStartOffset = GetShort(offVAGOffsets) * 8;
142 vagOffsetHdr->AddSimpleItem(offVAGOffsets, 2, "VAG Size /8 #0");
143 totalVAGSize = vagStartOffset;
144
145 for (uint32 i = 0; i < numVAGs; i++) {
146 uint32 vagOffset;
147 uint32 vagSize;
148
149 if (i == 0) {
150 vagOffset = vagStartOffset;
151 vagSize = GetShort(offVAGOffsets + (i + 1) * 2) * 8;
152 } else {
153 vagOffset = vagStartOffset + vagLocations[i - 1].offset + vagLocations[i - 1].size;
154 vagSize = GetShort(offVAGOffsets + (i + 1) * 2) * 8;
155 }
156
157 snprintf(name, sizeof(name), "VAG Size /8 #%u", i + 1);
158 vagOffsetHdr->AddSimpleItem(offVAGOffsets + (i + 1) * 2, 2, name);
159
160 if (vagOffset + vagSize <= nEndOffset) {
161 vagLocations.push_back(SizeOffsetPair(vagOffset, vagSize));
162 totalVAGSize += vagSize;
163 } else {
164 debug("VAG #%d at %x with size %x) is invalid", i + 1, vagOffset, vagSize);
165 }
166 }
167 _unLength = (offVAGOffsets + 2 * 256) - _dwOffset;
168
169 // single VAB file?
170 uint32 offVAGs = offVAGOffsets + 2 * 256;
171 if (_dwOffset == 0 && vagLocations.size() != 0) {
172 // load samples as well
173 PSXSampColl *newSampColl =
174 new PSXSampColl(this, offVAGs, totalVAGSize, vagLocations);
175 if (newSampColl->LoadVGMFile()) {
176 this->_sampColl = newSampColl;
177 } else {
178 delete newSampColl;
179 }
180 }
181 }
182
183 return true;
184 }
185
186 // ********
187 // VabInstr
188 // ********
189
VabInstr(VGMInstrSet * instrSet,uint32 offset,uint32 length,uint32 theBank,uint32 theInstrNum,const Common::String & name)190 VabInstr::VabInstr(VGMInstrSet *instrSet, uint32 offset, uint32 length, uint32 theBank,
191 uint32 theInstrNum, const Common::String &name)
192 : VGMInstr(instrSet, offset, length, theBank, theInstrNum, name), _tones(0), _masterVol(127) {}
193
~VabInstr()194 VabInstr::~VabInstr() {}
195
LoadInstr()196 bool VabInstr::LoadInstr() {
197 int8 numRgns = _tones;
198 for (int i = 0; i < numRgns; i++) {
199 VabRgn *rgn = new VabRgn(this, _dwOffset + i * 0x20);
200 if (!rgn->LoadRgn()) {
201 delete rgn;
202 return false;
203 }
204 _aRgns.push_back(rgn);
205 }
206 return true;
207 }
208
209 // ******
210 // VabRgn
211 // ******
212
VabRgn(VabInstr * instr,uint32 offset)213 VabRgn::VabRgn(VabInstr *instr, uint32 offset) : _ADSR1(0), _ADSR2(0), VGMRgn(instr, offset) {}
214
LoadRgn()215 bool VabRgn::LoadRgn() {
216 VabInstr *instr = (VabInstr *) _parInstr;
217 _unLength = 0x20;
218
219 AddGeneralItem(_dwOffset, 1, "Priority");
220 AddGeneralItem(_dwOffset + 1, 1, "Mode (use reverb?)");
221 AddVolume((GetByte(_dwOffset + 2) * instr->_masterVol) / (127.0 * 127.0), _dwOffset + 2, 1);
222 AddPan(GetByte(_dwOffset + 3), _dwOffset + 3);
223 AddUnityKey(GetByte(_dwOffset + 4), _dwOffset + 4);
224 AddGeneralItem(_dwOffset + 5, 1, "Pitch Tune");
225 AddKeyLow(GetByte(_dwOffset + 6), _dwOffset + 6);
226 AddKeyHigh(GetByte(_dwOffset + 7), _dwOffset + 7);
227 AddGeneralItem(_dwOffset + 8, 1, "Vibrato Width");
228 AddGeneralItem(_dwOffset + 9, 1, "Vibrato Time");
229 AddGeneralItem(_dwOffset + 10, 1, "Portamento Width");
230 AddGeneralItem(_dwOffset + 11, 1, "Portamento Holding Time");
231 AddGeneralItem(_dwOffset + 12, 1, "Pitch Bend Min");
232 AddGeneralItem(_dwOffset + 13, 1, "Pitch Bend Max");
233 AddGeneralItem(_dwOffset + 14, 1, "Reserved");
234 AddGeneralItem(_dwOffset + 15, 1, "Reserved");
235 AddGeneralItem(_dwOffset + 16, 2, "ADSR1");
236 AddGeneralItem(_dwOffset + 18, 2, "ADSR2");
237 AddGeneralItem(_dwOffset + 20, 2, "Parent Program");
238 AddSampNum(GetShort(_dwOffset + 22) - 1, _dwOffset + 22, 2);
239 AddGeneralItem(_dwOffset + 24, 2, "Reserved");
240 AddGeneralItem(_dwOffset + 26, 2, "Reserved");
241 AddGeneralItem(_dwOffset + 28, 2, "Reserved");
242 AddGeneralItem(_dwOffset + 30, 2, "Reserved");
243 _ADSR1 = GetShort(_dwOffset + 16);
244 _ADSR2 = GetShort(_dwOffset + 18);
245 if ((int) _sampNum < 0)
246 _sampNum = 0;
247
248 if (_keyLow > _keyHigh) {
249 debug("Low key higher than high key %d > %d (at %x)", _keyLow, _keyHigh, _dwOffset);
250 return false;
251 }
252
253 // gocha: AFAIK, the valid range of pitch is 0-127. It must not be negative.
254 // If it exceeds 127, driver clips the value and it will become 127. (In Hokuto no Ken, at
255 // least) I am not sure if the interpretation of this value depends on a driver or VAB version.
256 // The following code takes the byte as signed, since it could be a typical extended
257 // implementation.
258 int8 ft = (int8) GetByte(_dwOffset + 5);
259 double cents = ft * 100.0 / 128.0;
260 SetFineTune((int16) cents);
261
262 PSXConvADSR<VabRgn>(this, _ADSR1, _ADSR2, false);
263 return true;
264 }
265