public override void ConvertData(ProjectFile pf) { EmptyRefsForNamed(pf.DataHandle.GetChunk <GMChunkSOND>().List, pf.Sounds, (asset) => { GMSound sound = (GMSound)asset; BufferRegion buff; if ((sound.Flags & AudioEntryFlags.IsEmbedded) != AudioEntryFlags.IsEmbedded && (sound.Flags & AudioEntryFlags.IsCompressed) != AudioEntryFlags.IsCompressed) { buff = null; } else { if (pf._CachedAudioChunks.TryGetValue(sound.GroupID, out var chunk)) { buff = chunk.List[sound.AudioID].Data; } else { pf.WarningHandler.Invoke(ProjectFile.WarningType.MissingAudioGroup, $"Missing audio group ID {sound.GroupID} for {sound.Name?.Content ?? "<null>"}"); buff = null; } } if (pf.AudioGroupSettings == null) { return(new CachedSoundRefData(buff, "")); } return(new CachedSoundRefData(buff, (sound.GroupID >= 0 && sound.GroupID < pf.AudioGroupSettings.AudioGroups.Count) ? pf.AudioGroupSettings.AudioGroups[sound.GroupID] : "")); }); }
public static void Sound(ProjectWriter writer, GMSound self, GMProject _) { writer.Write(self.Name); writer.Write(self.Version); writer.Write((int)self.Kind); writer.Write(self.FileType); writer.Write(self.FileName); if (self.Data != null) { writer.Write(true); writer.Write(self.Data.Length); writer.Write(self.Data); } else { writer.Write(false); } writer.Write(self.GetEffectsInt()); writer.Write(self.Volume); writer.Write(self.Panning); writer.Write(self.Preload); }
/// <summary> /// Reads a Game Maker project file /// </summary> public void ReadProject(string file) { // If the file does not exist, throw exception if (File.Exists(file) == false) { throw new Exception("The Game Maker project file does not exist."); } // Get file extension string ext = file.Substring(file.LastIndexOf('.')).ToLower(); // If a GMS project file if (ext == ".gmx") { // Read in the project as a Game Maker Studio project and return ReadProjectGMS(file); return; } // Get file size FileInfo info = new FileInfo(file); long length = info.Length; // Create a new GM file reader using (GMFileReader reader = new GMFileReader(new FileStream(file, FileMode.Open, FileAccess.Read))) { // Progress event ProgressChanged("Starting project read...", reader.BaseStream.Position, length); // Read the magic number int id = reader.ReadGMInt(); // If the magic number was incorrect, not a Game Maker project file if (id != 1234321) { throw new Exception("Not a valid Game Maker project file."); } // Get Game Maker project file version int version = reader.ReadGMInt(); // Check version switch (version) { case 500: this.GameMakerVersion = GMVersionType.GameMaker50; break; case 510: this.GameMakerVersion = GMVersionType.GameMaker51; break; case 520: this.GameMakerVersion = GMVersionType.GameMaker52; break; case 530: this.GameMakerVersion = GMVersionType.GameMaker53; break; case 600: this.GameMakerVersion = GMVersionType.GameMaker60; break; case 701: this.GameMakerVersion = GMVersionType.GameMaker70; break; case 800: this.GameMakerVersion = GMVersionType.GameMaker80; break; case 810: this.GameMakerVersion = GMVersionType.GameMaker81; break; } // Skip over reserved bytes if (version < 600) { reader.ReadGMBytes(4); } // Game Maker 7 project file encryption if (version == 701) { // Bill and Fred, psssttt they like each other ;) int bill = reader.ReadGMInt(); int fred = reader.ReadGMInt(); // Skip bytes to treasure. reader.ReadGMBytes(bill * 4); // Get the seed for swap table int seed = reader.ReadGMInt(); // Skip bytes to get out of the junk yard reader.ReadGMBytes(fred * 4); // Read first byte of Game id (Not encrypted) byte b = reader.ReadByte(); // Set the seed reader.SetSeed(seed); // Read game id id = reader.ReadGMInt(b); } else // Read game id normally { id = reader.ReadGMInt(); } // Skip unknown bytes reader.ReadGMBytes(16); // Read settings ProgressChanged("Reading Settings...", reader.BaseStream.Position, length); // Read main project objects this.Settings = GMSettings.ReadSettings(reader); this.Settings.GameIdentifier = id; // If the version is greater than Game Maker 7.0 if (version > 701) { // Read triggers and constants. this.Triggers = GMTrigger.ReadTriggers(reader); this.Settings.Constants = GMConstant.ReadConstants(reader); } // Read sounds ProgressChanged("Reading Sounds...", reader.BaseStream.Position, length); this.Sounds = GMSound.ReadSounds(reader); // Read sprites ProgressChanged("Reading Sprites...", reader.BaseStream.Position, length); this.Sprites = GMSprite.ReadSprites(reader); // Read backgrounds ProgressChanged("Reading Backgrounds...", reader.BaseStream.Position, length); this.Backgrounds = GMBackground.ReadBackgrounds(reader); // Read paths ProgressChanged("Reading Paths...", reader.BaseStream.Position, length); this.Paths = GMPath.ReadPaths(reader); // Read scripts ProgressChanged("Reading Scripts...", reader.BaseStream.Position, length); this.Scripts = GMScript.ReadScripts(reader); // Get version int version2 = reader.ReadGMInt(); // Check version if (version2 != 440 && version2 != 540 && version2 != 800) { throw new Exception("Unsupported Pre-Font/Pre-Data File object version."); } // If version is old, read data files else, read fonts. if (version2 == 440) { // Read data files ProgressChanged("Reading Data Files...", reader.BaseStream.Position, length); this.DataFiles = GMDataFile.ReadDataFiles(reader); } else { // Read fonts ProgressChanged("Reading Fonts...", reader.BaseStream.Position, length); this.Fonts = GMFont.ReadFonts(version2, reader); } // Read timelines ProgressChanged("Reading Timelines...", reader.BaseStream.Position, length); this.Timelines = GMTimeline.ReadTimelines(reader); // Read objects ProgressChanged("Reading Objects...", reader.BaseStream.Position, length); this.Objects = GMObject.ReadObjects(reader); // Read rooms ProgressChanged("Reading Rooms...", reader.BaseStream.Position, length); this.Rooms = GMRoom.ReadRooms(this.Objects, reader); // Read last ids for instances and tiles this.LastInstanceId = reader.ReadGMInt(); this.LastTileId = reader.ReadGMInt(); // If the version is above 6.1, read include files and packages if (version >= 700) { // Read includes ProgressChanged("Reading Includes...", reader.BaseStream.Position, length); this.Settings.Includes = GMInclude.ReadIncludes(reader); // Read packages ProgressChanged("Reading Packages...", reader.BaseStream.Position, length); this.Packages.AddRange(GMPackage.ReadPackages(reader)); } // Read game information ProgressChanged("Reading Game Information...", reader.BaseStream.Position, length); this.GameInformation = GMGameInformation.ReadGameInformation(reader); // Get version version = reader.ReadGMInt(); // Check version if (version != 500) { throw new Exception("Unsupported Post-Game Information object version."); } // Read libraries ProgressChanged("Reading Libraries...", reader.BaseStream.Position, length); this.Libraries = GMLibrary.ReadLibraries(reader); // Read project tree ProgressChanged("Reading Project Tree...", reader.BaseStream.Position, length); this.ProjectTree = GMNode.ReadTree(file.Substring(file.LastIndexOf(@"\") + 1), reader); // Progress event ProgressChanged("Finished Reading Project.", reader.BaseStream.Position, length); } }
private static void ConvertSounds(ProjectFile pf) { var dataAssets = ((GMChunkSOND)pf.DataHandle.Chunks["SOND"]).List; var agrp = (GMChunkAGRP)pf.DataHandle.Chunks["AGRP"]; var groups = agrp.List; bool updatedVersion = pf.DataHandle.VersionInfo.IsNumberAtLeast(1, 0, 0, 9999); // First, sort sounds alphabetically List <AssetSound> sortedSounds = updatedVersion ? pf.Sounds.OrderBy(x => x.Name).ToList() : pf.Sounds; // Get all the AUDO chunk handles in the game GMChunkAUDO defaultChunk = (GMChunkAUDO)pf.DataHandle.Chunks["AUDO"]; defaultChunk.List.Clear(); Dictionary <string, GMChunkAUDO> audioChunks = new Dictionary <string, GMChunkAUDO>(); Dictionary <string, int> audioChunkIndices = new Dictionary <string, int>(); if (agrp.AudioData != null) { for (int i = 1; i < groups.Count; i++) { if (agrp.AudioData.ContainsKey(i)) { var currChunk = (GMChunkAUDO)agrp.AudioData[i].Chunks["AUDO"]; currChunk.List.Clear(); audioChunks.Add(groups[i].Name.Content, currChunk); audioChunkIndices.Add(groups[i].Name.Content, i); } } } dataAssets.Clear(); Dictionary <AssetSound, GMSound> finalMap = new Dictionary <AssetSound, GMSound>(); for (int i = 0; i < sortedSounds.Count; i++) { AssetSound asset = sortedSounds[i]; GMSound dataAsset = new GMSound() { Name = pf.DataHandle.DefineString(asset.Name), Volume = asset.Volume, Flags = GMSound.AudioEntryFlags.Regular, Effects = 0, Pitch = asset.Pitch, File = pf.DataHandle.DefineString(asset.OriginalSoundFile), Type = (asset.Type != null) ? pf.DataHandle.DefineString(asset.Type) : null }; finalMap[asset] = dataAsset; switch (asset.Attributes) { case AssetSound.Attribute.CompressedStreamed: if (updatedVersion) { dataAsset.AudioID = -1; } else { dataAsset.AudioID = defaultChunk.List.Count - 1; } dataAsset.GroupID = pf.DataHandle.VersionInfo.BuiltinAudioGroupID; // might be wrong File.WriteAllBytes(Path.Combine(pf.DataHandle.Directory, asset.SoundFile), asset.SoundFileBuffer); break; case AssetSound.Attribute.UncompressOnLoad: case AssetSound.Attribute.Uncompressed: dataAsset.Flags |= GMSound.AudioEntryFlags.IsEmbedded; goto case AssetSound.Attribute.CompressedNotStreamed; case AssetSound.Attribute.CompressedNotStreamed: if (asset.Attributes != AssetSound.Attribute.Uncompressed) { dataAsset.Flags |= GMSound.AudioEntryFlags.IsCompressed; } int ind; GMChunkAUDO chunk; if (!audioChunkIndices.TryGetValue(asset.AudioGroup, out ind)) { ind = pf.DataHandle.VersionInfo.BuiltinAudioGroupID; // might be wrong chunk = defaultChunk; } else { chunk = audioChunks[asset.AudioGroup]; } dataAsset.GroupID = ind; dataAsset.AudioID = chunk.List.Count; chunk.List.Add(new GMAudio() { Data = asset.SoundFileBuffer }); break; } } // Actually add sounds to the data foreach (AssetSound snd in pf.Sounds) { dataAssets.Add(finalMap[snd]); } }
/// <summary> /// Reads all sounds from Game Maker project file. /// </summary> private GMList<GMSound> ReadSounds() { // Get version. int version = ReadInt(); // Check version. if (version != 400 && version != 800) throw new Exception("Unsupported Pre-Sound object version."); // Create a new sound list. GMList<GMSound> sounds = new GMList<GMSound>(); // Amount of sound ids. int num = ReadInt(); // Iterate through sounds. for (int i = 0; i < num; i++) { // If version is 8.0, start inflate. if (version == 800) Decompress(); // If the sound at index does not exists, continue. if (ReadBool() == false) { sounds.LastId++; EndDecompress(); continue; } // Create sound object. GMSound sound = new GMSound(); // Set sound id. sound.Id = i; // Read sound data. sound.Name = ReadString(); // If version is 8.0, get last changed. if (version == 800) sound.LastChanged = ReadDouble(); // Get version. version = ReadInt(); // Check version. if (version != 440 && version != 600 && version != 800) throw new Exception("Unsupported Sound object version."); // Check version. if (version == 440) sound.Kind = (SoundKind)ReadInt(); else // Read sound data. sound.Type = (SoundType)ReadInt(); // Read sound data. sound.FileType = ReadString(); // Check version. if (version == 440) { // If sound data exists, read it. if ((int)sound.Kind != -1) sound.Data = ReadBytes(ReadInt()); // Read sound data. sound.AllowSoundEffects = ReadBool(); sound.Buffers = ReadInt(); sound.LoadOnlyOnUse = ReadBool(); } else { // Read sound data. sound.FileName = ReadString(); // If sound data exists, read it. if (ReadBool() == true) sound.Data = ReadBytes(ReadInt()); // Read sound data. sound.Effects = ReadInt(); sound.Volume = ReadDouble(); sound.Pan = ReadDouble(); sound.Preload = ReadBool(); } // End object inflate. EndDecompress(); // Add sound. sounds.Add(sound); } // Return sounds. return sounds; }
public static List <Asset> ConvertSounds(GMData data) { var dataAssets = ((GMChunkSOND)data.Chunks["SOND"]).List; var agrp = (GMChunkAGRP)data.Chunks["AGRP"]; var groups = agrp.List; // Get all the AUDO chunk handles in the game Dictionary <int, GMChunkAUDO> audioChunks = new Dictionary <int, GMChunkAUDO>() { { data.VersionInfo.BuiltinAudioGroupID, (GMChunkAUDO)data.Chunks["AUDO"] } }; if (agrp.AudioData != null) { for (int i = 1; i < groups.Count; i++) { if (agrp.AudioData.ContainsKey(i)) { audioChunks.Add(i, (GMChunkAUDO)agrp.AudioData[i].Chunks["AUDO"]); } } } List <Asset> list = new List <Asset>(); for (int i = 0; i < dataAssets.Count; i++) { GMSound asset = dataAssets[i]; AssetSound projectAsset = new AssetSound() { Name = asset.Name.Content, AudioGroup = (asset.GroupID >= 0 && asset.GroupID < groups.Count) ? groups[asset.GroupID].Name.Content : "", Volume = asset.Volume, Pitch = asset.Pitch, Type = asset.Type?.Content, OriginalSoundFile = asset.File.Content, SoundFile = asset.File.Content }; if ((asset.Flags & AudioEntryFlags.IsEmbedded) != AudioEntryFlags.IsEmbedded && (asset.Flags & AudioEntryFlags.IsCompressed) != AudioEntryFlags.IsCompressed) { // External file projectAsset.Attributes = AssetSound.Attribute.CompressedStreamed; string soundFilePath = Path.Combine(data.Directory, asset.File.Content); if (!soundFilePath.EndsWith(".ogg") && !soundFilePath.EndsWith(".mp3")) { soundFilePath += ".ogg"; } if (File.Exists(soundFilePath)) { projectAsset.SoundFileBuffer = File.ReadAllBytes(soundFilePath); if (!projectAsset.SoundFile.Contains(".")) { projectAsset.SoundFile += Path.GetExtension(soundFilePath); } } } else { // Internal file projectAsset.SoundFileBuffer = audioChunks[asset.GroupID].List[asset.AudioID].Data; if ((asset.Flags & AudioEntryFlags.IsCompressed) == AudioEntryFlags.IsCompressed) { // But compressed! if ((asset.Flags & AudioEntryFlags.IsEmbedded) == AudioEntryFlags.IsEmbedded) { projectAsset.Attributes = AssetSound.Attribute.UncompressOnLoad; } else { projectAsset.Attributes = AssetSound.Attribute.CompressedNotStreamed; } if (projectAsset.SoundFileBuffer.Length > 4 && !projectAsset.SoundFile.Contains(".")) { if (projectAsset.SoundFileBuffer[0] == 'O' && projectAsset.SoundFileBuffer[1] == 'g' && projectAsset.SoundFileBuffer[2] == 'g' && projectAsset.SoundFileBuffer[3] == 'S') { projectAsset.SoundFile += ".ogg"; } else { projectAsset.SoundFile += ".mp3"; } } } else { projectAsset.Attributes = AssetSound.Attribute.Uncompressed; if (!projectAsset.SoundFile.Contains(".")) { projectAsset.SoundFile += ".wav"; } } } list.Add(projectAsset); } return(list); }
public override void ConvertData(ProjectFile pf, int index) { GMSound asset = (GMSound)pf.Sounds[index].DataAsset; AssetSound projectAsset = new AssetSound() { Name = asset.Name?.Content, AudioGroup = ((CachedSoundRefData)pf.Sounds[index].CachedData).AudioGroupName, Volume = asset.Volume, Pitch = asset.Pitch, Type = asset.Type?.Content, OriginalSoundFile = asset.File.Content, SoundFile = asset.File.Content }; if ((asset.Flags & AudioEntryFlags.IsEmbedded) != AudioEntryFlags.IsEmbedded && (asset.Flags & AudioEntryFlags.IsCompressed) != AudioEntryFlags.IsCompressed) { // External file projectAsset.Attributes = AssetSound.Attribute.CompressedStreamed; string soundFilePath = Path.Combine(pf.DataHandle.Directory, asset.File.Content); if (!soundFilePath.EndsWith(".ogg") && !soundFilePath.EndsWith(".mp3")) { soundFilePath += ".ogg"; } if (File.Exists(soundFilePath)) { projectAsset.SoundFileBuffer = new BufferRegion(File.ReadAllBytes(soundFilePath)); if (!projectAsset.SoundFile.Contains(".")) { projectAsset.SoundFile += Path.GetExtension(soundFilePath); } } } else { // Internal file projectAsset.SoundFileBuffer = pf._CachedAudioChunks[asset.GroupID].List[asset.AudioID].Data; if ((asset.Flags & AudioEntryFlags.IsCompressed) == AudioEntryFlags.IsCompressed) { // But compressed! if ((asset.Flags & AudioEntryFlags.IsEmbedded) == AudioEntryFlags.IsEmbedded) { projectAsset.Attributes = AssetSound.Attribute.UncompressOnLoad; } else { projectAsset.Attributes = AssetSound.Attribute.CompressedNotStreamed; } if (projectAsset.SoundFileBuffer.Length > 4 && !projectAsset.SoundFile.Contains(".")) { var span = projectAsset.SoundFileBuffer.Memory.Span; if (span[0] == 'O' && span[1] == 'g' && span[2] == 'g' && span[3] == 'S') { projectAsset.SoundFile += ".ogg"; } else { projectAsset.SoundFile += ".mp3"; } } } else { projectAsset.Attributes = AssetSound.Attribute.Uncompressed; if (!projectAsset.SoundFile.Contains(".")) { projectAsset.SoundFile += ".wav"; } } } pf.Sounds[index].Asset = projectAsset; }
public override void ConvertProject(ProjectFile pf) { var dataAssets = pf.DataHandle.GetChunk <GMChunkSOND>().List; var agrp = pf.DataHandle.GetChunk <GMChunkAGRP>(); var groups = agrp.List; bool updatedVersion = pf.DataHandle.VersionInfo.IsNumberAtLeast(1, 0, 0, 9999); // First, sort sounds alphabetically List <AssetRef <AssetSound> > sortedSounds = updatedVersion ? pf.Sounds.OrderBy(x => x.Name).ToList() : pf.Sounds; // Get all the AUDO chunk handles in the game GMChunkAUDO defaultChunk = pf.DataHandle.GetChunk <GMChunkAUDO>(); defaultChunk.List.Clear(); Dictionary <string, GMChunkAUDO> audioChunks = new Dictionary <string, GMChunkAUDO>(); Dictionary <string, int> audioChunkIndices = new Dictionary <string, int>(); if (agrp.AudioData != null) { for (int i = 1; i < groups.Count; i++) { if (agrp.AudioData.ContainsKey(i)) { var currChunk = agrp.AudioData[i].GetChunk <GMChunkAUDO>(); currChunk.List.Clear(); audioChunks.Add(groups[i].Name.Content, currChunk); audioChunkIndices.Add(groups[i].Name.Content, i); } } } dataAssets.Clear(); Dictionary <AssetRef <AssetSound>, GMSound> finalMap = new Dictionary <AssetRef <AssetSound>, GMSound>(); for (int i = 0; i < sortedSounds.Count; i++) { AssetSound asset = sortedSounds[i].Asset; if (asset == null) { // This asset was never converted, so handle references and re-add it GMSound s = (GMSound)sortedSounds[i].DataAsset; // Get the group name from the cache var cachedData = (CachedSoundRefData)sortedSounds[i].CachedData; // Potentially handle the internal sound buffer if (cachedData.SoundBuffer != null) { string groupName = cachedData.AudioGroupName; int ind; GMChunkAUDO chunk; if (!audioChunkIndices.TryGetValue(groupName, out ind)) { ind = pf.DataHandle.VersionInfo.BuiltinAudioGroupID; // might be wrong chunk = defaultChunk; } else { chunk = audioChunks[groupName]; } s.GroupID = ind; s.AudioID = chunk.List.Count; chunk.List.Add(new GMAudio() { Data = cachedData.SoundBuffer }); } finalMap[sortedSounds[i]] = s; continue; } GMSound dataAsset = new GMSound() { Name = pf.DataHandle.DefineString(asset.Name), Volume = asset.Volume, Flags = GMSound.AudioEntryFlags.Regular, Effects = 0, Pitch = asset.Pitch, File = pf.DataHandle.DefineString(asset.OriginalSoundFile), Type = (asset.Type != null) ? pf.DataHandle.DefineString(asset.Type) : null }; finalMap[sortedSounds[i]] = dataAsset; switch (asset.Attributes) { case AssetSound.Attribute.CompressedStreamed: if (updatedVersion) { dataAsset.AudioID = -1; } else { dataAsset.AudioID = defaultChunk.List.Count - 1; } dataAsset.GroupID = pf.DataHandle.VersionInfo.BuiltinAudioGroupID; // might be wrong if (asset.SoundFileBuffer != null) { pf.DataHandle.Logger?.Invoke($"Writing sound file \"{asset.SoundFile}\"..."); pf.DataHandle.FileWrites.Post(new (Path.Combine(pf.DataHandle.Directory, asset.SoundFile), asset.SoundFileBuffer)); } break; case AssetSound.Attribute.UncompressOnLoad: case AssetSound.Attribute.Uncompressed: dataAsset.Flags |= GMSound.AudioEntryFlags.IsEmbedded; goto case AssetSound.Attribute.CompressedNotStreamed; case AssetSound.Attribute.CompressedNotStreamed: if (asset.Attributes != AssetSound.Attribute.Uncompressed) { dataAsset.Flags |= GMSound.AudioEntryFlags.IsCompressed; } int ind; GMChunkAUDO chunk; if (!audioChunkIndices.TryGetValue(asset.AudioGroup, out ind)) { ind = pf.DataHandle.VersionInfo.BuiltinAudioGroupID; // might be wrong chunk = defaultChunk; } else { chunk = audioChunks[asset.AudioGroup]; } dataAsset.GroupID = ind; dataAsset.AudioID = chunk.List.Count; chunk.List.Add(new GMAudio() { Data = asset.SoundFileBuffer }); break; } } // Actually add sounds to the data foreach (var assetRef in pf.Sounds) { dataAssets.Add(finalMap[assetRef]); } }