protected override void LoadIndex(GameInfo game) { const byte encByte = 0x69; Directory = ServiceLocator.FileStorage.GetDirectoryName(game.Path); using (var file = ServiceLocator.FileStorage.OpenFileRead(game.Path)) { var br = new BinaryReader(new XorStream(file,encByte)); while (br.BaseStream.Position < br.BaseStream.Length) { var block = ToTag(br.ReadBytes(4)); br.ReadUInt32BigEndian(); // size switch (block) { case "RNAM": ReadRoomNames(br); break; case "MAXS": ReadMaxSizes(br); break; case "DROO": var rooms = ReadResTypeList(br); RoomResources = new ReadOnlyCollection<Resource>(rooms); break; case "DSCR": var scripts = ReadResTypeList(br); ScriptResources = new ReadOnlyCollection<Resource>(scripts); break; case "DSOU": var sounds = ReadResTypeList(br); SoundResources = new ReadOnlyCollection<Resource>(sounds); break; case "DCOS": var costumes = ReadResTypeList(br); CostumeResources = new ReadOnlyCollection<Resource>(costumes); break; case "DCHR": var charset = ReadResTypeList(br); CharsetResources = new ReadOnlyCollection<Resource>(charset); break; case "DOBJ": ReadDirectoryOfObjects(br); break; default: // Console.Error.WriteLine("Unknown block {0}", block); break; } } } }
internal UImage(string fileName) { using(var reader = new BinaryReader(File.OpenRead(fileName))) { reader.ReadBytes(8); // magic and CRC, already checked Timestamp = (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) + TimeSpan.FromSeconds(reader.ReadInt32BigEndian())).ToLocalTime(); Size = reader.ReadUInt32BigEndian(); LoadAddress = reader.ReadUInt32BigEndian(); EntryPoint = reader.ReadUInt32BigEndian(); CRC = reader.ReadUInt32BigEndian(); OperatingSystem = (OS)reader.ReadByte(); Architecture = (Architecture)reader.ReadByte(); Type = (ImageType)reader.ReadByte(); Compression = (CompressionType)reader.ReadByte(); var nameAsBytes = reader.ReadBytes(32); Name = Encoding.UTF8.GetString(nameAsBytes.Reverse().SkipWhile(x => x == 0).Reverse().ToArray()); image = reader.ReadBytes((int)Size); } }
public static UImageResult TryLoad(string fileName, out UImage uImage) { uImage = null; if(new FileInfo(fileName).Length < 64) { return UImageResult.NotUImage; } byte[] headerForCrc; using(var reader = new BinaryReader(File.OpenRead(fileName))) { headerForCrc = reader.ReadBytes(64); // we need to zero crc part for(var i = 4; i < 8; i++) { headerForCrc[i] = 0; } } using(var reader = new BinaryReader(File.OpenRead(fileName))) { var magic = reader.ReadUInt32BigEndian(); if(magic != Magic) { return UImageResult.NotUImage; } var crc = reader.ReadUInt32BigEndian(); if(crc != GzipCrc32(headerForCrc)) { return UImageResult.BadChecksum; } reader.ReadBytes(22); var imageType = (ImageType)reader.ReadByte(); if(!Enum.IsDefined(typeof(ImageType), imageType)) { return UImageResult.NotSupportedImageType; } // TODO: check CRC of the header uImage = new UImage(fileName); return UImageResult.OK; } }
public override bool AppendData(BinaryReader b, int size) { if (_dataSize == -1) { Debug.Assert(size > 8); var imus_type = System.Text.Encoding.UTF8.GetString(b.ReadBytes(4)); /*uint32 imus_size =*/ b.ReadUInt32BigEndian(); // if (imus_type != "iMUS") // Console.Error.WriteLine("Invalid Chunk for imuse_channel"); size -= 8; _tbufferSize = size; Debug.Assert(_tbufferSize != 0); _tbuffer = b.ReadBytes(size); _dataSize = -2; } else { if (_tbuffer.Length != 0) { var old = _tbuffer; int new_size = size + _tbufferSize; _tbuffer = new byte[new_size]; Array.Copy(old, _tbuffer, _tbufferSize); b.BaseStream.Read(_tbuffer, _tbufferSize, size); _tbufferSize += size; } else { _tbufferSize = size; _tbuffer = new byte[_tbufferSize]; b.BaseStream.Read(_tbuffer, 0, size); } } ProcessBuffer(); _srbufferSize = _sbufferSize; if (_sbuffer.Length != 0 && _bitsize == 12) Decode(); return true; }
public override bool AppendData(BinaryReader b, int size) { if (_dataSize == -1) { Debug.Assert(size > 8); var saud_type = System.Text.Encoding.UTF8.GetString(b.ReadBytes(4)); /*uint32 saud_size =*/ b.ReadUInt32BigEndian(); if (saud_type != "SAUD") throw new NotSupportedException(string.Format("Invalid Chunk for SaudChannel : {0}", saud_type)); size -= 8; _dataSize = -2; } if (_tbuffer.Length != 0) { var old = _tbuffer; _tbuffer = new byte[_tbufferSize + size]; Array.Copy(old, _tbuffer, _tbufferSize); old = null; Array.Copy(b.ReadBytes(size), 0, _tbuffer, _tbufferSize, size); _tbufferSize += size; } else { _tbufferSize = size; _tbuffer = b.ReadBytes(_tbufferSize); } if (_keepSize) { _sbufferSize = _tbufferSize; _sbuffer = _tbuffer; _tbufferSize = 0; _tbuffer = new byte[0]; } else { ProcessBuffer(); } return true; }
void ReadMap() { var br = new BinaryReader(_stream); _stream.Seek(_mapOffset + 22, SeekOrigin.Begin); _resMap.resAttr = br.ReadUInt16BigEndian(); _resMap.typeOffset = br.ReadUInt16BigEndian(); _resMap.nameOffset = br.ReadUInt16BigEndian(); _resMap.numTypes = br.ReadUInt16BigEndian(); _resMap.numTypes++; _stream.Seek(_mapOffset + _resMap.typeOffset + 2, SeekOrigin.Begin); _resTypes = new ResType[_resMap.numTypes]; for (int i = 0; i < _resMap.numTypes; i++) { _resTypes[i].id = br.ReadUInt32BigEndian(); _resTypes[i].items = br.ReadUInt16BigEndian(); _resTypes[i].offset = br.ReadUInt16BigEndian(); _resTypes[i].items++; Debug.WriteLine("resType: <{0}> items: {1} offset: {2} (0x{3:X2})", _resTypes[i].id, _resTypes[i].items, _resTypes[i].offset, _resTypes[i].offset); } _resLists = new Resource[_resMap.numTypes][]; for (int i = 0; i < _resMap.numTypes; i++) { _resLists[i] = new Resource[_resTypes[i].items]; _stream.Seek(_resTypes[i].offset + _mapOffset + _resMap.typeOffset, SeekOrigin.Begin); for (int j = 0; j < _resTypes[i].items; j++) { var resPtr = _resLists[i][j] = new Resource(); resPtr.id = br.ReadUInt16BigEndian(); resPtr.nameOffset = (short)br.ReadUInt16BigEndian(); resPtr.dataOffset = br.ReadUInt32BigEndian(); br.ReadUInt32BigEndian(); resPtr.attr = (byte)(resPtr.dataOffset >> 24); resPtr.dataOffset &= 0xFFFFFF; } for (int j = 0; j < _resTypes[i].items; j++) { if (_resLists[i][j].nameOffset != -1) { _stream.Seek(_resLists[i][j].nameOffset + _mapOffset + _resMap.nameOffset, SeekOrigin.Begin); byte len = br.ReadByte(); _resLists[i][j].name = br.ReadBytes(len).GetText(); } } } }
public bool LoadInstrument(Stream stream) { var br = new BinaryReader(stream); ushort soundType = br.ReadUInt16BigEndian(); if (soundType != 1) { Debug.WriteLine("Player_Mac::loadInstrument: Unsupported sound type {0}", soundType); return false; } var typeCount = br.ReadUInt16BigEndian(); if (typeCount != 1) { Debug.WriteLine("Player_Mac::loadInstrument: Unsupported data type count %d", typeCount); return false; } var dataType = br.ReadUInt16BigEndian(); if (dataType != 5) { Debug.WriteLine("Player_Mac::loadInstrument: Unsupported data type %d", dataType); return false; } br.ReadUInt32BigEndian(); // initialization option var cmdCount = br.ReadUInt16BigEndian(); if (cmdCount != 1) { Debug.WriteLine("Player_Mac::loadInstrument: Unsupported command count %d", cmdCount); return false; } var command = br.ReadUInt16BigEndian(); if (command != 0x8050 && command != 0x8051) { Debug.WriteLine("Player_Mac::loadInstrument: Unsupported command 0x%04X", command); return false; } br.ReadUInt16BigEndian(); // 0 var soundHeaderOffset = br.ReadUInt32BigEndian(); stream.Seek(soundHeaderOffset, SeekOrigin.Begin); var soundDataOffset = br.ReadUInt32BigEndian(); var size = br.ReadUInt32BigEndian(); var rate = br.ReadUInt32BigEndian() >> 16; var loopStart = br.ReadUInt32BigEndian(); var loopEnd = br.ReadUInt32BigEndian(); byte encoding = br.ReadByte(); byte baseFreq = br.ReadByte(); if (encoding != 0) { Debug.WriteLine("Player_Mac::loadInstrument: Unsupported encoding %d", encoding); return false; } stream.Seek(soundDataOffset, SeekOrigin.Current); var data = br.ReadBytes((int)size); _instrument._data = data; _instrument._size = size; _instrument._rate = rate; _instrument._loopStart = loopStart; _instrument._loopEnd = loopEnd; _instrument._baseFreq = baseFreq; return true; }
/** * Read resource from the MacBinary file * @param typeID FourCC of the type * @param resID Resource ID to fetch * @return Pointer to a SeekableReadStream with loaded resource */ public Stream GetResource(uint typeID, ushort resID) { var br = new BinaryReader(_stream); int typeNum = -1; int resNum = -1; for (int i = 0; i < _resMap.numTypes; i++) if (_resTypes[i].id == typeID) { typeNum = i; break; } if (typeNum == -1) return null; for (int i = 0; i < _resTypes[typeNum].items; i++) if (_resLists[typeNum][i].id == resID) { resNum = i; break; } if (resNum == -1) return null; _stream.Seek(_dataOffset + _resLists[typeNum][resNum].dataOffset, SeekOrigin.Begin); var len = (int)br.ReadUInt32BigEndian(); // Ignore resources with 0 length if (len == 0) return null; return new MemoryStream(br.ReadBytes(len)); }
bool Load(Stream stream) { var br = new BinaryReader(stream); if (_mode == Mode.None) return false; stream.Seek(_resForkOffset, SeekOrigin.Begin); _dataOffset = (uint)(br.ReadUInt32BigEndian() + _resForkOffset); _mapOffset = (uint)(br.ReadUInt32BigEndian() + _resForkOffset); _dataLength = br.ReadUInt32BigEndian(); _mapLength = br.ReadUInt32BigEndian(); // do sanity check if (stream.Position == stream.Length || _dataOffset >= stream.Length || _mapOffset >= stream.Length || _dataLength + _mapLength > stream.Length) { _resForkOffset = -1; _mode = Mode.None; return false; } Debug.WriteLine("got header: data {0} [{1}] map {2} [{3}]", _dataOffset, _dataLength, _mapOffset, _mapLength); _stream = stream; ReadMap(); return true; }
public override void LoadMusic(byte[] data) { input = new MemoryStream(data); UnloadMusic(); int midiType; var isGmf = false; var br = new BinaryReader(input); var sig = br.ReadBytes(4); input.Seek(0, SeekOrigin.Begin); if (AreEquals(sig, "RIFF")) { // Skip the outer RIFF header. input.Seek(8, SeekOrigin.Current); } if (AreEquals(sig, "MThd")) { // SMF with MTHd information. input.Seek(4, SeekOrigin.Current); var len = br.ReadUInt32BigEndian(); if (len != 6) { throw new InvalidOperationException(string.Format("MThd length 6 expected but found {0}", len)); } br.ReadByte(); //? midiType = br.ReadByte(); // Verify that this MIDI either is a Type 2 // or has only 1 track. We do not support // multitrack Type 1 files. NumTracks = br.ReadUInt16BigEndian(); if (midiType > 2 /*|| (midiType < 2 && _numTracks > 1)*/) { throw new InvalidOperationException(string.Format("No support for a Type {0} MIDI with {1} tracks", midiType, NumTracks)); } PulsesPerQuarterNote = br.ReadUInt16BigEndian(); } else if (AreEquals(sig, "GMF\x1")) { // Older GMD/MUS file with no header info. // Assume 1 track, 192 PPQN, and no MTrk headers. isGmf = true; midiType = 0; NumTracks = 1; PulsesPerQuarterNote = 192; // 'GMD\x1' + 3 bytes of useless (translate: unknown) information input.Seek(7, SeekOrigin.Current); } else { throw new InvalidOperationException(string.Format("Expected MThd or GMD header but found '{0}{1}{2}{3}' instead", sig[0], sig[1], sig[2], sig[3])); } // Now we identify and store the location for each track. if (NumTracks > Tracks.Length) { throw new InvalidOperationException(string.Format("Can only handle {0} tracks but was handed {1}", Tracks.Length, NumTracks)); } uint totalSize = 0; var tracksRead = 0; while (tracksRead < NumTracks) { sig = br.ReadBytes(4); if (!AreEquals(sig, "MTrk") && !isGmf) { var msg = new StringBuilder(); msg.AppendFormat("Position: {0} ('{1}')", input.Position - 4, (char)sig[0]).AppendLine(); msg.AppendFormat("Hit invalid block '{0}{1}{2}{3}' while scanning for track locations", sig[0], sig[1], sig[2], sig[3]); throw new InvalidOperationException(msg.ToString()); } // If needed, skip the MTrk and length bytes Tracks[tracksRead] = new Track{ Position = input.Position + (isGmf ? -4 : 4) }; if (!isGmf) { var len = br.ReadUInt32BigEndian(); totalSize += len; input.Seek(len, SeekOrigin.Current); } else { // TODO: vs An SMF End of Track meta event must be placed // at the end of the stream. // data[size++] = 0xFF; // data[size++] = 0x2F; // data[size++] = 0x00; // data[size++] = 0x00; throw new NotImplementedException("Gmf not implemented"); } ++tracksRead; } // If this is a Type 1 MIDI, we need to now compress // our tracks down into a single Type 0 track. //_buffer = 0; if (midiType == 1) { // FIXME: Doubled the buffer size to prevent crashes with the // Inherit the Earth MIDIs. Jamieson630 said something about a // better fix, but this will have to do in the meantime. // _buffer = (byte*)malloc(size * 2); // compressToType0(); // _numTracks = 1; // _tracks[0] = _buffer; throw new NotImplementedException("MidiType 1 not yet implemented."); } // Note that we assume the original data passed in // will persist beyond this call, i.e. we do NOT // copy the data to our own buffer. Take warning.... ResetTracking(); Tempo = 500000; ActiveTrack = 0; }
internal protected byte[] FindStartOfSound(int sound, ChunkType ct = ChunkType.MThd | ChunkType.FORM) { int size, pos; var ptr = ScummEngine.Instance.ResourceManager.GetSound(ScummEngine.Instance.Sound.MusicType, sound); if (ptr == null) { Debug.WriteLine("IMuseInternal::findStartOfSound(): Sound {0} doesn't exist", sound); return null; } // Check for old-style headers first, like 'RO' const ChunkType trFlag = ChunkType.MThd | ChunkType.FORM; if (System.Text.Encoding.UTF8.GetString(ptr, 0, 3) == "ROL") return ct == trFlag ? ptr : null; if (System.Text.Encoding.UTF8.GetString(ptr, 4, 2) == "SO") { if (ct == trFlag) { var tmp = new byte[ptr.Length - 4]; Array.Copy(ptr, 4, tmp, 0, tmp.Length); return tmp; } return null; } var ids = new string[] { "MThd", "FORM", "MDhd", "MDpg" }; using (var ms = new MemoryStream(ptr)) { var br = new BinaryReader(ms); ms.Seek(4, SeekOrigin.Current); size = (int)br.ReadUInt32BigEndian(); // Okay, we're looking for one of those things: either // an 'MThd' tag (for SMF), or a 'FORM' tag (for XMIDI). size = 48; // Arbitrary; we should find our tag within the first 48 bytes of the resource pos = 0; while (pos < size) { for (int i = 0; i < ids.Length; ++i) { var sig = System.Text.Encoding.UTF8.GetString(br.ReadBytes(4)); ms.Seek(-4, SeekOrigin.Current); if ((((int)ct) & (1 << i)) != 0 && (sig == ids[i])) { var tmp = new byte[ptr.Length - ms.Position]; Array.Copy(ptr, (int)ms.Position, tmp, 0, tmp.Length); return tmp; } } ++pos; // We could probably iterate more intelligently ms.Seek(1, SeekOrigin.Current); } if (ct == (ChunkType.MThd | ChunkType.FORM)) Debug.WriteLine("IMuseInternal.FindStartOfSound(): Failed to align on sound {0}", sound); } return null; }
protected void LoadStartParameters(int sound) { _priority = 0x80; _volume = 0x7F; VolChan = 0xFFFF; _vol_eff = (byte)((_se.GetChannelVolume(0xFFFF) << 7) >> 7); _pan = 0; _transpose = 0; _detune = 0; var ptr = _se.FindStartOfSound(sound, IMuseInternal.ChunkType.MDhd); uint size; if (ptr != null) { using (var br = new BinaryReader(new MemoryStream(ptr))) { br.BaseStream.Seek(4, SeekOrigin.Current); size = br.ReadUInt32BigEndian(); br.BaseStream.Seek(4, SeekOrigin.Current); // MDhd chunks don't get used in MI1 and contain only zeroes. // We check for volume, priority and speed settings of zero here. if (size != 0 && (ptr[2] | ptr[3] | ptr[7]) != 0) { _priority = ptr[2]; _volume = ptr[3]; _pan = (sbyte)ptr[4]; _transpose = (sbyte)ptr[5]; _detune = (sbyte)ptr[6]; SetSpeed(ptr[7]); } } } }
void LoadFont(string filename) { byte[] dataSrc; using (var file = ServiceLocator.FileStorage.OpenFileRead(filename)) { var reader = new BinaryReader(file); var tag = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(4)); if (tag != "ANIM") { throw new InvalidOperationException("NutRenderer.LoadFont() there is no ANIM chunk in font header"); } var length = (int)reader.ReadUInt32BigEndian(); dataSrc = reader.ReadBytes(length); } if (System.Text.Encoding.UTF8.GetString(dataSrc, 0, 4) != "AHDR") { throw new InvalidOperationException("NutRenderer::loadFont() there is no AHDR chunk in font header"); } // We pre-decode the font, which may seem wasteful at first. Actually, // the memory needed for just the decoded glyphs is smaller than the // whole of the undecoded font file. _numChars = BitConverter.ToUInt16(dataSrc, 10); Debug.Assert(_numChars <= _chars.Length); _paletteMap = new byte[256]; var offset = 0; var decodedLength = 0; for (var l = 0; l < _numChars; l++) { offset += (int)ScummHelper.SwapBytes(BitConverter.ToUInt32(dataSrc, offset + 4)) + 16; int width = BitConverter.ToUInt16(dataSrc, offset + 14); int height = BitConverter.ToUInt16(dataSrc, offset + 16); int size = width * height; decodedLength += size; if (size > _maxCharSize) _maxCharSize = size; } Debug.WriteLine("NutRenderer::loadFont('{0}') - decodedLength = {1}", filename, decodedLength); _decodedData = new byte[decodedLength]; var decodedPos = 0; offset = 0; for (var l = 0; l < _numChars; l++) { offset += (int)ScummHelper.SwapBytes(BitConverter.ToUInt32(dataSrc, offset + 4)) + 8; if (System.Text.Encoding.UTF8.GetString(dataSrc, offset, 4) != "FRME") { throw new InvalidOperationException(string.Format("NutRenderer::loadFont({0}) there is no FRME chunk {1} (offset {2:X})", filename, l, offset)); } offset += 8; if (System.Text.Encoding.UTF8.GetString(dataSrc, offset, 4) != "FOBJ") { throw new InvalidOperationException(string.Format("NutRenderer::loadFont({0}) there is no FOBJ chunk in FRME chunk {1} (offset {2:X})", filename, l, offset)); } int codec = BitConverter.ToUInt16(dataSrc, offset + 8); // _chars[l].xoffs = READ_LE_UINT16(dataSrc + offset + 10); // _chars[l].yoffs = READ_LE_UINT16(dataSrc + offset + 12); _chars[l].Width = BitConverter.ToUInt16(dataSrc, offset + 14); _chars[l].Height = BitConverter.ToUInt16(dataSrc, offset + 16); _chars[l].Src = _decodedData; _chars[l].SrcOffset = decodedPos; decodedPos += (_chars[l].Width * _chars[l].Height); // If characters have transparency, then bytes just get skipped and // so there may appear some garbage. That's why we have to fill it // with a default color first. if (codec == 44) { for (int i = 0; i < _chars[l].Width * _chars[l].Height; i++) { _chars[l].Src[_chars[l].SrcOffset + i] = Smush44TransparentColor; } _paletteMap[Smush44TransparentColor] = 1; _chars[l].Transparency = Smush44TransparentColor; } else { for (int i = 0; i < _chars[l].Width * _chars[l].Height; i++) { _chars[l].Src[_chars[l].SrcOffset + i] = DefaultTransparentColor; } _paletteMap[DefaultTransparentColor] = 1; _chars[l].Transparency = DefaultTransparentColor; } switch (codec) { case 1: Codec1(_chars[l].Src, _chars[l].SrcOffset, dataSrc, offset + 22, _chars[l].Width, _chars[l].Height, _chars[l].Width); break; case 21: case 44: Codec21(_chars[l].Src, _chars[l].SrcOffset, dataSrc, offset + 22, _chars[l].Width, _chars[l].Height, _chars[l].Width); break; default: throw new InvalidOperationException(string.Format("NutRenderer::loadFont: unknown codec: {0}", codec)); } } // We have decoded the font. Now let's see if we can re-compress it to // a more compact format. Start by counting the number of colors. int numColors = 0; for (var l = 0; l < 256; l++) { if (_paletteMap[l] != 0) { if (numColors < _palette.Length) { _paletteMap[l] = (byte)numColors; _palette[numColors] = (byte)l; } numColors++; } } // Now _palette contains all the used colors, and _paletteMap maps the // real color to the palette index. if (numColors <= 2) _bpp = 1; else if (numColors <= 4) _bpp = 2; else if (numColors <= 16) _bpp = 4; else _bpp = 8; if (_bpp < 8) { int compressedLength = 0; for (var l = 0; l < 256; l++) { compressedLength += (((_bpp * _chars[l].Width + 7) / 8) * _chars[l].Height); } Debug.WriteLine("NutRenderer::loadFont('{0}') - compressedLength = {1} ({2} bpp)", filename, compressedLength, _bpp); var compressedData = new byte[compressedLength]; offset = 0; for (var l = 0; l < 256; l++) { var src = _chars[l].Src; var srcPos = _chars[l].SrcOffset; var dstPos = offset; int srcPitch = _chars[l].Width; int dstPitch = (_bpp * _chars[l].Width + 7) / 8; for (var h = 0; h < _chars[l].Height; h++) { byte bit = 0x80; var nextDst = dstPos + dstPitch; for (int w = 0; w < srcPitch; w++) { byte color = _paletteMap[src[srcPos + w]]; for (int i = 0; i < _bpp; i++) { if ((color & (1 << i)) != 0) compressedData[dstPos] |= bit; bit >>= 1; } if (bit == 0) { bit = 0x80; dstPos++; } } srcPos += srcPitch; dstPos = nextDst; } _chars[l].Src = compressedData; _chars[l].SrcOffset = offset; offset += (dstPitch * _chars[l].Height); } _decodedData = compressedData; _charBuffer = new byte[_maxCharSize]; } _paletteMap = null; }
void ParseNextFrame() { string subType; uint subSize = 0; long subOffset = 0; if (_seekPos >= 0) { if (_smixer != null) _smixer.Stop(); if (!string.IsNullOrEmpty(_seekFile)) { _base = new BinaryReader(ServiceLocator.FileStorage.OpenFileRead(_seekFile)); _base.ReadUInt32BigEndian(); _baseSize = _base.ReadUInt32BigEndian(); if (_seekPos > 0) { Debug.Assert(_seekPos > 8); // In this case we need to get palette and number of frames subType = _base.ReadBytes(4).ToText(); subSize = _base.ReadUInt32BigEndian(); subOffset = _base.BaseStream.Position; Debug.Assert(subType == "AHDR"); HandleAnimHeader(subSize, _base); _base.BaseStream.Seek(subOffset + subSize, SeekOrigin.Begin); _middleAudio = true; _seekPos -= 8; } else { // We need this in Full Throttle when entering/leaving // the old mine road. TryCmpFile(_seekFile); } _skipPalette = false; } else { _skipPalette = true; } _base.BaseStream.Seek(_seekPos + 8, SeekOrigin.Begin); _frame = _seekFrame; _startFrame = _frame; _startTime = Environment.TickCount; _seekPos = -1; } Debug.Assert(_base != null); if (_base.BaseStream.Position >= _baseSize) { _vm.SmushVideoShouldFinish = true; _endOfFile = true; return; } subType = _base.ReadBytes(4).ToText(); subSize = _base.ReadUInt32BigEndian(); subOffset = _base.BaseStream.Position; // Debug.WriteLine("Chunk: {0} at {1}", subType, subOffset); switch (subType) { case "AHDR": // FT INSANE may seek file to the beginning HandleAnimHeader(subSize, _base); break; case "FRME": HandleFrame(subSize, _base); break; default: throw new InvalidOperationException(string.Format("Unknown Chunk found at {0:X}: {1}, {2}", subOffset, subType, subSize)); } _base.BaseStream.Seek(subOffset + subSize, SeekOrigin.Begin); if (_insanity) _vm.Sound.ProcessSound(); _vm.IMuseDigital.FlushTracks(); }
// Internal mutex-free versions of the IMuse and MusicEngine methods. protected bool StartSoundInternal(int sound, int offset = 0) { // Do not start a sound if it is already set to start on an ImTrigger // event. This fixes carnival music problems where a sound has been set // to trigger at the right time, but then is started up immediately // anyway, only to be restarted later when the trigger occurs. // // However, we have to make sure the sound with the trigger is actually // playing, otherwise the music may stop when Sam and Max are thrown // out of Bumpusville, because entering the mansion sets up a trigger // for a sound that isn't necessarily playing. This is somewhat related // to bug #780918. foreach (var trigger in _snm_triggers) { if (trigger.Sound != 0 && trigger.Id != 0 && trigger.Command[0] == 8 && trigger.Command[1] == sound && GetSoundStatusInternal(trigger.Sound, true) != 0) return false; } var ptr = FindStartOfSound(sound); if (ptr == null) { Debug.WriteLine("IMuseInternal::startSound(): Couldn't find sound {0}", sound); return false; } // Check which MIDI driver this track should use. // If it's NULL, it ain't something we can play. var driver = GetBestMidiDriver(sound); if (driver == null) return false; // If the requested sound is already playing, start it over // from scratch. This was originally a hack to prevent Sam & Max // iMuse messiness while upgrading the iMuse engine, but it // is apparently necessary to deal with fade-and-restart // race conditions that were observed in MI2. Reference // Bug #590511 and Patch #607175 (which was reversed to fix // an FOA regression: Bug #622606). var player = FindActivePlayer(sound); if (player == null) { ptr = FindStartOfSound(sound, ChunkType.MDhd); int size = 128; if (ptr != null) { using (var br = new BinaryReader(new MemoryStream(ptr))) { br.BaseStream.Seek(4, SeekOrigin.Begin); var tmp = br.ReadUInt32BigEndian(); size = tmp != 0 && ptr[10] != 0 ? ptr[10] : 128; } } player = AllocatePlayer((byte)size); } if (player == null) return false; // WORKAROUND: This is to work around a problem at the Dino Bungie // Memorial. // // There are three pieces of music involved here: // // 80 - Main theme (looping) // 81 - Music when entering Rex's and Wally's room (not looping) // 82 - Music when listening to Rex or Wally // // When entering, tune 81 starts, tune 80 is faded down (not out) and // a trigger is set in tune 81 to fade tune 80 back up. // // When listening to Rex or Wally, tune 82 is started, tune 81 is faded // out and tune 80 is faded down even further. // // However, when tune 81 is faded out its trigger will cause tune 80 to // fade back up, resulting in two tunes being played simultaneously at // full blast. It's no use trying to keep tune 81 playing at volume 0. // It doesn't loop, so eventually it will terminate on its own. // // I don't know how the original interpreter handled this - or even if // it handled it at all - but it looks like sloppy scripting to me. Our // workaround is to clear the trigger if the player listens to Rex or // Wally before tune 81 has finished on its own. if (_game_id == GameId.SamNMax && sound == 82 && GetSoundStatusInternal(81, false) != 0) ImClearTrigger(81, 1); player.Clear(); player.SetOffsetNote(offset); return player.StartSound(sound, driver); }
protected override void LoadIndex(GameInfo game) { Directory = ServiceLocator.FileStorage.GetDirectoryName(game.Path); using (var file = ServiceLocator.FileStorage.OpenFileRead(game.Path)) { var br = new BinaryReader(file); while (br.BaseStream.Position < br.BaseStream.Length) { var tag = System.Text.Encoding.UTF8.GetString(br.ReadBytes(4)); br.ReadUInt32BigEndian(); switch (tag) { case "DCHR": case "DIRF": var charset = ReadResTypeList(br); CharsetResources = new ReadOnlyCollection<Resource>(charset); break; case "DOBJ": ReadDirectoryOfObjects(br); break; case "RNAM": ReadRoomNames(br); break; case "DROO": case "DIRR": var rooms = ReadResTypeList(br); RoomResources = new ReadOnlyCollection<Resource>(rooms); break; case "DSCR": case "DIRS": var scripts = ReadResTypeList(br); ScriptResources = new ReadOnlyCollection<Resource>(scripts); break; case "DRSC": var roomScripts = ReadResTypeList(br); RoomScriptResources = new ReadOnlyCollection<Resource>(roomScripts); break; case "DCOS": case "DIRC": var costumes = ReadResTypeList(br); CostumeResources = new ReadOnlyCollection<Resource>(costumes); break; case "MAXS": ReadMaxSizes(br); break; case "DIRN": case "DSOU": var sounds = ReadResTypeList(br); SoundResources = new ReadOnlyCollection<Resource>(sounds); break; case "AARY": ReadArrayFromIndexFile(br); break; case "ANAM": // Used by: The Dig, FT { var num = br.ReadUInt16(); AudioNames = new string[num]; for (int i = 0; i < num; i++) { AudioNames[i] = br.ReadBytes(9).GetText(); } } break; // default: // Console.Error.WriteLine("Unknown tag {0} found in index file directory", tag); // break; } } } }
MdatResource LoadMdatFile(Stream musicData) { var br = new BinaryReader(musicData); bool hasHeader = false; var mdatSize = musicData.Length; if (mdatSize >= 0x200) { // 0x0000: 10 Bytes Header "TFMX-SONG " var buf = System.Text.Encoding.UTF8.GetString(br.ReadBytes(10)); hasHeader = buf == "TFMX-SONG "; } if (!hasHeader) { Debug.WriteLine("Tfmx: File is not a Tfmx Module"); return null; } var resource = new MdatResource(); resource.mdatAlloc = null; resource.mdatOffset = 0; resource.mdatLen = 0; // 0x000A: int16 flags resource.headerFlags = br.ReadUInt16BigEndian(); // 0x000C: int32 ? // 0x0010: 6*40 Textfield musicData.Seek(4 + 6 * 40, SeekOrigin.Current); /* 0x0100: Songstart x 32*/ for (int i = 0; i < NumSubsongs; ++i) resource.subsong[i].songstart = br.ReadUInt16BigEndian(); /* 0x0140: Songend x 32*/ for (int i = 0; i < NumSubsongs; ++i) resource.subsong[i].songend = br.ReadUInt16BigEndian(); /* 0x0180: Tempo x 32*/ for (int i = 0; i < NumSubsongs; ++i) resource.subsong[i].tempo = br.ReadUInt16BigEndian(); /* 0x01c0: unused ? */ musicData.Seek(16, SeekOrigin.Current); /* 0x01d0: trackstep, pattern data p, macro data p */ var offTrackstep = br.ReadUInt32BigEndian(); uint offPatternP, offMacroP; // This is how MI`s TFMX-Player tests for unpacked Modules. if (offTrackstep == 0) { // unpacked File resource.trackstepOffset = 0x600 + 0x200; offPatternP = 0x200 + 0x200; offMacroP = 0x400 + 0x200; } else { // packed File resource.trackstepOffset = offTrackstep; offPatternP = br.ReadUInt32BigEndian(); offMacroP = br.ReadUInt32BigEndian(); } // TODO: if a File is packed it could have for Ex only 2 Patterns/Macros // the following loops could then read beyond EOF. // To correctly handle this it would be necessary to sort the pointers and // figure out the number of Macros/Patterns // We could also analyze pointers if they are correct offsets, // so that accesses can be unchecked later // Read in pattern starting offsets musicData.Seek(offPatternP, SeekOrigin.Begin); for (int i = 0; i < MaxPatternOffsets; ++i) resource.patternOffset[i] = br.ReadUInt32BigEndian(); // use last PatternOffset (stored at 0x5FC in mdat) if unpacked File // or fixed offset 0x200 if packed resource.sfxTableOffset = offTrackstep != 0 ? 0x200 : resource.patternOffset[127]; // Read in macro starting offsets musicData.Seek(offMacroP, SeekOrigin.Begin); for (int i = 0; i < MaxMacroOffsets; ++i) resource.macroOffset[i] = br.ReadUInt32BigEndian(); // Read in mdat-file // TODO: we can skip everything thats already stored in the resource-structure. var mdatOffset = offTrackstep != 0 ? 0x200 : 0x600; // 0x200 is very conservative var allocSize = mdatSize - mdatOffset; musicData.Seek(mdatOffset, SeekOrigin.Begin); var mdatAlloc = br.ReadBytes((int)allocSize); resource.mdatAlloc = mdatAlloc; resource.mdatOffset = mdatOffset; resource.mdatLen = (int)mdatSize; return resource; }
public override bool LoadStream(Stream stream) { Close(); _fileStream = stream; _br = new BinaryReader(stream); // Read in the Smacker header _header = new Header(); _header.signature = _br.ReadUInt32BigEndian(); if (_header.signature != ScummHelper.MakeTag('S', 'M', 'K', '2') && _header.signature != ScummHelper.MakeTag('S', 'M', 'K', '4')) return false; uint width = _br.ReadUInt32(); uint height = _br.ReadUInt32(); uint frameCount = _br.ReadUInt32(); int frameDelay = _br.ReadInt32(); // frame rate contains 2 digits after the comma, so 1497 is actually 14.97 fps Rational frameRate; if (frameDelay > 0) frameRate = new Rational(1000, frameDelay); else if (frameDelay < 0) frameRate = new Rational(100000, -frameDelay); else frameRate = new Rational(1000); // Flags are determined by which bit is set, which can be one of the following: // 0 - set to 1 if file contains a ring frame. // 1 - set to 1 if file is Y-interlaced // 2 - set to 1 if file is Y-doubled // If bits 1 or 2 are set, the frame should be scaled to twice its height // before it is displayed. _header.flags = _br.ReadUInt32(); var videoTrack = CreateVideoTrack(width, height, frameCount, frameRate, _header.flags, _header.signature); AddTrack(videoTrack); // TODO: should we do any extra processing for Smacker files with ring frames? // TODO: should we do any extra processing for Y-doubled videos? Are they the // same as Y-interlaced videos? uint i; for (i = 0; i < 7; ++i) _header.audioSize[i] = _br.ReadUInt32(); _header.treesSize = _br.ReadUInt32(); _header.mMapSize = _br.ReadUInt32(); _header.mClrSize = _br.ReadUInt32(); _header.fullSize = _br.ReadUInt32(); _header.typeSize = _br.ReadUInt32(); for (i = 0; i < 7; ++i) { // AudioRate - Frequency and format information for each sound track, up to 7 audio tracks. // The 32 constituent bits have the following meaning: // * bit 31 - indicates Huffman + Dpcm compression // * bit 30 - indicates that audio data is present for this track // * bit 29 - 1 = 16-bit audio; 0 = 8-bit audio // * bit 28 - 1 = stereo audio; 0 = mono audio // * bit 27 - indicates Bink Rdft compression // * bit 26 - indicates Bink Dct compression // * bits 25-24 - unused // * bits 23-0 - audio sample rate uint audioInfo = _br.ReadUInt32(); _header.audioInfo[i].hasAudio = (audioInfo & 0x40000000) != 0; _header.audioInfo[i].is16Bits = (audioInfo & 0x20000000) != 0; _header.audioInfo[i].isStereo = (audioInfo & 0x10000000) != 0; _header.audioInfo[i].sampleRate = audioInfo & 0xFFFFFF; if ((audioInfo & 0x8000000) != 0) _header.audioInfo[i].compression = AudioCompression.Rdft; else if ((audioInfo & 0x4000000) != 0) _header.audioInfo[i].compression = AudioCompression.Dct; else if ((audioInfo & 0x80000000) != 0) _header.audioInfo[i].compression = AudioCompression.Dpcm; else _header.audioInfo[i].compression = AudioCompression.None; if (_header.audioInfo[i].hasAudio) { if (_header.audioInfo[i].compression == AudioCompression.Rdft || _header.audioInfo[i].compression == AudioCompression.Dct) throw new InvalidOperationException("Unhandled Smacker v2 audio compression"); AddTrack(new SmackerAudioTrack(_mixer, _header.audioInfo[i], _soundType)); } } _header.dummy = _br.ReadUInt32(); _frameSizes = new uint[frameCount]; for (i = 0; i < frameCount; ++i) _frameSizes[i] = _br.ReadUInt32(); _frameTypes = new byte[frameCount]; for (i = 0; i < frameCount; ++i) _frameTypes[i] = _br.ReadByte(); var huffmanTrees = _br.ReadBytes((int)_header.treesSize); using (var ms = new MemoryStream(huffmanTrees)) { var bs = BitStream.Create8Lsb(ms); videoTrack.ReadTrees(bs, (int)_header.mMapSize, (int)_header.mClrSize, (int)_header.fullSize, (int)_header.typeSize); } _firstFrameStart = (uint)_fileStream.Position; return true; }
public bool RestoreGameFromFile(byte slot) { ushort cnt; var fName = $"sword1.{slot:D3}"; using (var inf = new BinaryReader(_saveFileMan.OpenForLoading(fName))) { // TODO: //if (inf==null) //{ // // Display an error message, and do nothing // // TODO: DisplayMessage(0, "Can't open file '%s'. (%s)", fName, _saveFileMan.popErrorDesc().c_str()); // return false; //} uint saveHeader = inf.ReadUInt32(); if (saveHeader != SAVEGAME_HEADER) { // Display an error message, and do nothing // TODO: DisplayMessage(0, "Save game '%s' is corrupt", fName); return false; } inf.BaseStream.Seek(40, SeekOrigin.Current); // skip description byte saveVersion = inf.ReadByte(); if (saveVersion > SAVEGAME_VERSION) { // TODO: warning("Different save game version"); return false; } if (saveVersion < 2) // These older version of the savegames used a flag to signal presence of thumbnail inf.BaseStream.Seek(1, SeekOrigin.Current); SkipThumbnail(inf); inf.ReadUInt32BigEndian(); // save date inf.ReadUInt16BigEndian(); // save time if (saveVersion < 2) { // Before version 2 we didn't had play time feature // TODO: g_engine.setTotalPlayTime(0); } else { var time = inf.ReadUInt32BigEndian(); // TODO: g_engine.setTotalPlayTime(time * 1000); } _restoreBuf = new byte[ ObjectMan.TOTAL_SECTIONS * 2 + Logic.NUM_SCRIPT_VARS * 4 + (SwordObject.Size - 12000)]; var liveBuf = new UShortAccess(_restoreBuf); var scriptBuf = new UIntAccess(_restoreBuf, 2 * ObjectMan.TOTAL_SECTIONS); var playerBuf = new UIntAccess(_restoreBuf, 2 * ObjectMan.TOTAL_SECTIONS + 4 * Logic.NUM_SCRIPT_VARS); for (cnt = 0; cnt < ObjectMan.TOTAL_SECTIONS; cnt++) liveBuf[cnt] = inf.ReadUInt16(); for (cnt = 0; cnt < Logic.NUM_SCRIPT_VARS; cnt++) scriptBuf[cnt] = inf.ReadUInt32(); uint playerSize = (SwordObject.Size - 12000) / 4; for (var cnt2 = 0; cnt2 < playerSize; cnt2++) playerBuf[cnt2] = inf.ReadUInt32(); // TODO: error //if (inf.err() || inf.eos()) //{ // displayMessage(0, "Can't read from file '%s'. (%s)", fName, _saveFileMan.popErrorDesc().c_str()); // delete inf; // free(_restoreBuf); // _restoreBuf = NULL; // return false; //} } return true; }
public int MatchFile(string filename) { int offset; bool found = false; int freeSlot = -1; int fileId; for (fileId = 0; fileId < _budleDirCache.Length; fileId++) { if ((_budleDirCache[fileId].BundleTable == null) && (freeSlot == -1)) { freeSlot = fileId; } if (string.Equals(filename, _budleDirCache[fileId].FileName, StringComparison.OrdinalIgnoreCase)) { found = true; break; } } if (!found) { var file = new BinaryReader(ServiceLocator.FileStorage.OpenFileRead(filename)); // if (freeSlot == -1) // Console.Error.WriteLine("BundleDirCache::matchFileFile() Can't find free slot for file bundle dir cache"); var tag = file.ReadTag(); if (tag == "LB23") _budleDirCache[freeSlot].IsCompressed = true; offset = (int)file.ReadUInt32BigEndian(); _budleDirCache[freeSlot].FileName = filename; _budleDirCache[freeSlot].NumFiles = (int)file.ReadUInt32BigEndian(); _budleDirCache[freeSlot].BundleTable = CreateAudioTable(_budleDirCache[freeSlot].NumFiles); file.BaseStream.Seek(offset, SeekOrigin.Begin); _budleDirCache[freeSlot].IndexTable = CreateIndexTable(_budleDirCache[freeSlot].NumFiles); for (int i = 0; i < _budleDirCache[freeSlot].NumFiles; i++) { var name = new List<byte>(); byte c; if (tag == "LB23") { _budleDirCache[freeSlot].BundleTable[i].Filename = file.ReadBytes(24).GetText(); } else { for (var z2 = 0; z2 < 8; z2++) if ((c = file.ReadByte()) != 0) name.Add(c); name.Add((byte)'.'); for (var z2 = 0; z2 < 4; z2++) if ((c = file.ReadByte()) != 0) name.Add(c); _budleDirCache[freeSlot].BundleTable[i].Filename = name.ToArray().GetText(); } _budleDirCache[freeSlot].BundleTable[i].Offset = (int)file.ReadUInt32BigEndian(); _budleDirCache[freeSlot].BundleTable[i].Size = (int)file.ReadUInt32BigEndian(); _budleDirCache[freeSlot].IndexTable[i].Filename = _budleDirCache[freeSlot].BundleTable[i].Filename; _budleDirCache[freeSlot].IndexTable[i].Index = i; } Array.Sort(_budleDirCache[freeSlot].IndexTable, new Comparison<IndexNode>((x, y) => string.Compare(x.Filename, y.Filename, StringComparison.OrdinalIgnoreCase))); return freeSlot; } return fileId; }
void HandleFrame(uint frameSize, BinaryReader b) { // Debug.WriteLine("SmushPlayer.HandleFrame({0})", _frame); _skipNext = false; if (_insanity) { _vm.Insane.ProcPreRendering(); } while (frameSize > 0) { var subType = _base.ReadBytes(4).ToText(); var subSize = b.ReadUInt32BigEndian(); var subOffset = b.BaseStream.Position; switch (subType) { case "NPAL": HandleNewPalette(subSize, b); break; case "FOBJ": HandleFrameObject(subSize, b); break; #if USE_ZLIB case "ZFOB": HandleZlibFrameObject(subSize, b); break; #endif case "PSAD": if (!_compressedFileMode) HandleSoundFrame(subSize, b); break; case "TRES": HandleTextResource(subType, subSize, b); break; case "XPAL": HandleDeltaPalette(subSize, b); break; case "IACT": HandleIACT(subSize, b); break; case "STOR": HandleStore(subSize, b); break; case "FTCH": HandleFetch(subSize, b); break; case "SKIP": _vm.Insane.ProcSKIP((int)subSize, b); break; case "TEXT": HandleTextResource(subType, subSize, b); break; default: throw new InvalidOperationException(string.Format("Unknown frame subChunk found : {0}, {1}", subType, subSize)); } frameSize -= subSize + 8; b.BaseStream.Seek(subOffset + subSize, SeekOrigin.Begin); if ((subSize & 1) != 0) { b.BaseStream.Seek(1, SeekOrigin.Current); frameSize--; } } if (_insanity) { _vm.Insane.ProcPostRendering(_dst, 0, 0, 0, _frame, _nbframes - 1); } if (_width != 0 && _height != 0) { UpdateScreen(); } _smixer.HandleFrame(); _frame++; }
// Sequencer part protected int StartSeqSound(int sound, bool resetVars = true) { if (resetVars) { _loop_to_beat = 1; _loop_from_beat = 1; _track_index = 0; _loop_counter = 0; _loop_to_tick = 0; _loop_from_tick = 0; } var ptr = _se.FindStartOfSound(sound); if (ptr == null) return -1; if (Encoding.UTF8.GetString(ptr, 0, 2) == "RO") { // Old style 'RO' resource _parser = MidiParser.CreateRO(); } else if (Encoding.UTF8.GetString(ptr, 0, 4) == "FORM") { // Humongous Games XMIDI resource _parser = MidiParser.CreateXMidiParser(); } else { // SCUMM SMF resource _parser = MidiParser.CreateSmfParser(); } _parser.MidiDriver = this; _parser.Property(MidiParserProperty.SmartJump, 1); _parser.LoadMusic(ptr); _parser.ActiveTrack = _track_index; ptr = _se.FindStartOfSound(sound, IMuseInternal.ChunkType.MDhd); var speed = 128; if (resetVars) { if (ptr != null) { using (var br = new BinaryReader(new MemoryStream(ptr))) { br.BaseStream.Seek(4, SeekOrigin.Begin); speed = br.ReadUInt32BigEndian() != 0 && ptr[15] != 0 ? ptr[15] : 128; } } } else { speed = _speed; } SetSpeed((byte)speed); return 0; }