1
// -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
// vi:tw=80:et:ts=2:sts=2
4
// -----------------------------------------------------------------------
6
// This file is part of RLVM, a RealLive virtual machine clone.
8
// -----------------------------------------------------------------------
10
// Copyright (C) 2009 Elliot Glaysher
12
// This program is free software; you can redistribute it and/or modify
13
// it under the terms of the GNU General Public License as published by
14
// the Free Software Foundation; either version 3 of the License, or
15
// (at your option) any later version.
17
// This program is distributed in the hope that it will be useful,
18
// but WITHOUT ANY WARRANTY; without even the implied warranty of
19
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
// GNU General Public License for more details.
22
// You should have received a copy of the GNU General Public License
23
// along with this program; if not, write to the Free Software
24
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
26
// -----------------------------------------------------------------------
28
// Parts of this file have been copied from koedec.cc in the xclannad
29
// distribution and they are:
31
// Copyright (c) 2004-2006 Kazunori "jagarl" Ueno
32
// All rights reserved.
34
// Redistribution and use in source and binary forms, with or without
35
// modification, are permitted provided that the following conditions
37
// 1. Redistributions of source code must retain the above copyright
38
// notice, this list of conditions and the following disclaimer.
39
// 2. Redistributions in binary form must reproduce the above copyright
40
// notice, this list of conditions and the following disclaimer in the
41
// documentation and/or other materials provided with the distribution.
42
// 3. The name of the author may not be used to endorse or promote products
43
// derived from this software without specific prior written permission.
45
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
46
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
48
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
49
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
50
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
54
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56
// -----------------------------------------------------------------------
58
#include "Systems/Base/KOEPACVoiceArchive.hpp"
65
#include <boost/filesystem/path.hpp>
66
#include <boost/filesystem/fstream.hpp>
67
#include <boost/scoped_array.hpp>
69
#include "Utilities/Exception.hpp"
70
#include "xclannad/endian.hpp"
73
using boost::scoped_array;
74
using boost::shared_ptr;
76
using std::ostringstream;
77
namespace fs = boost::filesystem;
81
// KOEPAC translation tables provided by Jagarl.
83
/* 8bit -> 16bit への変換テーブル。本来は signed short だが
84
** とりあえず unsigned で扱っている
87
uint16_t koe_8bit_trans_tbl[256] = {
88
0x8000, 0x81ff, 0x83f9, 0x85ef, 0x87e1, 0x89cf, 0x8bb9, 0x8d9f,
89
0x8f81, 0x915f, 0x9339, 0x950f, 0x96e1, 0x98af, 0x9a79, 0x9c3f,
90
0x9e01, 0x9fbf, 0xa179, 0xa32f, 0xa4e1, 0xa68f, 0xa839, 0xa9df,
91
0xab81, 0xad1f, 0xaeb9, 0xb04f, 0xb1e1, 0xb36f, 0xb4f9, 0xb67f,
92
0xb801, 0xb97f, 0xbaf9, 0xbc6f, 0xbde1, 0xbf4f, 0xc0b9, 0xc21f,
93
0xc381, 0xc4df, 0xc639, 0xc78f, 0xc8e1, 0xca2f, 0xcb79, 0xccbf,
94
0xce01, 0xcf3f, 0xd079, 0xd1af, 0xd2e1, 0xd40f, 0xd539, 0xd65f,
95
0xd781, 0xd89f, 0xd9b9, 0xdacf, 0xdbe1, 0xdcef, 0xddf9, 0xdeff,
96
0xe001, 0xe0ff, 0xe1f9, 0xe2ef, 0xe3e1, 0xe4cf, 0xe5b9, 0xe69f,
97
0xe781, 0xe85f, 0xe939, 0xea0f, 0xeae1, 0xebaf, 0xec79, 0xed3f,
98
0xee01, 0xeebf, 0xef79, 0xf02f, 0xf0e1, 0xf18f, 0xf239, 0xf2df,
99
0xf381, 0xf41f, 0xf4b9, 0xf54f, 0xf5e1, 0xf66f, 0xf6f9, 0xf77f,
100
0xf801, 0xf87f, 0xf8f9, 0xf96f, 0xf9e1, 0xfa4f, 0xfab9, 0xfb1f,
101
0xfb81, 0xfbdf, 0xfc39, 0xfc8f, 0xfce1, 0xfd2f, 0xfd79, 0xfdbf,
102
0xfe01, 0xfe3f, 0xfe79, 0xfeaf, 0xfee1, 0xff0f, 0xff39, 0xff5f,
103
0xff81, 0xff9f, 0xffb9, 0xffcf, 0xffe1, 0xffef, 0xfff9, 0xffff,
104
0x0000, 0x0001, 0x0007, 0x0011, 0x001f, 0x0031, 0x0047, 0x0061,
105
0x007f, 0x00a1, 0x00c7, 0x00f1, 0x011f, 0x0151, 0x0187, 0x01c1,
106
0x01ff, 0x0241, 0x0287, 0x02d1, 0x031f, 0x0371, 0x03c7, 0x0421,
107
0x047f, 0x04e1, 0x0547, 0x05b1, 0x061f, 0x0691, 0x0707, 0x0781,
108
0x07ff, 0x0881, 0x0907, 0x0991, 0x0a1f, 0x0ab1, 0x0b47, 0x0be1,
109
0x0c7f, 0x0d21, 0x0dc7, 0x0e71, 0x0f1f, 0x0fd1, 0x1087, 0x1141,
110
0x11ff, 0x12c1, 0x1387, 0x1451, 0x151f, 0x15f1, 0x16c7, 0x17a1,
111
0x187f, 0x1961, 0x1a47, 0x1b31, 0x1c1f, 0x1d11, 0x1e07, 0x1f01,
112
0x1fff, 0x2101, 0x2207, 0x2311, 0x241f, 0x2531, 0x2647, 0x2761,
113
0x287f, 0x29a1, 0x2ac7, 0x2bf1, 0x2d1f, 0x2e51, 0x2f87, 0x30c1,
114
0x31ff, 0x3341, 0x3487, 0x35d1, 0x371f, 0x3871, 0x39c7, 0x3b21,
115
0x3c7f, 0x3de1, 0x3f47, 0x40b1, 0x421f, 0x4391, 0x4507, 0x4681,
116
0x47ff, 0x4981, 0x4b07, 0x4c91, 0x4e1f, 0x4fb1, 0x5147, 0x52e1,
117
0x547f, 0x5621, 0x57c7, 0x5971, 0x5b1f, 0x5cd1, 0x5e87, 0x6041,
118
0x61ff, 0x63c1, 0x6587, 0x6751, 0x691f, 0x6af1, 0x6cc7, 0x6ea1,
119
0x707f, 0x7261, 0x7447, 0x7631, 0x781f, 0x7a11, 0x7c07, 0x7fff
122
/* ADPCM・・・じゃないらしい。ただのDPCMのナめたテーブル。
123
** 自動生成すりゃいいんだけど256byteだったら
127
int8_t koe_ad_trans_tbl[256] = {
128
0x00, 0xff, 0x01, 0xfe, 0x02, 0xfd, 0x03, 0xfc,
129
0x04, 0xfb, 0x05, 0xfa, 0x06, 0xf9, 0x07, 0xf8,
130
0x08, 0xf7, 0x09, 0xf6, 0x0a, 0xf5, 0x0b, 0xf4,
131
0x0c, 0xf3, 0x0d, 0xf2, 0x0e, 0xf1, 0x0f, 0xf0,
132
0x10, 0xef, 0x11, 0xee, 0x12, 0xed, 0x13, 0xec,
133
0x14, 0xeb, 0x15, 0xea, 0x16, 0xe9, 0x17, 0xe8,
134
0x18, 0xe7, 0x19, 0xe6, 0x1a, 0xe5, 0x1b, 0xe4,
135
0x1c, 0xe3, 0x1d, 0xe2, 0x1e, 0xe1, 0x1f, 0xe0,
136
0x20, 0xdf, 0x21, 0xde, 0x22, 0xdd, 0x23, 0xdc,
137
0x24, 0xdb, 0x25, 0xda, 0x26, 0xd9, 0x27, 0xd8,
138
0x28, 0xd7, 0x29, 0xd6, 0x2a, 0xd5, 0x2b, 0xd4,
139
0x2c, 0xd3, 0x2d, 0xd2, 0x2e, 0xd1, 0x2f, 0xd0,
140
0x30, 0xcf, 0x31, 0xce, 0x32, 0xcd, 0x33, 0xcc,
141
0x34, 0xcb, 0x35, 0xca, 0x36, 0xc9, 0x37, 0xc8,
142
0x38, 0xc7, 0x39, 0xc6, 0x3a, 0xc5, 0x3b, 0xc4,
143
0x3c, 0xc3, 0x3d, 0xc2, 0x3e, 0xc1, 0x3f, 0xc0,
144
0x40, 0xbf, 0x41, 0xbe, 0x42, 0xbd, 0x43, 0xbc,
145
0x44, 0xbb, 0x45, 0xba, 0x46, 0xb9, 0x47, 0xb8,
146
0x48, 0xb7, 0x49, 0xb6, 0x4a, 0xb5, 0x4b, 0xb4,
147
0x4c, 0xb3, 0x4d, 0xb2, 0x4e, 0xb1, 0x4f, 0xb0,
148
0x50, 0xaf, 0x51, 0xae, 0x52, 0xad, 0x53, 0xac,
149
0x54, 0xab, 0x55, 0xaa, 0x56, 0xa9, 0x57, 0xa8,
150
0x58, 0xa7, 0x59, 0xa6, 0x5a, 0xa5, 0x5b, 0xa4,
151
0x5c, 0xa3, 0x5d, 0xa2, 0x5e, 0xa1, 0x5f, 0xa0,
152
0x60, 0x9f, 0x61, 0x9e, 0x62, 0x9d, 0x63, 0x9c,
153
0x64, 0x9b, 0x65, 0x9a, 0x66, 0x99, 0x67, 0x98,
154
0x68, 0x97, 0x69, 0x96, 0x6a, 0x95, 0x6b, 0x94,
155
0x6c, 0x93, 0x6d, 0x92, 0x6e, 0x91, 0x6f, 0x90,
156
0x70, 0x8f, 0x71, 0x8e, 0x72, 0x8d, 0x73, 0x8c,
157
0x74, 0x8b, 0x75, 0x8a, 0x76, 0x89, 0x77, 0x88,
158
0x78, 0x87, 0x79, 0x86, 0x7a, 0x85, 0x7b, 0x84,
159
0x7c, 0x83, 0x7d, 0x82, 0x7e, 0x81, 0x7f, 0x80
164
// -----------------------------------------------------------------------
166
// -----------------------------------------------------------------------
167
class KOEPACVoiceSample : public VoiceSample {
169
KOEPACVoiceSample(fs::path file, int offset, int length, int rate)
170
: stream_(std::fopen(file.external_file_string().c_str(), "rb")),
171
offset_(offset), length_(length), rate_(rate) {
174
~KOEPACVoiceSample() {
179
virtual char* decode(int* size);
188
char* KOEPACVoiceSample::decode(int* dest_len) {
189
// This function has been mildly adapted from decode_koe in xclannad. I have
190
// modified types so that it works on 64-bit systems and changed malloc()s to
191
// new[]s, as the consumer of decode() will delete [] the returned pointer.
194
scoped_array<char> table(new char[length_ * 2]);
195
fseek(stream_, offset_, 0);
196
fread(table.get(), 2, length_, stream_);
199
for (int i = 0; i < length_; i++)
200
all_len += read_little_endian_short(table.get() + i*2);
203
uint8_t* src_orig = new uint8_t[all_len];
204
uint16_t* dest_orig = new uint16_t[length_ * 0x1000 + 0x2c];
206
if (src_orig == NULL || dest_orig == NULL) return NULL;
207
uint8_t* src = src_orig;
208
fread(src, 1, all_len, stream_);
209
*dest_len = length_ * 0x400 * 4;
210
const char* header = MakeWavHeader(rate_, 2, 2, *dest_len);
211
memcpy(dest_orig, header, 0x2c);
212
uint16_t* dest = dest_orig + 0x2c;
215
for (int i = 0; i < length_; i++) {
216
int slen = read_little_endian_short(table.get() + i * 2);
217
if (slen == 0) { // do nothing
218
memset(dest, 0, 0x1000);
221
} else if (slen == 0x400) { // table 変換
222
for (int j = 0; j < 0x400; j++) {
223
write_little_endian_short((char*)(dest+0), koe_8bit_trans_tbl[*src]);
224
write_little_endian_short((char*)(dest+1), koe_8bit_trans_tbl[*src]);
231
for (int j = 0, k = 0; j < slen && k < 0x800; j++) {
234
d -= koe_ad_trans_tbl[s & 0x0f];
242
d -= koe_ad_trans_tbl[s2];
244
o2 = koe_8bit_trans_tbl[(uint8_t)d];
245
write_little_endian_short((char*)(dest+k), o2);
246
write_little_endian_short((char*)(dest+k+1), o2);
250
d -= koe_ad_trans_tbl[s & 0x0f];
252
d -= koe_ad_trans_tbl[ src[++j] ];
254
o2 = koe_8bit_trans_tbl[ (unsigned char)d];
255
write_little_endian_short((char*)(dest+k), o2);
256
write_little_endian_short((char*)(dest+k+1), o2);
265
return (char*)dest_orig;
268
// -----------------------------------------------------------------------
269
// KOEPACVoiceArchive
270
// -----------------------------------------------------------------------
271
KOEPACVoiceArchive::KOEPACVoiceArchive(fs::path file, int file_no)
272
: VoiceArchive(file_no),
277
// -----------------------------------------------------------------------
279
KOEPACVoiceArchive::~KOEPACVoiceArchive() {
282
// -----------------------------------------------------------------------
284
shared_ptr<VoiceSample> KOEPACVoiceArchive::findSample(int sample_num) {
285
std::vector<Entry>::const_iterator it =
286
std::lower_bound(entries_.begin(), entries_.end(), sample_num);
287
if (it != entries_.end()) {
288
return shared_ptr<VoiceSample>(
289
new KOEPACVoiceSample(file_, it->offset, it->length, rate_));
292
throw rlvm::Exception("Couldn't find sample in KOEPACVoiceArchive");
295
// -----------------------------------------------------------------------
297
void KOEPACVoiceArchive::readTable(boost::filesystem::path file) {
298
fs::ifstream ifs(file, ifstream::in | ifstream::binary);
301
oss << "Could not open file \"" << file << "\".";
302
throw rlvm::Exception(oss.str());
305
// Copied from koedec.cc
307
ifs.read(head, 0x20);
308
if (strncmp(head, "KOEPAC", 7) != 0) {
310
oss << file << " does not appear to be in KOEPAC format";
311
throw rlvm::Exception(oss.str());
314
int table_len = read_little_endian_int(head + 0x10);
315
entries_.reserve(table_len);
317
rate_ = read_little_endian_int(head + 0x18);
322
char* buf = new char[table_len * 8];
323
ifs.read(buf, table_len * 8);
324
for (int i = 0; i < table_len; i++) {
325
int koe_num = read_little_endian_short(buf + i * 8);
326
int length = read_little_endian_short(buf + i * 8 + 2);
327
int offset = read_little_endian_int(buf + i * 8 + 4);
328
entries_.push_back(Entry(koe_num, length, offset));
330
sort(entries_.begin(), entries_.end());