UNPKG

opll2opl

Version:
544 lines (499 loc) 15.1 kB
type ChipType = { value: number; name: string; }; export type Chips = { sn76489?: { clock: number; feedback: number; shiftRegisterWidth: number; flags: number; }; ym2413?: { clock: number }; ym2612?: { clock: number; chipType: ChipType }; ym2151?: { clock: number; chipType: ChipType }; segaPcm?: { clock: number; interfaceRegister: number }; rf5c68?: { clock: number }; ym2203?: { clock: number; ssgFlags: number }; ym2608?: { clock: number; ssgFlags: number }; ym2610?: { clock: number; chipType: ChipType }; ym3812?: { clock: number }; ym3526?: { clock: number }; y8950?: { clock: number }; ymf262?: { clock: number }; ymf278b?: { clock: number }; ymf271?: { clock: number }; ymz280b?: { clock: number }; rf5c164?: { clock: number }; pwm?: { clock: number }; ay8910?: { clock: number; chipType: ChipType; flags: number; }; gameBoyDmg?: { clock: number }; nesApu?: { clock: number; fds: boolean }; multiPcm?: { clock: number }; upd7759?: { clock: number }; okim6258?: { clock: number; flags: number }; okim6295?: { clock: number }; k051649?: { clock: number }; k054539?: { clock: number; flags: number }; huc6280?: { clock: number }; c140?: { clock: number; chipType: ChipType }; k053260?: { clock: number }; pokey?: { clock: number }; qsound?: { clock: number }; scsp?: { clock: number }; wonderSwan?: { clock: number }; vsu?: { clock: number }; saa1099?: { clock: number }; es5503?: { clock: number; numberOfChannels: number }; es5505?: { clock: number; chipType: ChipType; numberOfChannels: number }; x1_010?: { clock: number }; c352?: { clock: number; clockDivider: number }; ga20?: { clock: number }; }; export type VGMHeader = { version: number; offsets: { eof: number; gd3: number; loop: number; data: number; extraHeader: number; }; samples: { total: number; loop: number; }; rate: number; chips: Chips; loopModifier: number; loopBase: number; volumeModifier: number; }; function getParamsCommon(d: DataView, clockIndex: number) { const clock = d.getUint32(clockIndex, true); if (clock) { return { clock }; } return undefined; } function getParamsCommonWithFlags( d: DataView, clockIndex: number, flagsIndex: number ) { const clock = d.getUint32(clockIndex, true); if (clock) { return { clock, flags: d.getUint8(flagsIndex) }; } return undefined; } function getParamsSn76489(d: DataView) { const obj = getParamsCommonWithFlags(d, 0x0c, 0x2b); if (obj) { return { ...obj, feedback: d.getUint16(0x28, true), shiftRegisterWidth: d.getUint8(0x2a), }; } return undefined; } function getParamsSegaPcm(d: DataView) { const obj = getParamsCommon(d, 0x38); if (obj) { return { ...obj, interfaceRegister: d.getUint32(0x3c, true), }; } return undefined; } function getParamsYm2151(d: DataView) { const obj = getParamsCommon(d, 0x30); if (obj) { const t = obj.clock >> 31; return { ...obj, clock: obj.clock & 0x7fffffff, chipType: { value: t, name: t ? "YM2164" : "YM2151", }, }; } return undefined; } function getParamsYm2203(d: DataView) { const obj = getParamsCommon(d, 0x44); if (obj) { return { ...obj, ssgFlags: d.getUint8(0x7a), }; } return undefined; } function getParamsYm2608(d: DataView) { const obj = getParamsCommon(d, 0x48); if (obj) { return { ...obj, ssgFlags: d.getUint8(0x7b), }; } return undefined; } function getParamsYm2610(d: DataView) { const obj = getParamsCommon(d, 0x48); if (obj) { const t = d.getUint8(0x4c); return { ...obj, clock: obj.clock & 0x7fffffff, chipType: { value: t, name: t ? "YM2610" : "YM2610B", }, }; } return undefined; } function getParamsYm2612(d: DataView) { const obj = getParamsCommon(d, 0x2c); if (obj) { const t = obj.clock >> 31; return { ...obj, clock: obj.clock & 0x7fffffff, chipType: { value: t, name: t ? "YM3438" : "YM2612", }, }; } return undefined; } function getParamsNesApu(d: DataView) { const obj = getParamsCommon(d, 0x84); if (obj) { const fds = obj.clock >> 31 !== 0; return { ...obj, clock: obj.clock & 0x7fffffff, fds, }; } return undefined; } function getParamsEs5503(d: DataView) { const obj = getParamsCommon(d, 0xcc); if (obj) { return { ...obj, numberOfChannels: d.getUint8(0xd4), }; } return undefined; } function getParamsEs5505(d: DataView) { const obj = getParamsCommon(d, 0xd0); if (obj) { const t = obj.clock >> 30; return { ...obj, clock: obj.clock & 0x7fffffff, chipType: { value: t, name: t ? "ES5506" : "ES5505", }, numberOfChannels: d.getUint8(0xd5), }; } return undefined; } function getParamsAy8910(d: DataView) { const obj = getParamsCommon(d, 0x74); if (obj) { const t = d.getUint8(0x78); const flags = d.getUint8(0x79); return { ...obj, chipType: { value: t, name: ((t: number) => { switch (t) { case 0x00: return "AY8910"; case 0x01: return "AY8912"; case 0x02: return "AY8913"; case 0x03: return "AY8930"; case 0x10: return "YM2149"; case 0x11: return "YM3439"; case 0x12: return "YMZ284"; case 0x13: return "YMZ294"; default: return "UNKNOWN"; } })(t), }, flags, }; } return undefined; } function getParamsC140(d: DataView) { const obj = getParamsCommon(d, 0xa8); if (obj) { const t = d.getUint8(0x96); return { ...obj, chipType: { value: t, name: ((t: number) => { switch (t) { case 0x00: return "C140, Namco System 2"; case 0x01: return "C140, Namco System 21"; case 0x02: return "219 ASIC, Namco NA-1/2"; default: return "UNKNOWN"; } })(t), }, }; } return undefined; } function getParamsC352(d: DataView) { const obj = getParamsCommon(d, 0xdc); if (obj) { return { ...obj, clockDivider: d.getUint8(0xd6), }; } return undefined; } export function parse(data: ArrayBuffer): VGMHeader { const d = new DataView(data); const version = d.getUint32(0x08, true); const chips: Chips = { sn76489: getParamsSn76489(d), ym2413: getParamsCommon(d, 0x10), }; const eof = d.getUint32(0x04, true); const gd3 = d.getUint32(0x14, true); const loop = d.getUint32(0x1c, true); const props = { version, offsets: { eof: eof ? 0x04 + eof : 0, gd3: gd3 ? 0x14 + gd3 : 0, loop: loop ? 0x1c + loop : 0, data: 0x40, extraHeader: 0, }, samples: { total: d.getUint32(0x18, true), loop: d.getUint32(0x20, true), }, rate: d.getUint32(0x24, true), chips, loopModifier: 0, loopBase: 0, volumeModifier: 0, }; if (version >= 0x110) { chips.ym2612 = getParamsYm2612(d); chips.ym2151 = getParamsYm2151(d); } if (version >= 0x150) { props.offsets.data = 0x34 + d.getUint32(0x34, true); } if (version >= 0x151) { chips.segaPcm = getParamsSegaPcm(d); chips.rf5c68 = getParamsCommon(d, 0x40); chips.ym2203 = getParamsYm2203(d); chips.ym2608 = getParamsYm2608(d); chips.ym2610 = getParamsYm2610(d); chips.ym3812 = getParamsCommon(d, 0x50); chips.ym3526 = getParamsCommon(d, 0x54); chips.y8950 = getParamsCommon(d, 0x58); chips.ymf262 = getParamsCommon(d, 0x5c); chips.ymf278b = getParamsCommon(d, 0x60); chips.ymf271 = getParamsCommon(d, 0x64); chips.ymz280b = getParamsCommon(d, 0x68); chips.rf5c164 = getParamsCommon(d, 0x6c); chips.pwm = getParamsCommon(d, 0x70); chips.ay8910 = getParamsAy8910(d); props.loopModifier = d.getUint8(0x7f); } if (version >= 0x160) { props.volumeModifier = d.getUint8(0x7c); props.loopBase = d.getUint8(0x7e); } if (version >= 0x161) { chips.gameBoyDmg = getParamsCommon(d, 0x80); chips.nesApu = getParamsNesApu(d); chips.multiPcm = getParamsCommon(d, 0x88); chips.upd7759 = getParamsCommon(d, 0x8c); chips.okim6258 = getParamsCommonWithFlags(d, 0x90, 0x94); chips.c140 = getParamsC140(d); chips.okim6295 = getParamsCommon(d, 0x98); chips.k051649 = getParamsCommon(d, 0x9c); chips.k054539 = getParamsCommonWithFlags(d, 0xa0, 0x95); chips.huc6280 = getParamsCommon(d, 0xa4); chips.k053260 = getParamsCommon(d, 0xac); chips.pokey = getParamsCommon(d, 0xb0); chips.qsound = getParamsCommon(d, 0xb4); } if (version >= 0x170) { const v = d.getUint32(0xbc, true); props.offsets.extraHeader = v ? 0xbc + v : 0; } if (version >= 0x171) { chips.scsp = getParamsCommon(d, 0xb8); chips.wonderSwan = getParamsCommon(d, 0xc0); chips.vsu = getParamsCommon(d, 0xc4); chips.saa1099 = getParamsCommon(d, 0xc8); chips.es5505 = getParamsEs5505(d); chips.es5503 = getParamsEs5503(d); chips.x1_010 = getParamsCommon(d, 0xd8); chips.c352 = getParamsC352(d); chips.ga20 = getParamsCommon(d, 0xe0); } return props; } export function build(header: VGMHeader): ArrayBuffer { const buf = new ArrayBuffer(header.offsets.data); const view = new DataView(buf); const chips = header.chips; view.setUint32(0x00, 0x206d6756, true); // "Vgm " view.setUint32(0x04, header.offsets.eof - 0x04, true); view.setUint32(0x08, header.version, true); view.setUint32(0x0c, chips.sn76489 ? chips.sn76489.clock : 0, true); view.setUint32(0x10, chips.ym2413 ? chips.ym2413.clock : 0, true); view.setUint32(0x14, header.offsets.gd3, true); view.setUint32(0x18, header.samples.total, true); view.setUint32( 0x1c, 0x1c <= header.offsets.loop ? header.offsets.loop - 0x1c : 0, true ); view.setUint32(0x20, header.samples.loop, true); if (0x101 <= header.version) { view.setUint32(0x24, header.rate, true); } if (0x110 <= header.version) { view.setUint16(0x28, chips.sn76489 ? chips.sn76489.feedback : 0, true); view.setUint8(0x2a, chips.sn76489 ? chips.sn76489.shiftRegisterWidth : 0); view.setUint32(0x2c, chips.ym2612 ? chips.ym2612.clock : 0, true); view.setUint32(0x30, chips.ym2151 ? chips.ym2151.clock : 0, true); } if (0x150 <= header.version) { view.setUint32(0x34, header.offsets.data - 0x34, true); } if (0x151 <= header.version) { view.setUint8(0x2b, chips.sn76489 ? chips.sn76489.flags : 0); view.setUint32(0x38, chips.segaPcm ? chips.segaPcm.clock : 0, true); view.setUint32( 0x3c, chips.segaPcm ? chips.segaPcm.interfaceRegister : 0, true ); view.setUint32(0x40, chips.rf5c68 ? chips.rf5c68.clock : 0, true); view.setUint32(0x44, chips.ym2203 ? chips.ym2203.clock : 0, true); view.setUint32(0x48, chips.ym2608 ? chips.ym2608.clock : 0, true); view.setUint32( 0x4c, chips.ym2610 ? chips.ym2610.clock | (chips.ym2610.chipType.value << 31) : 0, true ); view.setUint32(0x50, chips.ym3812 ? chips.ym3812.clock : 0, true); view.setUint32(0x54, chips.ym3526 ? chips.ym3526.clock : 0, true); view.setUint32(0x58, chips.y8950 ? chips.y8950.clock : 0, true); view.setUint32(0x5c, chips.ymf262 ? chips.ymf262.clock : 0, true); view.setUint32(0x60, chips.ymf278b ? chips.ymf278b.clock : 0, true); view.setUint32(0x64, chips.ymf271 ? chips.ymf271.clock : 0, true); view.setUint32(0x68, chips.ymz280b ? chips.ymz280b.clock : 0, true); view.setUint32(0x6c, chips.rf5c164 ? chips.rf5c164.clock : 0, true); view.setUint32(0x70, chips.pwm ? chips.pwm.clock : 0, true); view.setUint32(0x74, chips.ay8910 ? chips.ay8910.clock : 0, true); view.setUint8(0x78, chips.ay8910 ? chips.ay8910.chipType.value : 0); view.setUint8(0x79, chips.ay8910 ? chips.ay8910.flags : 0); view.setUint8(0x7a, chips.ym2203 ? chips.ym2203.ssgFlags : 0); view.setUint8(0x7b, chips.ym2608 ? chips.ym2608.ssgFlags : 0); view.setUint8(0x7f, header.loopModifier); } if (0x160 <= header.version) { view.setUint8(0x7c, header.volumeModifier); view.setUint8(0x7d, 0); view.setUint8(0x7e, header.loopBase); } if (0x161 <= header.version) { view.setUint32(0x80, chips.gameBoyDmg ? chips.gameBoyDmg.clock : 0, true); view.setUint32( 0x84, chips.nesApu ? chips.nesApu.clock | (chips.nesApu.fds ? 1 << 31 : 0) : 0, true ); view.setUint32(0x88, chips.multiPcm ? chips.multiPcm.clock : 0, true); view.setUint32(0x8c, chips.upd7759 ? chips.upd7759.clock : 0, true); view.setUint32(0x90, chips.okim6258 ? chips.okim6258.clock : 0, true); view.setUint8(0x94, chips.okim6258 ? chips.okim6258.flags : 0); view.setUint8(0x95, chips.k054539 ? chips.k054539.flags : 0); view.setUint8(0x96, chips.c140 ? chips.c140.chipType.value : 0); view.setUint8(0x97, 0); view.setUint32(0x98, chips.okim6295 ? chips.okim6295.clock : 0, true); view.setUint32(0x9c, chips.k051649 ? chips.k051649.clock : 0, true); view.setUint32(0xa0, chips.k054539 ? chips.k054539.clock : 0, true); view.setUint32(0xa4, chips.huc6280 ? chips.huc6280.clock : 0, true); view.setUint32(0xa8, chips.c140 ? chips.c140.clock : 0, true); view.setUint32(0xac, chips.k053260 ? chips.k053260.clock : 0, true); view.setUint32(0xb0, chips.pokey ? chips.pokey.clock : 0, true); view.setUint32(0xb4, chips.qsound ? chips.qsound.clock : 0, true); } if (0x170 <= header.version) { view.setUint32(0xbc, header.offsets.extraHeader, true); } if (0x171 <= header.version) { view.setUint32(0xb8, chips.scsp ? chips.scsp.clock : 0, true); view.setUint32(0xc0, chips.wonderSwan ? chips.wonderSwan.clock : 0, true); view.setUint32(0xc4, chips.vsu ? chips.vsu.clock : 0, true); view.setUint32(0xc8, chips.saa1099 ? chips.saa1099.clock : 0, true); view.setUint32(0xcc, chips.es5503 ? chips.es5503.clock : 0, true); view.setUint32( 0xd0, chips.es5505 ? chips.es5505.clock | (chips.es5505.chipType.value << 31) : 0, true ); view.setUint8(0xd4, chips.es5503 ? chips.es5503.numberOfChannels : 0); view.setUint8(0xd5, chips.es5505 ? chips.es5505.numberOfChannels : 0); view.setUint8(0xd6, chips.c352 ? chips.c352.clockDivider : 0); view.setUint32(0xd8, chips.x1_010 ? chips.x1_010.clock : 0, true); view.setUint32(0xdc, chips.c352 ? chips.c352.clock : 0, true); view.setUint32(0xe0, chips.ga20 ? chips.ga20.clock : 0, true); } return buf; } export function clone(obj: any) { return JSON.parse(JSON.stringify(obj)); } export default VGMHeader;