Mercurial > audlegacy-plugins
view src/adplug/core/a2m.cxx @ 3085:ac0af6b39272
Introduce new GIO plugin to buildsystem. stdio is now deprecated.
Thoughts:
- getc()/ungetc() should be moved to VFS core now
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Wed, 29 Apr 2009 20:58:36 -0500 |
parents | 75de016f8979 |
children |
line wrap: on
line source
/* * Adplug - Replayer for many OPL2/OPL3 audio file formats. * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * a2m.cpp - A2M Loader by Simon Peter <dn.tlp@gmx.net> * * NOTES: * This loader detects and loads version 1, 4, 5 & 8 files. * * version 1-4 files: * Following commands are ignored: FF1 - FF9, FAx - FEx * * version 5-8 files: * Instrument panning is ignored. Flags byte is ignored. * Following commands are ignored: Gxy, Hxy, Kxy - &xy */ #include "a2m.h" const unsigned int Ca2mLoader::MAXFREQ = 2000, Ca2mLoader::MINCOPY = ADPLUG_A2M_MINCOPY, Ca2mLoader::MAXCOPY = ADPLUG_A2M_MAXCOPY, Ca2mLoader::COPYRANGES = ADPLUG_A2M_COPYRANGES, Ca2mLoader::CODESPERRANGE = ADPLUG_A2M_CODESPERRANGE, Ca2mLoader::TERMINATE = 256, Ca2mLoader::FIRSTCODE = ADPLUG_A2M_FIRSTCODE, Ca2mLoader::MAXCHAR = FIRSTCODE + COPYRANGES * CODESPERRANGE - 1, Ca2mLoader::SUCCMAX = MAXCHAR + 1, Ca2mLoader::TWICEMAX = ADPLUG_A2M_TWICEMAX, Ca2mLoader::ROOT = 1, Ca2mLoader::MAXBUF = 42 * 1024, Ca2mLoader::MAXDISTANCE = 21389, Ca2mLoader::MAXSIZE = 21389 + MAXCOPY; const unsigned short Ca2mLoader::bitvalue[14] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; const signed short Ca2mLoader::copybits[COPYRANGES] = { 4, 6, 8, 10, 12, 14 }; const signed short Ca2mLoader::copymin[COPYRANGES] = { 0, 16, 80, 336, 1360, 5456 }; CPlayer * Ca2mLoader::factory (Copl * newopl) { return new Ca2mLoader (newopl); } bool Ca2mLoader::load (VFSFile * fd, const CFileProvider & fp) { binistream *f = fp.open (fd); if (!f) return false; char id[10]; int i, j, k, t; unsigned int l; unsigned char *org = NULL, *orgptr, flags = 0, numpats, version; unsigned long crc, alength; unsigned short len[9], *secdata, *secptr; const unsigned char convfx[16] = { 0, 1, 2, 23, 24, 3, 5, 4, 6, 9, 17, 13, 11, 19, 7, 14 }; const unsigned char convinf1[16] = { 0, 1, 2, 6, 7, 8, 9, 4, 5, 3, 10, 11, 12, 13, 14, 15 }; const unsigned char newconvfx[] = { 0, 1, 2, 3, 4, 5, 6, 23, 24, 21, 10, 11, 17, 13, 7, 19, 255, 255, 22, 25, 255, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 14, 255 }; // read header f->readString (id, 10); crc = f->readInt (4); version = f->readInt (1); numpats = f->readInt (1); // file validation section if (strncmp (id, "_A2module_", 10) || (version != 1 && version != 5 && version != 4 && version != 8)) { fp.close (f); return false; } // load, depack & convert section nop = numpats; length = 128; restartpos = 0; if (version < 5) { for (i = 0; i < 5; i++) len[i] = f->readInt (2); t = 9; } else { // version >= 5 for (i = 0; i < 9; i++) len[i] = f->readInt (2); t = 18; } // block 0 secdata = new unsigned short[len[0] / 2]; if (version == 1 || version == 5) { for (i = 0; i < len[0] / 2; i++) secdata[i] = f->readInt (2); org = new unsigned char[MAXBUF]; orgptr = org; sixdepak (secdata, org, len[0]); } else { orgptr = (unsigned char *) secdata; for (i = 0; i < len[0]; i++) orgptr[i] = f->readInt (1); } memcpy (songname, orgptr, 43); orgptr += 43; memcpy (author, orgptr, 43); orgptr += 43; memcpy (instname, orgptr, 250 * 33); orgptr += 250 * 33; for (i = 0; i < 250; i++) { // instruments inst[i].data[0] = *(orgptr + i * 13 + 10); inst[i].data[1] = *(orgptr + i * 13); inst[i].data[2] = *(orgptr + i * 13 + 1); inst[i].data[3] = *(orgptr + i * 13 + 4); inst[i].data[4] = *(orgptr + i * 13 + 5); inst[i].data[5] = *(orgptr + i * 13 + 6); inst[i].data[6] = *(orgptr + i * 13 + 7); inst[i].data[7] = *(orgptr + i * 13 + 8); inst[i].data[8] = *(orgptr + i * 13 + 9); inst[i].data[9] = *(orgptr + i * 13 + 2); inst[i].data[10] = *(orgptr + i * 13 + 3); if (version < 5) inst[i].misc = *(orgptr + i * 13 + 11); else { // version >= 5 -> OPL3 format int pan = *(orgptr + i * 13 + 11); if (pan) inst[i].data[0] |= (pan & 3) << 4; // set pan else inst[i].data[0] |= 48; // enable both speakers } inst[i].slide = *(orgptr + i * 13 + 12); } orgptr += 250 * 13; memcpy (order, orgptr, 128); orgptr += 128; bpm = *orgptr; orgptr++; initspeed = *orgptr; orgptr++; if (version >= 5) flags = *orgptr; if (version == 1 || version == 5) delete[]org; delete[]secdata; // blocks 1-4 or 1-8 alength = len[1]; for (i = 0; i < (version < 5 ? numpats / 16 : numpats / 8); i++) alength += len[i + 2]; secdata = new unsigned short[alength / 2]; if (version == 1 || version == 5) { for (l = 0; l < alength / 2; l++) secdata[l] = f->readInt (2); org = new unsigned char[MAXBUF * (numpats / (version == 1 ? 16 : 8) + 1)]; orgptr = org; secptr = secdata; orgptr += sixdepak (secptr, orgptr, len[1]); secptr += len[1] / 2; if (version == 1) { if (numpats > 16) orgptr += sixdepak (secptr, orgptr, len[2]); secptr += len[2] / 2; if (numpats > 32) orgptr += sixdepak (secptr, orgptr, len[3]); secptr += len[3] / 2; if (numpats > 48) sixdepak (secptr, orgptr, len[4]); } else { if (numpats > 8) orgptr += sixdepak (secptr, orgptr, len[2]); secptr += len[2] / 2; if (numpats > 16) orgptr += sixdepak (secptr, orgptr, len[3]); secptr += len[3] / 2; if (numpats > 24) orgptr += sixdepak (secptr, orgptr, len[4]); secptr += len[4] / 2; if (numpats > 32) orgptr += sixdepak (secptr, orgptr, len[5]); secptr += len[5] / 2; if (numpats > 40) orgptr += sixdepak (secptr, orgptr, len[6]); secptr += len[6] / 2; if (numpats > 48) orgptr += sixdepak (secptr, orgptr, len[7]); secptr += len[7] / 2; if (numpats > 56) sixdepak (secptr, orgptr, len[8]); } delete[]secdata; secdata = 0; } else { org = (unsigned char *) secdata; for (l = 0; l < alength; l++) org[l] = f->readInt (1); } if (version < 5) { for (i = 0; i < numpats; i++) for (j = 0; j < 64; j++) for (k = 0; k < 9; k++) { struct Tracks *track = &tracks[i * 9 + k][j]; unsigned char *o = &org[i * 64 * t * 4 + j * t * 4 + k * 4]; track->note = o[0] == 255 ? 127 : o[0]; track->inst = o[1]; track->command = convfx[o[2]]; track->param2 = o[3] & 0x0f; if (track->command != 14) track->param1 = o[3] >> 4; else { track->param1 = convinf1[o[3] >> 4]; if (track->param1 == 15 && !track->param2) { // convert key-off track->command = 8; track->param1 = 0; track->param2 = 0; } } if (track->command == 14) { switch (track->param1) { case 2: // convert define waveform track->command = 25; track->param1 = track->param2; track->param2 = 0xf; break; case 8: // convert volume slide up track->command = 26; track->param1 = track->param2; track->param2 = 0; break; case 9: // convert volume slide down track->command = 26; track->param1 = 0; break; } } } } else { // version >= 5 realloc_patterns (64, 64, 18); for (i = 0; i < numpats; i++) for (j = 0; j < 18; j++) for (k = 0; k < 64; k++) { struct Tracks *track = &tracks[i * 18 + j][k]; unsigned char *o = &org[i * 64 * t * 4 + j * 64 * 4 + k * 4]; track->note = o[0] == 255 ? 127 : o[0]; track->inst = o[1]; track->command = newconvfx[o[2]]; track->param1 = o[3] >> 4; track->param2 = o[3] & 0x0f; // Convert '&' command if (o[2] == 36) switch (track->param1) { case 0: // pattern delay (frames) track->command = 29; track->param1 = 0; // param2 already set correctly break; case 1: // pattern delay (rows) track->command = 14; track->param1 = 8; // param2 already set correctly break; } } } init_trackord (); if (version == 1 || version == 5) { delete[]org; } else { delete[]secdata; } // Process flags if (version >= 5) { CmodPlayer::flags |= Opl3; // All versions >= 5 are OPL3 if (flags & 8) CmodPlayer::flags |= Tremolo; // Tremolo depth if (flags & 16) CmodPlayer::flags |= Vibrato; // Vibrato depth } fp.close (f); rewind (0); return true; } float Ca2mLoader::getrefresh () { if (tempo != 18) return (float) (tempo); else return 18.2f; } /*** private methods *************************************/ void Ca2mLoader::inittree () { unsigned short i; for (i = 2; i <= TWICEMAX; i++) { dad[i] = i / 2; freq[i] = 1; } for (i = 1; i <= MAXCHAR; i++) { leftc[i] = 2 * i; rghtc[i] = 2 * i + 1; } } void Ca2mLoader::updatefreq (unsigned short a, unsigned short b) { do { freq[dad[a]] = freq[a] + freq[b]; a = dad[a]; if (a != ROOT) { if (leftc[dad[a]] == a) b = rghtc[dad[a]]; else b = leftc[dad[a]]; } } while (a != ROOT); if (freq[ROOT] == MAXFREQ) for (a = 1; a <= TWICEMAX; a++) freq[a] >>= 1; } void Ca2mLoader::updatemodel (unsigned short code) { unsigned short a = code + SUCCMAX, b, c, code1, code2; freq[a]++; if (dad[a] != ROOT) { code1 = dad[a]; if (leftc[code1] == a) updatefreq (a, rghtc[code1]); else updatefreq (a, leftc[code1]); do { code2 = dad[code1]; if (leftc[code2] == code1) b = rghtc[code2]; else b = leftc[code2]; if (freq[a] > freq[b]) { if (leftc[code2] == code1) rghtc[code2] = a; else leftc[code2] = a; if (leftc[code1] == a) { leftc[code1] = b; c = rghtc[code1]; } else { rghtc[code1] = b; c = leftc[code1]; } dad[b] = code1; dad[a] = code2; updatefreq (b, c); a = b; } a = dad[a]; code1 = dad[a]; } while (code1 != ROOT); } } unsigned short Ca2mLoader::inputcode (unsigned short bits) { unsigned short i, code = 0; for (i = 1; i <= bits; i++) { if (!ibitcount) { if (ibitcount == MAXBUF) ibufcount = 0; ibitbuffer = wdbuf[ibufcount]; ibufcount++; ibitcount = 15; } else ibitcount--; if (ibitbuffer > 0x7fff) code |= bitvalue[i - 1]; ibitbuffer <<= 1; } return code; } unsigned short Ca2mLoader::uncompress () { unsigned short a = 1; do { if (!ibitcount) { if (ibufcount == MAXBUF) ibufcount = 0; ibitbuffer = wdbuf[ibufcount]; ibufcount++; ibitcount = 15; } else ibitcount--; if (ibitbuffer > 0x7fff) a = rghtc[a]; else a = leftc[a]; ibitbuffer <<= 1; } while (a <= MAXCHAR); a -= SUCCMAX; updatemodel (a); return a; } void Ca2mLoader::decode () { unsigned short i, j, k, t, c, count = 0, dist, len, index; inittree (); c = uncompress (); while (c != TERMINATE) { if (c < 256) { obuf[obufcount] = (unsigned char) c; obufcount++; if (obufcount == MAXBUF) { output_size = MAXBUF; obufcount = 0; } buf[count] = (unsigned char) c; count++; if (count == MAXSIZE) count = 0; } else { t = c - FIRSTCODE; index = t / CODESPERRANGE; len = t + MINCOPY - index * CODESPERRANGE; dist = inputcode (copybits[index]) + len + copymin[index]; j = count; k = count - dist; if (count < dist) k += MAXSIZE; for (i = 0; i <= len - 1; i++) { obuf[obufcount] = buf[k]; obufcount++; if (obufcount == MAXBUF) { output_size = MAXBUF; obufcount = 0; } buf[j] = buf[k]; j++; k++; if (j == MAXSIZE) j = 0; if (k == MAXSIZE) k = 0; } count += len; if (count >= MAXSIZE) count -= MAXSIZE; } c = uncompress (); } output_size = obufcount; } unsigned short Ca2mLoader::sixdepak (unsigned short *source, unsigned char *dest, unsigned short size) { if ((unsigned int) size + 4096 > MAXBUF) return 0; buf = new unsigned char[MAXSIZE]; input_size = size; ibitcount = 0; ibitbuffer = 0; obufcount = 0; ibufcount = 0; wdbuf = source; obuf = dest; decode (); if (buf) { delete[]buf; buf = 0; } return output_size; }