Mercurial > audlegacy-plugins
view src/adplug/core/dmo.cxx @ 1833:15e7d3473974
paranormal: MK2
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Mon, 24 Sep 2007 13:57:28 -0500 |
parents | 4709ce4e209e |
children | 224ebe9caaac |
line wrap: on
line source
/* Adplug - Replayer for many OPL2/OPL3 audio file formats. Copyright (C) 1999 - 2004, 2006 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 dmo.cpp - TwinTeam loader by Riven the Mage <riven@ok.ru> */ /* NOTES: Panning is ignored. A WORD ist 16 bits, a DWORD is 32 bits and a BYTE is 8 bits in this context. */ #include <string.h> #include <binstr.h> #include "dmo.h" #include "debug.h" #define LOWORD(l) ((l) & 0xffff) #define HIWORD(l) ((l) >> 16) #define LOBYTE(w) ((w) & 0xff) #define HIBYTE(w) ((w) >> 8) #define ARRAY_AS_DWORD(a, i) \ ((a[i + 3] << 24) + (a[i + 2] << 16) + (a[i + 1] << 8) + a[i]) #define ARRAY_AS_WORD(a, i) ((a[i + 1] << 8) + a[i]) #define CHARP_AS_WORD(p) (((*(p + 1)) << 8) + (*p)) /* -------- Public Methods -------------------------------- */ CPlayer * CdmoLoader::factory (Copl * newopl) { return new CdmoLoader (newopl); } bool CdmoLoader::load (VFSFile * fd, const CFileProvider & fp) { int i, j; binistream *f; std::string filename (fd->uri); // check header dmo_unpacker *unpacker = new dmo_unpacker; unsigned char chkhdr[16]; f = fp.open (fd); if (!f) return false; if (!fp.extension (filename, ".dmo")) return false; f->readString ((char *) chkhdr, 16); if (!unpacker->decrypt (chkhdr, 16)) { delete unpacker; fp.close (f); return false; } // get file size long packed_length = fp.filesize (f); f->seek (0); unsigned char *packed_module = new unsigned char[packed_length]; // load file f->readString ((char *) packed_module, packed_length); fp.close (f); // decrypt unpacker->decrypt (packed_module, packed_length); long unpacked_length = 0x2000 * ARRAY_AS_WORD (packed_module, 12); unsigned char *module = new unsigned char[unpacked_length]; // unpack if (!unpacker->unpack (packed_module + 12, module, unpacked_length)) { delete unpacker; delete[]packed_module; delete[]module; return false; } delete unpacker; delete[]packed_module; // "TwinTeam" - signed ? if (memcmp (module, "TwinTeam Module File" "\x0D\x0A", 22)) { delete module; return false; } // load header binisstream uf (module, unpacked_length); uf.setFlag (binio::BigEndian, false); uf.setFlag (binio::FloatIEEE); memset (&header, 0, sizeof (s3mheader)); uf.ignore (22); // ignore DMO header ID string uf.readString (header.name, 28); uf.ignore (2); // _unk_1 header.ordnum = uf.readInt (2); header.insnum = uf.readInt (2); header.patnum = uf.readInt (2); uf.ignore (2); // _unk_2 header.is = uf.readInt (2); header.it = uf.readInt (2); memset (header.chanset, 0xFF, 32); for (i = 0; i < 9; i++) header.chanset[i] = 0x10 + i; uf.ignore (32); // ignore panning settings for all 32 channels // load orders for (i = 0; i < 256; i++) orders[i] = uf.readInt (1); orders[header.ordnum] = 0xFF; // load pattern lengths unsigned short my_patlen[100]; for (i = 0; i < 100; i++) my_patlen[i] = uf.readInt (2); // load instruments for (i = 0; i < header.insnum; i++) { memset (&inst[i], 0, sizeof (s3minst)); uf.readString (inst[i].name, 28); inst[i].volume = uf.readInt (1); inst[i].dsk = uf.readInt (1); inst[i].c2spd = uf.readInt (4); inst[i].type = uf.readInt (1); inst[i].d00 = uf.readInt (1); inst[i].d01 = uf.readInt (1); inst[i].d02 = uf.readInt (1); inst[i].d03 = uf.readInt (1); inst[i].d04 = uf.readInt (1); inst[i].d05 = uf.readInt (1); inst[i].d06 = uf.readInt (1); inst[i].d07 = uf.readInt (1); inst[i].d08 = uf.readInt (1); inst[i].d09 = uf.readInt (1); inst[i].d0a = uf.readInt (1); /* * Originally, riven sets d0b = d0a and ignores 1 byte in the * stream, but i guess this was a typo, so i read it here. */ inst[i].d0b = uf.readInt (1); } // load patterns for (i = 0; i < header.patnum; i++) { long cur_pos = uf.pos (); for (j = 0; j < 64; j++) { while (1) { unsigned char token = uf.readInt (1); if (!token) break; unsigned char chan = token & 31; // note + instrument ? if (token & 32) { unsigned char bufbyte = uf.readInt (1); pattern[i][j][chan].note = bufbyte & 15; pattern[i][j][chan].oct = bufbyte >> 4; pattern[i][j][chan].instrument = uf.readInt (1); } // volume ? if (token & 64) pattern[i][j][chan].volume = uf.readInt (1); // command ? if (token & 128) { pattern[i][j][chan].command = uf.readInt (1); pattern[i][j][chan].info = uf.readInt (1); } } } uf.seek (cur_pos + my_patlen[i]); } delete[]module; rewind (0); return true; } std::string CdmoLoader::gettype () { return std::string ("TwinTeam (packed S3M)"); } std::string CdmoLoader::getauthor () { /* All available .DMO modules written by one composer. And because all .DMO stuff was lost due to hd crash (TwinTeam guys said this), there are never(?) be another. */ return std::string ("Benjamin GERARDIN"); } /* -------- Private Methods ------------------------------- */ unsigned short CdmoLoader::dmo_unpacker::brand (unsigned short range) { unsigned short ax, bx, cx, dx; ax = LOWORD (bseed); bx = HIWORD (bseed); cx = ax; ax = LOWORD (cx * 0x8405); dx = HIWORD (cx * 0x8405); cx <<= 3; cx = (((HIBYTE (cx) + LOBYTE (cx)) & 0xFF) << 8) + LOBYTE (cx); dx += cx; dx += bx; bx <<= 2; dx += bx; dx = (((HIBYTE (dx) + LOBYTE (bx)) & 0xFF) << 8) + LOBYTE (dx); bx <<= 5; dx = (((HIBYTE (dx) + LOBYTE (bx)) & 0xFF) << 8) + LOBYTE (dx); ax += 1; if (!ax) dx += 1; // leave it that way or amd64 might get it wrong bseed = dx; bseed <<= 16; bseed += ax; return HIWORD (HIWORD (LOWORD (bseed) * range) + HIWORD (bseed) * range); } bool CdmoLoader::dmo_unpacker::decrypt (unsigned char *buf, long len) { unsigned long seed = 0; int i; bseed = ARRAY_AS_DWORD (buf, 0); for (i = 0; i < ARRAY_AS_WORD (buf, 4) + 1; i++) seed += brand (0xffff); bseed = seed ^ ARRAY_AS_DWORD (buf, 6); if (ARRAY_AS_WORD (buf, 10) != brand (0xffff)) return false; for (i = 0; i < (len - 12); i++) buf[12 + i] ^= brand (0x100); buf[len - 2] = buf[len - 1] = 0; return true; } short CdmoLoader::dmo_unpacker::unpack_block (unsigned char *ibuf, long ilen, unsigned char *obuf) { unsigned char code, par1, par2; unsigned short ax, bx, cx; unsigned char * ipos = ibuf; unsigned char * opos = obuf; // LZ77 child while (ipos - ibuf < ilen) { code = *ipos++; // 00xxxxxx: copy (xxxxxx + 1) bytes if ((code >> 6) == 0) { cx = (code & 0x3F) + 1; if (opos + cx >= oend) return -1; for (int i = 0; i < cx; i++) *opos++ = *ipos++; continue; } // 01xxxxxx xxxyyyyy: copy (Y + 3) bytes from (X + 1) if ((code >> 6) == 1) { par1 = *ipos++; ax = ((code & 0x3F) << 3) + ((par1 & 0xE0) >> 5) + 1; cx = (par1 & 0x1F) + 3; if (opos + cx >= oend) return -1; for (int i = 0; i < cx; i++) *opos++ = *(opos - ax); continue; } // 10xxxxxx xyyyzzzz: copy (Y + 3) bytes from (X + 1); copy Z bytes if ((code >> 6) == 2) { int i; par1 = *ipos++; ax = ((code & 0x3F) << 1) + (par1 >> 7) + 1; cx = ((par1 & 0x70) >> 4) + 3; bx = par1 & 0x0F; if (opos + bx + cx >= oend) return -1; for (i = 0; i < cx; i++) *opos++ = *(opos - ax); for (i = 0; i < bx; i++) *opos++ = *ipos++; continue; } // 11xxxxxx xxxxxxxy yyyyzzzz: copy (Y + 4) from X; copy Z bytes if ((code >> 6) == 3) { int i; par1 = *ipos++; par2 = *ipos++; bx = ((code & 0x3F) << 7) + (par1 >> 1); cx = ((par1 & 0x01) << 4) + (par2 >> 4) + 4; ax = par2 & 0x0F; if (opos + ax + cx >= oend) return -1; for (i = 0; i < cx; i++) *opos++ = *(opos - bx); for (i = 0; i < ax; i++) *opos++ = *ipos++; continue; } } return opos - obuf; } long CdmoLoader::dmo_unpacker::unpack (unsigned char *ibuf, unsigned char *obuf, unsigned long outputsize) { long olen = 0; unsigned short block_count = CHARP_AS_WORD (ibuf); ibuf += 2; unsigned char * block_length = ibuf; ibuf += 2 * block_count; oend = obuf + outputsize; for (int i = 0; i < block_count; i++) { unsigned short bul = CHARP_AS_WORD (ibuf); if (unpack_block (ibuf + 2, CHARP_AS_WORD (block_length) - 2, obuf) != bul) return 0; obuf += bul; olen += bul; ibuf += CHARP_AS_WORD (block_length); block_length += 2; } return olen; }