Пример #1
0
        private void ReloadSounds()
        {
            comboSound.Items.Clear();

            var defaultSoundList    = TrCatalog.GetAllSounds(_editor.Tool.DestinationWad.GameVersion);
            var soundCatalogPresent = _editor.Tool.ReferenceLevel != null && _editor.Tool.ReferenceLevel.Settings.GlobalSoundMap.Count > 0;

            var maxKnownSound = -1;

            foreach (var sound in defaultSoundList)
            {
                if (sound.Key > maxKnownSound)
                {
                    maxKnownSound = (int)sound.Key;
                }
            }

            if (soundCatalogPresent)
            {
                foreach (var sound in _editor.Tool.ReferenceLevel.Settings.GlobalSoundMap)
                {
                    if (sound.Id > maxKnownSound)
                    {
                        maxKnownSound = sound.Id;
                    }
                }
            }

            for (int i = 0; i <= maxKnownSound; i++)
            {
                var lbl = i.ToString().PadLeft(4, '0') + ": ";

                if (soundCatalogPresent && _editor.Tool.ReferenceLevel.Settings.GlobalSoundMap.Any(item => item.Id == i))
                {
                    lbl += _editor.Tool.ReferenceLevel.Settings.GlobalSoundMap.First(item => item.Id == i).Name;
                }
                else if (defaultSoundList.Any(item => item.Key == i))
                {
                    lbl += defaultSoundList.First(item => item.Key == i).Value;
                }
                else
                {
                    lbl += "Unknown sound";
                }

                comboSound.Items.Add(lbl);
            }

            comboSound.Items.Add("Custom sound ID");
            comboSound.SelectedIndex = 0;
        }
Пример #2
0
        public FormSkeletonEditor(WadToolClass tool, DeviceManager manager, Wad2 wad, WadMoveableId moveableId)
        {
            InitializeComponent();

            _wad      = wad;
            _moveable = _wad.Moveables[moveableId].Clone();
            _tool     = tool;

            panelRendering.Configuration = _tool.Configuration;
            panelRendering.InitializeRendering(tool, manager);

            _tool.EditorEventRaised += Tool_EditorEventRaised;

            WadMoveable skin;
            var         skinId = new WadMoveableId(TrCatalog.GetMoveableSkin(_tool.DestinationWad.GameVersion, _moveable.Id.TypeId));

            if (_tool.DestinationWad.Moveables.ContainsKey(skinId))
            {
                skin = _tool.DestinationWad.Moveables[skinId];
            }
            else
            {
                skin = _tool.DestinationWad.Moveables[_moveable.Id];
            }

            // Clone the skeleton and load it
            _bones = new List <WadMeshBoneNode>();
            for (int i = 0; i < _moveable.Bones.Count; i++)
            {
                var boneNode = new WadMeshBoneNode(null, skin.Bones[i].Mesh, _moveable.Bones[i]);
                boneNode.Bone.Translation = _moveable.Bones[i].Translation;
                boneNode.GlobalTransform  = Matrix4x4.Identity;
                _bones.Add(boneNode);
            }

            treeSkeleton.Nodes.AddRange(LoadSkeleton());
            ExpandSkeleton();

            panelRendering.Skeleton = _bones;

            if (treeSkeleton.SelectedNodes.Count <= 0)
            {
                treeSkeleton.SelectNode(treeSkeleton.Nodes[0]);
                panelRendering.SelectedNode = (WadMeshBoneNode)treeSkeleton.SelectedNodes[0].Tag;
            }

            panelRendering.Invalidate();
        }
Пример #3
0
        private void dgvStateChanges_CellFormattingSafe(object sender, DarkUI.Controls.DarkDataGridViewSafeCellFormattingEventArgs e)
        {
            if (!(e.Row.DataBoundItem is WadStateChangeRow))
            {
                return;
            }

            if (e.ColumnIndex == 0)
            {
                e.CellStyle.ForeColor = Colors.DisabledText.Multiply(0.8f);
            }
            else if (e.ColumnIndex == 4)
            {
                var item = (WadStateChangeRow)e.Row.DataBoundItem;
                var cell = dgvStateChanges.Rows[e.RowIndex].Cells[e.ColumnIndex];
                cell.ToolTipText = TrCatalog.GetAnimationName(_editor.Tool.DestinationWad.GameVersion, _editor.Moveable.Id.TypeId, item.NextAnimation);
            }
        }
Пример #4
0
        private void Initialize(AnimationNode animation, WadStateChange newStateChange)
        {
            if (_initializing)
            {
                return;
            }
            _initializing = true;

            _animation = animation;

            _backupStates = new List <WadStateChange>();
            foreach (var sc in animation.WadAnimation.StateChanges)
            {
                _backupStates.Add(sc.Clone());
            }

            lblStateChangeAnnouncement.Text = string.Empty;
            dgvStateChanges.Rows.Clear();

            var rows = new List <WadStateChangeRow>();

            foreach (var sc in _animation.WadAnimation.StateChanges)
            {
                foreach (var d in sc.Dispatches)
                {
                    rows.Add(new WadStateChangeRow(TrCatalog.GetStateName(_editor.Tool.DestinationWad.GameVersion, _editor.Moveable.Id.TypeId, sc.StateId), sc.StateId, d.InFrame, d.OutFrame, d.NextAnimation, d.NextFrame));
                }
            }

            if (newStateChange != null && newStateChange.Dispatches.Count == 1)
            {
                rows.Add(new WadStateChangeRow(TrCatalog.GetStateName(_editor.Tool.DestinationWad.GameVersion, _editor.Moveable.Id.TypeId, newStateChange.StateId),
                                               newStateChange.StateId,
                                               newStateChange.Dispatches[0].InFrame,
                                               newStateChange.Dispatches[0].OutFrame,
                                               newStateChange.Dispatches[0].NextAnimation,
                                               newStateChange.Dispatches[0].NextFrame));
                _createdNew = true;
            }

            dgvStateChanges.DataSource = new BindingList <WadStateChangeRow>(new List <WadStateChangeRow>(rows));

            _initializing = false;
        }
Пример #5
0
        public FormStateChangesEditor(AnimationEditor editor, AnimationNode animation, WadStateChange newStateChange = null)
        {
            InitializeComponent();

            _editor = editor;

            dgvControls.CreateNewRow = () => new WadStateChangeRow()
            {
                StateName = TrCatalog.GetStateName(_editor.Tool.DestinationWad.GameVersion, _editor.Moveable.Id.TypeId, 0)
            };
            dgvControls.DataGridView = dgvStateChanges;
            dgvControls.Enabled      = true;

            Initialize(animation, newStateChange);
            _editor.Tool.EditorEventRaised += Tool_EditorEventRaised;

            // Set window property handlers
            Configuration.LoadWindowProperties(this, _editor.Tool.Configuration);
            FormClosing += new FormClosingEventHandler((s, e) => Configuration.SaveWindowProperties(this, _editor.Tool.Configuration));
        }
Пример #6
0
        private void FindLaraSkin()
        {
            if (comboItems.Items.Count == 0 || comboItems.SelectedIndex < 0 || !(comboItems.SelectedItem is WadMoveable))
            {
                return;
            }

            var item = comboItems.SelectedItem as WadMoveable;

            if (item.Id == WadMoveableId.Lara) // Show Lara's skin
            {
                var skinId       = new WadMoveableId(TrCatalog.GetMoveableSkin(_editor.Level.Settings.GameVersion, item.Id.TypeId));
                var moveableSkin = _editor.Level.Settings.WadTryGetMoveable(skinId);
                if (moveableSkin != null)
                {
                    panelItem.CurrentObject = moveableSkin;
                }

                panelItem.Invalidate();
            }
        }
Пример #7
0
        private void ReloadSlots()
        {
            // Decide on ID type
            if (TypeClass == typeof(WadMoveableId))
            {
                PopulateSlots(TrCatalog.GetAllMoveables(GameVersion).Where(item => !_wad.Moveables.Any(moveable => moveable.Key.TypeId == item.Key) && !(_additionalObjectsToHide?.Any(add => add == item.Key) ?? false)).ToList());
            }
            else if (TypeClass == typeof(WadStaticId))
            {
                PopulateSlots(TrCatalog.GetAllStatics(GameVersion).Where(item => !_wad.Statics.Any(stat => stat.Key.TypeId == item.Key) && !(_additionalObjectsToHide?.Any(add => add == item.Key) ?? false)).ToList());
            }
            else if (TypeClass == typeof(WadSpriteSequenceId))
            {
                PopulateSlots(TrCatalog.GetAllSpriteSequences(GameVersion).Where(item => !_wad.SpriteSequences.Any(sprite => sprite.Key.TypeId == item.Key) && !(_additionalObjectsToHide?.Any(add => add == item.Key) ?? false)).ToList());
            }
            else
            {
                throw new NotImplementedException("The " + TypeClass + " is not implemented yet.");
            }

            // Make sure it redraws
            lstSlots.Invalidate();
        }
Пример #8
0
        private void dgvStateChanges_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {
            if (e.ColumnIndex == 0)
            {
                return;
            }

            try
            {
                var cell = dgvStateChanges.Rows[e.RowIndex].Cells[e.ColumnIndex];
                var name = dgvStateChanges.Columns[e.ColumnIndex].Name;

                // For some reason, validating against UInt16 type results in unrecoverable DGV exception on
                // wrong incoming values, so we're validating against Int16 and filtering out negative values afterwards.

                Int16 parsedValue = 0;
                if (e.FormattedValue == null || !Int16.TryParse(e.FormattedValue.ToString(), out parsedValue))
                {
                    if (!Int16.TryParse(cell.Value.ToString(), out parsedValue))
                    {
                        parsedValue = 0;
                    }
                }

                var limit = Int16.MaxValue;

                if (name == columnNextAnimation.Name)
                {
                    limit = (Int16)(_editor.Animations.Count - 1);
                }
                else if (name == columnNextFrame.Name)
                {
                    Int16 limitNew = 0;
                    if (Int16.TryParse(dgvStateChanges.Rows[e.RowIndex].Cells[4].Value.ToString(), out limitNew))
                    {
                        limit = (Int16)(_editor.GetRealNumberOfFrames(limitNew));
                    }
                }
                else if (name == columnLowFrame.Name)
                {
                    Int16 limitNew = 0;
                    if (Int16.TryParse(dgvStateChanges.Rows[e.RowIndex].Cells[3].Value.ToString(), out limitNew))
                    {
                        limit = limitNew;
                    }
                }
                else if (name == columnHighFrame.Name)
                {
                    limit = (Int16)(_editor.GetRealNumberOfFrames() - 1);
                }

                if (parsedValue > limit)
                {
                    cell.Value = limit;
                }
                else if (parsedValue < 0)
                {
                    cell.Value = (Int16)0;
                }
                else
                {
                    cell.Value = parsedValue;
                }

                if (name == columnStateId.Name)
                {
                    dgvStateChanges.Rows[e.RowIndex].Cells[0].Value = TrCatalog.GetStateName(_editor.Tool.DestinationWad.GameVersion, _editor.Moveable.Id.TypeId, (uint)parsedValue);
                }
            }
            catch (Exception ex) { }
        }
Пример #9
0
        public static bool ConvertWad2ToNewSoundFormat(string src, string dest)
        {
            /* PROCEDURE:
             * 1. Collect all sounds from Wad2
             * 2. Initialise the list of SoundInfoConversionRow, getting ID from TrCatalog
             * 3. Show the dialog to the user. Here he can load an additional catalog if he changed sounds via TRLE tools.
             *    He can also choose which sounds to export to Xml and if export also samples.
             * 4. Assign new IDs to sound infos
             * 5. Remap sounds in animcommands
             * 6. Optionally export samples if needed and bind them to sound infos
             * 7. Save Wad2 + Xml (if sounds are present)
             */

            try
            {
                // Load Wad2
                Wad2 wad = Wad2Loader.LoadFromFile(src, false);

                // Check if the Wad2 needs to be converted
                if (wad.SoundSystem != SoundSystem.Dynamic)
                {
                    return(true);
                }

                // Now collect all sound infos from obsolete lists and build a new list
                var soundInfos = wad.AllLoadedSoundInfos.Values.ToList();

                // Loop through each sound info and try to get the classic Id from TrCatalog.xml
                var conversionList = new List <SoundInfoConversionRow>();
                foreach (var soundInfo in soundInfos)
                {
                    var row = new SoundInfoConversionRow(soundInfo, soundInfo.Name);

                    // If user has changed name, result will be -1 and the user will need to manually set the new sound id
                    row.NewId = TrCatalog.TryGetSoundInfoIdByDescription(wad.GameVersion, soundInfo.Name);
                    if (row.NewId != -1)
                    {
                        row.NewName = TrCatalog.GetOriginalSoundName(wad.GameVersion, (uint)row.NewId);
                    }

                    conversionList.Add(row);
                }

                // Now we'll show a dialog with all conversion rows and the user will need to make some choices
                WadSounds sounds = null;
                using (var form = new Wad2SoundsConversionDialog(wad.GameVersion, conversionList))
                {
                    if (form.ShowDialog() == DialogResult.Cancel)
                    {
                        return(false);
                    }

                    // If the user has loaded a custom catalog, let's get a pointer to it
                    if (form.Sounds != null)
                    {
                        sounds = form.Sounds;
                    }
                }

                // Assign new Id and name
                foreach (var row in conversionList)
                {
                    row.SoundInfo.Id   = row.NewId;
                    row.SoundInfo.Name = row.NewName;
                }

                // Remap all sounds in animcommands
                foreach (var row in conversionList)
                {
                    if (row.NewId != -1)
                    {
                        foreach (var moveable in wad.Moveables)
                        {
                            foreach (var animation in moveable.Value.Animations)
                            {
                                foreach (var cmd in animation.AnimCommands)
                                {
                                    if (cmd.SoundInfoObsolete != null && cmd.SoundInfoObsolete == row.SoundInfo)
                                    {
                                        cmd.Parameter2        = (short)((cmd.Parameter2 & 0xc000) | row.NewId);
                                        cmd.SoundInfoObsolete = null;
                                    }
                                }
                            }
                        }
                    }
                }

                // Bind samples (only if additional catalog was loaded, TrCatalog has not samples names)
                if (sounds != null)
                {
                    foreach (var row in conversionList)
                    {
                        if (row.SaveToXml)
                        {
                            if (row.ExportSamples)
                            {
                                // We export samples only if user has marked both Export to Xml and Export samples checkboxes
                                var samples = new List <string>();
                                foreach (var sample in row.SoundInfo.Samples)
                                {
                                    if (sample.IsLoaded)
                                    {
                                        // If sample is valid, export the .WAV file to the same directory of Wad2
                                        string sampleName = row.NewName.ToLower() + "_" + row.SoundInfo.Samples.IndexOf(sample) + ".wav";
                                        samples.Add(sampleName);
                                        File.WriteAllBytes(Path.GetDirectoryName(dest) + "\\" + sampleName, sample.Data);
                                    }
                                }

                                // Assign new samples names
                                row.SoundInfo.Samples.Clear();
                                foreach (var sample in samples)
                                {
                                    row.SoundInfo.Samples.Add(new WadSample(sample));
                                }
                            }
                            else
                            {
                                // If samples count is the same then use samples from catalog
                                var refInfo = sounds.TryGetSoundInfo(row.NewId);

                                if (refInfo != null && row.SoundInfo.Samples.Count == refInfo.Samples.Count)
                                {
                                    row.SoundInfo.Samples.Clear();
                                    row.SoundInfo.Samples.AddRange(refInfo.Samples);
                                }
                                else
                                {
                                    row.SoundInfo.Samples.Clear();
                                }
                            }
                        }
                    }
                }

                // Create the new sounds archive
                foreach (var row in conversionList)
                {
                    if (row.SaveToXml)
                    {
                        wad.Sounds.SoundInfos.Add(row.SoundInfo);
                    }
                }

                // Sort sound infos
                wad.Sounds.SoundInfos.Sort((a, b) => a.Id.CompareTo(b.Id));

                // Make a backup copy
                if (src == dest)
                {
                    int    index          = 0;
                    string backupFilename = "";
                    while (true)
                    {
                        backupFilename = dest + "." + index + ".bak";
                        if (!File.Exists(backupFilename))
                        {
                            break;
                        }
                        index++;
                    }

                    File.Copy(src, backupFilename, true);
                }

                // Save Wad2 with Xml sounds
                wad.SoundSystem = SoundSystem.Xml;
                Wad2Writer.SaveToFile(wad, dest);

                // Finished!
                return(true);
            }
            catch (Exception e)
            {
                return(false);
            }
        }
Пример #10
0
        private void Tool_EditorEventRaised(IEditorEvent obj)
        {
            if (obj is InitEvent)
            {
                // At startup initialise a new Wad2
                if (_tool.Configuration.Tool_MakeEmptyWadAtStartup)
                {
                    _tool.DestinationWad = new Wad2 {
                        GameVersion = TRVersion.Game.TR4
                    };
                    _tool.RaiseEvent(new WadToolClass.DestinationWadChangedEvent());
                }
            }

            if (obj is WadToolClass.MessageEvent)
            {
                var msg = (WadToolClass.MessageEvent)obj;
                PopUpInfo.Show(popup, this, panel3D, msg.Message, msg.Type);
            }

            if (obj is WadToolClass.SelectedObjectEditedEvent || obj is InitEvent)
            {
            }

            if (obj is WadToolClass.DestinationWadChangedEvent || obj is InitEvent)
            {
                treeDestWad.Wad = _tool.DestinationWad;
                treeDestWad.UpdateContent();

                panel3D.UpdateAnimationScrollbar();
                panel3D.Invalidate();

                if (_tool.DestinationWad != null)
                {
                    labelStatistics.Text = "Moveables: " + _tool.DestinationWad.Moveables.Count + " | " +
                                           "Statics: " + _tool.DestinationWad.Statics.Count + " | " +
                                           "Sprites sequences: " + _tool.DestinationWad.SpriteSequences.Count + " | " +
                                           "Textures: " + _tool.DestinationWad.MeshTexturesUnique.Count;
                }
                else
                {
                    labelStatistics.Text = "";
                }
            }

            if (obj is WadToolClass.SourceWadChangedEvent || obj is InitEvent)
            {
                panelSource.SectionHeader = "Source";
                if (_tool?.SourceWad != null)
                {
                    panelSource.SectionHeader += (String.IsNullOrEmpty(_tool.SourceWad.FileName) ? " (Imported)" : " (" + Path.GetFileName(_tool.SourceWad.FileName) + ")");
                }

                treeSourceWad.Wad = _tool.SourceWad;
                treeSourceWad.UpdateContent();

                panel3D.UpdateAnimationScrollbar();
                panel3D.Invalidate();
            }

            if (obj is WadToolClass.MainSelectionChangedEvent ||
                obj is WadToolClass.DestinationWadChangedEvent ||
                obj is WadToolClass.SourceWadChangedEvent || obj is InitEvent)
            {
                var mainSelection = _tool.MainSelection;
                if (mainSelection == null)
                {
                    panel3D.CurrentObject = null;

                    butEditAnimations.Visible     = false;
                    butEditSkeleton.Visible       = false;
                    butEditStaticModel.Visible    = false;
                    butEditSpriteSequence.Visible = false;
                }
                else
                {
                    Wad2 wad = _tool.GetWad(mainSelection.Value.WadArea);

                    // Display the object (or set it to Lara's skin instead if it's Lara)
                    if (mainSelection.Value.Id is WadMoveableId)
                    {
                        panel3D.CurrentObject = wad.TryGet(new WadMoveableId(TrCatalog.GetMoveableSkin(wad.GameVersion, ((WadMoveableId)mainSelection.Value.Id).TypeId)));
                    }
                    else
                    {
                        panel3D.CurrentObject = wad.TryGet(mainSelection.Value.Id);
                    }

                    panel3D.AnimationIndex = 0;
                    panel3D.KeyFrameIndex  = 0;

                    // Update the toolbar below the rendering area
                    butEditAnimations.Visible     = (mainSelection.Value.Id is WadMoveableId);
                    butEditSkeleton.Visible       = (mainSelection.Value.Id is WadMoveableId);
                    butEditStaticModel.Visible    = (mainSelection.Value.Id is WadStaticId);
                    butEditSpriteSequence.Visible = (mainSelection.Value.Id is WadSpriteSequenceId);

                    panel3D.ResetCamera();
                    panel3D.Invalidate();
                }

                panel3D.UpdateAnimationScrollbar();
                panel3D.Invalidate();
            }

            if (obj is WadToolClass.ReferenceLevelChangedEvent)
            {
                if (_tool.ReferenceLevel != null)
                {
                    butCloseRefLevel.Enabled = true;
                    lblRefLevel.Enabled      = true;
                    closeReferenceLevelToolStripMenuItem.Enabled = true;
                    lblRefLevel.Text = Path.GetFileNameWithoutExtension(_tool.ReferenceLevel.Settings.LevelFilePath);
                }
                else
                {
                    butCloseRefLevel.Enabled = false;
                    lblRefLevel.Enabled      = false;
                    closeReferenceLevelToolStripMenuItem.Enabled = false;
                    lblRefLevel.Text = "(project not loaded)";
                }
            }

            if (obj is WadToolClass.UnsavedChangesEvent)
            {
                UpdateSaveUI(((WadToolClass.UnsavedChangesEvent)obj).UnsavedChanges);
            }
        }
Пример #11
0
        public static WadMoveable ConvertTrLevelMoveableToWadMoveable(Wad2 wad, TrLevel oldLevel, int moveableIndex,
                                                                      TextureArea[] objectTextures)
        {
            Console.WriteLine("Converting Moveable " + moveableIndex);

            var         oldMoveable = oldLevel.Moveables[moveableIndex];
            WadMoveable newMoveable = new WadMoveable(new WadMoveableId(oldMoveable.ObjectID));

            // First a list of meshes for this moveable is built
            var oldMeshes = new List <tr_mesh>();

            for (int j = 0; j < oldMoveable.NumMeshes; j++)
            {
                oldMeshes.Add(oldLevel.Meshes[(int)oldLevel.RealPointers[(int)(oldMoveable.StartingMesh + j)]]);
            }

            // Convert the WadMesh
            var newMeshes = new List <WadMesh>();

            foreach (var oldMesh in oldMeshes)
            {
                newMeshes.Add(ConvertTrLevelMeshToWadMesh(wad, oldLevel, oldMesh, objectTextures));
            }

            // Build the skeleton
            var root = new WadBone();

            root.Name        = "bone_0_root";
            root.Parent      = null;
            root.Translation = Vector3.Zero;
            root.Mesh        = newMeshes[0];

            newMoveable.Bones.Add(root);

            for (int j = 0; j < oldMoveable.NumMeshes - 1; j++)
            {
                var bone = new WadBone();
                bone.Name        = "bone_" + (j + 1).ToString();
                bone.Parent      = null;
                bone.Translation = Vector3.Zero;
                bone.Mesh        = newMeshes[j + 1];
                newMoveable.Bones.Add(bone);
            }

            for (int mi = 0; mi < (oldMeshes.Count - 1); mi++)
            {
                int j = mi + 1;

                var opcode = (WadLinkOpcode)oldLevel.MeshTrees[(int)(oldMoveable.MeshTree + mi * 4)];
                int linkX  = oldLevel.MeshTrees[(int)(oldMoveable.MeshTree + mi * 4) + 1];
                int linkY  = -oldLevel.MeshTrees[(int)(oldMoveable.MeshTree + mi * 4) + 2];
                int linkZ  = oldLevel.MeshTrees[(int)(oldMoveable.MeshTree + mi * 4) + 3];

                newMoveable.Bones[j].OpCode      = opcode;
                newMoveable.Bones[j].Translation = new Vector3(linkX, linkY, linkZ);
            }

            // Convert animations
            int numAnimations = 0;
            int nextMoveable  = oldLevel.GetNextMoveableWithAnimations(moveableIndex);

            if (nextMoveable == -1)
            {
                numAnimations = oldLevel.Animations.Count - oldMoveable.Animation;
            }
            else
            {
                numAnimations = oldLevel.Moveables[nextMoveable].Animation - oldMoveable.Animation;
            }

            var frameBases = new Dictionary <WadAnimation, ushort>();

            for (int j = 0; j < numAnimations; j++)
            {
                if (oldMoveable.Animation == -1)
                {
                    break;
                }

                WadAnimation newAnimation = new WadAnimation();
                var          oldAnimation = oldLevel.Animations[j + oldMoveable.Animation];
                newAnimation.FrameRate     = oldAnimation.FrameRate;
                newAnimation.NextAnimation = (ushort)(oldAnimation.NextAnimation - oldMoveable.Animation);
                newAnimation.NextFrame     = oldAnimation.NextFrame;
                newAnimation.StateId       = oldAnimation.StateID;
                newAnimation.EndFrame      = (ushort)(oldAnimation.FrameEnd - oldAnimation.FrameStart);
                newAnimation.Name          = TrCatalog.GetAnimationName(oldLevel.Version, oldMoveable.ObjectID, (uint)j);

                for (int k = 0; k < oldAnimation.NumStateChanges; k++)
                {
                    WadStateChange sc    = new WadStateChange();
                    var            wadSc = oldLevel.StateChanges[(int)oldAnimation.StateChangeOffset + k];
                    sc.StateId = wadSc.StateID;

                    for (int n = 0; n < wadSc.NumAnimDispatches; n++)
                    {
                        WadAnimDispatch ad    = new WadAnimDispatch();
                        var             wadAd = oldLevel.AnimDispatches[(int)wadSc.AnimDispatch + n];

                        ad.InFrame       = (ushort)(wadAd.Low - oldAnimation.FrameStart);
                        ad.OutFrame      = (ushort)(wadAd.High - oldAnimation.FrameStart);
                        ad.NextAnimation = (ushort)((wadAd.NextAnimation - oldMoveable.Animation) % numAnimations);
                        ad.NextFrame     = (ushort)wadAd.NextFrame;

                        sc.Dispatches.Add(ad);
                    }

                    newAnimation.StateChanges.Add(sc);
                }

                if (oldAnimation.NumAnimCommands < oldLevel.AnimCommands.Count)
                {
                    int lastCommand = oldAnimation.AnimCommand;

                    for (int k = 0; k < oldAnimation.NumAnimCommands; k++)
                    {
                        short commandType = oldLevel.AnimCommands[lastCommand + 0];

                        WadAnimCommand command = new WadAnimCommand {
                            Type = (WadAnimCommandType)commandType
                        };
                        switch (commandType)
                        {
                        case 1:
                            command.Parameter1 = (short)oldLevel.AnimCommands[lastCommand + 1];
                            command.Parameter2 = (short)oldLevel.AnimCommands[lastCommand + 2];
                            command.Parameter3 = (short)oldLevel.AnimCommands[lastCommand + 3];

                            lastCommand += 4;
                            break;

                        case 2:
                            command.Parameter1 = (short)oldLevel.AnimCommands[lastCommand + 1];
                            command.Parameter2 = (short)oldLevel.AnimCommands[lastCommand + 2];

                            lastCommand += 3;
                            break;

                        case 3:
                            lastCommand += 1;
                            break;

                        case 4:
                            lastCommand += 1;
                            break;

                        case 5:
                            command.Parameter1 = (short)(oldLevel.AnimCommands[lastCommand + 1] - oldAnimation.FrameStart);
                            command.Parameter2 = (short)oldLevel.AnimCommands[lastCommand + 2];
                            lastCommand       += 3;
                            break;

                        case 6:
                            command.Parameter1 = (short)(oldLevel.AnimCommands[lastCommand + 1] - oldAnimation.FrameStart);
                            command.Parameter2 = (short)oldLevel.AnimCommands[lastCommand + 2];
                            lastCommand       += 3;
                            break;

                        default:     // Ignore invalid anim commands (see for example karnak.wad)
                            logger.Warn("Unknown anim command " + commandType);
                            goto ExitForLoop;
                        }

                        newAnimation.AnimCommands.Add(command);
                    }
ExitForLoop:
                    ;
                }

                int  frames = (int)oldAnimation.FrameOffset / 2;
                uint numFrames;

                if (j + oldMoveable.Animation == oldLevel.Animations.Count - 1)
                {
                    if (oldAnimation.FrameSize == 0)
                    {
                        numFrames = oldLevel.Version == TRVersion.Game.TR1 ? (uint)((newAnimation.EndFrame + 1) / newAnimation.FrameRate) : 0;
                    }
                    else
                    {
                        numFrames = ((uint)(2 * oldLevel.Frames.Count) - oldAnimation.FrameOffset) / (uint)(2 * oldAnimation.FrameSize);
                    }
                }
                else
                {
                    if (oldAnimation.FrameSize == 0)
                    {
                        numFrames = oldLevel.Version == TRVersion.Game.TR1 ? (uint)((newAnimation.EndFrame + 1) / newAnimation.FrameRate) : 0;
                    }
                    else
                    {
                        numFrames = (oldLevel.Animations[oldMoveable.Animation + j + 1].FrameOffset - oldAnimation.FrameOffset) / (uint)(2 * oldAnimation.FrameSize);
                    }
                }

                for (int f = 0; f < numFrames; f++)
                {
                    WadKeyFrame frame        = new WadKeyFrame();
                    int         startOfFrame = frames;

                    frame.BoundingBox = new BoundingBox(new Vector3(oldLevel.Frames[frames],
                                                                    -oldLevel.Frames[frames + 2],
                                                                    oldLevel.Frames[frames + 4]),
                                                        new Vector3(oldLevel.Frames[frames + 1],
                                                                    -oldLevel.Frames[frames + 3],
                                                                    oldLevel.Frames[frames + 5]));

                    frames += 6;

                    frame.Offset = new Vector3(oldLevel.Frames[frames],
                                               (short)(-oldLevel.Frames[frames + 1]),
                                               oldLevel.Frames[frames + 2]);

                    frames += 3;

                    // TR1 has also the number of angles to follow
                    if (oldLevel.Version == TRVersion.Game.TR1)
                    {
                        frames++;
                    }

                    for (int n = 0; n < oldMoveable.NumMeshes; n++)
                    {
                        frame.Angles.Add(
                            WadKeyFrameRotation.FromTrAngle(ref frames, oldLevel.Frames,
                                                            oldLevel.Version == TRVersion.Game.TR1,
                                                            oldLevel.Version == TRVersion.Game.TR4 || oldLevel.Version == TRVersion.Game.TR5));
                    }

                    if ((frames - startOfFrame) < oldAnimation.FrameSize)
                    {
                        frames += ((int)oldAnimation.FrameSize - (frames - startOfFrame));
                    }

                    newAnimation.KeyFrames.Add(frame);
                }

                frameBases.Add(newAnimation, oldAnimation.FrameStart);

                // New velocities
                float acceleration = oldAnimation.Accel / 65536.0f;
                newAnimation.StartVelocity = oldAnimation.Speed / 65536.0f;

                float lateralAcceleration = oldAnimation.AccelLateral / 65536.0f;
                newAnimation.StartLateralVelocity = oldAnimation.SpeedLateral / 65536.0f;

                if (newAnimation.KeyFrames.Count != 0 && newAnimation.FrameRate != 0)
                {
                    newAnimation.EndVelocity = newAnimation.StartVelocity + acceleration *
                                               (newAnimation.KeyFrames.Count - 1) * newAnimation.FrameRate;
                    newAnimation.EndLateralVelocity = newAnimation.StartLateralVelocity + lateralAcceleration *
                                                      (newAnimation.KeyFrames.Count - 1) * newAnimation.FrameRate;
                }

                newMoveable.Animations.Add(newAnimation);
            }

            for (int i = 0; i < newMoveable.Animations.Count; i++)
            {
                var animation = newMoveable.Animations[i];

                if (animation.KeyFrames.Count == 0)
                {
                    animation.EndFrame = 0;
                }

                // HACK: this fixes some invalid NextAnimations values
                animation.NextAnimation %= (ushort)newMoveable.Animations.Count;

                newMoveable.Animations[i] = animation;
            }

            for (int i = 0; i < newMoveable.Animations.Count; i++)
            {
                var animation = newMoveable.Animations[i];

                // HACK: this fixes some invalid NextFrame values
                if (frameBases[newMoveable.Animations[animation.NextAnimation]] != 0)
                {
                    animation.NextFrame %= frameBases[newMoveable.Animations[animation.NextAnimation]];
                }

                foreach (var stateChange in animation.StateChanges)
                {
                    for (int J = 0; J < stateChange.Dispatches.Count; ++J)
                    {
                        WadAnimDispatch animDispatch = stateChange.Dispatches[J];
                        if (frameBases[newMoveable.Animations[animDispatch.NextAnimation]] != 0)
                        {
                            ushort newFrame = (ushort)(animDispatch.NextFrame % frameBases[newMoveable.Animations[animDispatch.NextAnimation]]);

                            // In some cases dispatches have invalid NextFrame.
                            // From tests it seems that's ok to delete the dispatch or put the NextFrame equal to zero.
                            if (newFrame > newMoveable.Animations[animDispatch.NextAnimation].EndFrame)
                            {
                                newFrame = 0;
                            }

                            animDispatch.NextFrame = newFrame;
                        }
                        stateChange.Dispatches[J] = animDispatch;
                    }
                }

                newMoveable.Animations[i] = animation;
            }

            return(newMoveable);
        }
Пример #12
0
 public string ToString(TRVersion.Game gameVersion)
 {
     return("(" + TypeId + ") " + TrCatalog.GetOriginalSoundName(gameVersion, TypeId));
 }
Пример #13
0
        public static Wad2 ConvertWad2ToTR5Main(WadToolClass tool, IWin32Window owner, Wad2 src)
        {
            Wad2 dest = new Wad2();

            dest.GameVersion = TRVersion.Game.TR5Main;
            dest.SoundSystem = SoundSystem.Xml;

            foreach (var moveable in src.Moveables)
            {
                var compatibleSlot = TrCatalog.GetMoveableTR5MainSlot(src.GameVersion, moveable.Key.TypeId);
                if (compatibleSlot == "")
                {
                    continue;
                }

                bool isMoveable = false;
                var  destId     = TrCatalog.GetItemIndex(TRVersion.Game.TR5Main, compatibleSlot, out isMoveable);
                if (!destId.HasValue)
                {
                    continue;
                }

                var newId = new WadMoveableId(destId.Value);

                foreach (var animation in moveable.Value.Animations)
                {
                    foreach (var command in animation.AnimCommands)
                    {
                        if (command.Type == WadAnimCommandType.PlaySound)
                        {
                            int id = command.Parameter2 & 0x3FFF;
                            id += TrCatalog.GetTR5MainSoundMapStart(src.GameVersion);
                            command.Parameter2 = (short)((command.Parameter2 & 0xC000) | (id & 0x3FFF));
                        }
                    }
                }

                dest.Add(newId, moveable.Value);
            }

            foreach (var sequence in src.SpriteSequences)
            {
                var compatibleSlot = TrCatalog.GetSpriteSequenceTR5MainSlot(src.GameVersion, sequence.Key.TypeId);
                if (compatibleSlot == "")
                {
                    continue;
                }

                bool isMoveable = false;
                var  destId     = TrCatalog.GetItemIndex(TRVersion.Game.TR5Main, compatibleSlot, out isMoveable);
                if (!destId.HasValue)
                {
                    continue;
                }

                var newId = new WadSpriteSequenceId(destId.Value);

                dest.Add(newId, sequence.Value);
            }

            foreach (var staticObject in src.Statics)
            {
                dest.Add(staticObject.Key, staticObject.Value);
            }

            return(dest);
        }
Пример #14
0
        public static void Main(string[] args)
        {
            string           startFile      = null;
            string           batchFile      = null;
            bool             doBatchCompile = false;
            BatchCompileList batchList      = null;

            if (args.Length >= 1)
            {
                // Open files on start
                if (args[0].EndsWith(".prj2", StringComparison.InvariantCultureIgnoreCase))
                {
                    startFile = args[0];
                }

                // Batch-compile levels
                if (args[0].EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase))
                {
                    batchFile      = args[0];
                    batchList      = BatchCompileList.ReadFromXml(batchFile);
                    doBatchCompile = batchList?.Files.Count > 0;
                }
            }

            // Load configuration
            var initialEvents = new List <LogEventInfo>();
            var configuration = new Configuration().LoadOrUseDefault <Configuration>(initialEvents);

            // Update DarkUI configuration
            Colors.Brightness = configuration.UI_FormColor_Brightness / 100.0f;

            if (configuration.Editor_AllowMultipleInstances || doBatchCompile ||
                mutex.WaitOne(TimeSpan.Zero, true))
            {
                // Setup logging
                using (var log = new Logging(configuration.Log_MinLevel, configuration.Log_WriteToFile, configuration.Log_ArchiveN, initialEvents))
                {
                    // Create configuration file
                    configuration.SaveTry();

                    // Setup application
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
                    Application.ThreadException += (sender, e) =>
                    {
                        log.HandleException(e.Exception);
                        using (var dialog = new ThreadExceptionDialog(e.Exception))
                            if (dialog.ShowDialog() == DialogResult.Abort)
                            {
                                Environment.Exit(1);
                            }
                    };
                    Application.AddMessageFilter(new ControlScrollFilter());
                    SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());

                    if (!File.Exists(Application.StartupPath + "\\Catalogs\\TrCatalog.xml") ||
                        !File.Exists(Application.StartupPath + "\\Catalogs\\NgCatalog.xml"))
                    {
                        MessageBox.Show("One of the catalog files is missing.\nMake sure you have TrCatalog.xml and NgCatalog.xml in /Catalogs/ subfolder.");
                        Environment.Exit(1);
                    }

                    // Load catalogs
                    TrCatalog.LoadCatalog(Application.StartupPath + "\\Catalogs\\TrCatalog.xml");
                    NgCatalog.LoadCatalog(Application.StartupPath + "\\Catalogs\\NgCatalog.xml");

                    // Run
                    Editor editor = new Editor(SynchronizationContext.Current, configuration);
                    Editor.Instance = editor;

                    // Run editor normally if no batch compile is pending.
                    // Otherwise, don't load main form and jump straight to batch-compiling levels.

                    if (!doBatchCompile)
                    {
                        using (FormMain form = new FormMain(editor))
                        {
                            form.Show();

                            if (!string.IsNullOrEmpty(startFile)) // Open files on start
                            {
                                if (startFile.EndsWith(".prj", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    EditorActions.OpenLevelPrj(form, startFile);
                                }
                                else
                                {
                                    EditorActions.OpenLevel(form, startFile);
                                }
                            }
                            else if (editor.Configuration.Editor_OpenLastProjectOnStartup)
                            {
                                if (Properties.Settings.Default.RecentProjects != null && Properties.Settings.Default.RecentProjects.Count > 0 &&
                                    File.Exists(Properties.Settings.Default.RecentProjects[0]))
                                {
                                    EditorActions.OpenLevel(form, Properties.Settings.Default.RecentProjects[0]);
                                }
                            }
                            Application.Run(form);
                        }
                    }
                    else
                    {
                        EditorActions.BuildInBatch(editor, batchList, batchFile);
                    }
                }
            }
            else if (startFile != null) // Send opening file to existing editor instance
            {
                SingleInstanceManagement.Send(Process.GetCurrentProcess(), new List <string>()
                {
                    ".prj2"
                }, startFile);
            }
            else // Just bring editor to top, if user tries to launch another copy
            {
                SingleInstanceManagement.Bump(Process.GetCurrentProcess());
            }
        }
Пример #15
0
        private static Wad2 LoadWad2(ChunkReader chunkIO, bool obsolete)
        {
            if (obsolete)
            {
                LEB128.ReadUInt(chunkIO.Raw);
            }
            var wad = new Wad2();

            Dictionary <long, WadTexture>   textures   = null;
            Dictionary <long, WadSample>    samples    = null;
            Dictionary <long, WadSoundInfo> soundInfos = null;
            Dictionary <long, WadSprite>    sprites    = null;

            wad.SoundSystem = SoundSystem.Dynamic;

            chunkIO.ReadChunks((id, chunkSize) =>
            {
                if (id == Wad2Chunks.GameVersion)
                {
                    wad.GameVersion = (TRVersion.Game)chunkIO.ReadChunkLong(chunkSize);
                    return(true);
                }
                else if (id == Wad2Chunks.SoundSystem)
                {
                    wad.SoundSystem = (SoundSystem)chunkIO.ReadChunkLong(chunkSize);
                    return(true);
                }
                else if (LoadTextures(chunkIO, id, wad, ref textures))
                {
                    return(true);
                }
                else if (LoadSamples(chunkIO, id, wad, ref samples, obsolete))
                {
                    return(true);
                }
                else if (LoadSoundInfos(chunkIO, id, wad, ref soundInfos, samples))
                {
                    return(true);
                }
                else if (LoadFixedSoundInfos(chunkIO, id, wad, soundInfos))
                {
                    return(true);
                }
                else if (LoadAdditionalSoundInfos(chunkIO, id, wad, soundInfos, samples))
                {
                    return(true);
                }
                else if (LoadSprites(chunkIO, id, wad, ref sprites))
                {
                    return(true);
                }
                else if (LoadSpriteSequences(chunkIO, id, wad, sprites))
                {
                    return(true);
                }
                else if (LoadMoveables(chunkIO, id, wad, soundInfos, textures))
                {
                    return(true);
                }
                else if (LoadStatics(chunkIO, id, wad, textures))
                {
                    return(true);
                }
                return(false);
            });

            if (obsolete)
            {
                foreach (KeyValuePair <long, WadSoundInfo> soundInfo in soundInfos)
                {
                    if (TrCatalog.IsSoundFixedByDefault(TRVersion.Game.TR4, checked ((uint)soundInfo.Key)))
                    {
                        var Id = new WadFixedSoundInfoId(checked ((uint)soundInfo.Key));
                        wad.FixedSoundInfosObsolete.Add(Id, new WadFixedSoundInfo(Id)
                        {
                            SoundInfo = soundInfo.Value
                        });
                    }
                }
            }

            // XML_SOUND_SYSTEM: Used for conversion of Wad2 to new sound system
            wad.AllLoadedSoundInfos = soundInfos;

            // Force wad to be xml wad in case there's no sound infos at all
            if (wad.SoundSystem != SoundSystem.Xml && wad.AllLoadedSoundInfos?.Count == 0)
            {
                wad.SoundSystem = SoundSystem.Xml;
            }

            return(wad);
        }
Пример #16
0
        private static bool LoadSoundInfo(ChunkReader chunkIO, Wad2 wad, Dictionary <long, WadSample> samples,
                                          out WadSoundInfo soundInfo, out long index)
        {
            var   tempSoundInfo = new WadSoundInfo(0);
            long  tempIndex     = 0;
            float volume        = 0;
            float chance        = 0;
            float pitch         = 0;
            float range         = 0;

            chunkIO.ReadChunks((id2, chunkSize2) =>
            {
                // XML_SOUND_SYSTEM
                if (id2 == Wad2Chunks.SoundInfoIndex)
                {
                    tempIndex = chunkIO.ReadChunkLong(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoVolume)
                {
                    volume = chunkIO.ReadChunkFloat(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoRange)
                {
                    range = chunkIO.ReadChunkFloat(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoPitch)
                {
                    pitch = chunkIO.ReadChunkFloat(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoChance)
                {
                    chance = chunkIO.ReadChunkFloat(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoDisablePanning)
                {
                    tempSoundInfo.DisablePanning = chunkIO.ReadChunkBool(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoRandomizePitch)
                {
                    tempSoundInfo.RandomizePitch = chunkIO.ReadChunkBool(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoRandomizeVolume)
                {
                    tempSoundInfo.RandomizeVolume = chunkIO.ReadChunkBool(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoLoopBehaviour)
                {
                    tempSoundInfo.LoopBehaviour = (WadSoundLoopBehaviour)(3 & chunkIO.ReadChunkByte(chunkSize2));
                }
                else if (id2 == Wad2Chunks.SoundInfoName || id2 == Wad2Chunks.SoundInfoNameObsolete)
                {
                    tempSoundInfo.Name = chunkIO.ReadChunkString(chunkSize2);
                }
                else if (id2 == Wad2Chunks.SoundInfoSampleIndex)
                {
                    tempSoundInfo.Samples.Add(samples[chunkIO.ReadChunkInt(chunkSize2)]); // Legacy
                }
                else
                {
                    return(false);
                }
                return(true);
            });

            // Convert from floats to ints
            tempSoundInfo.Volume         = (int)Math.Round(volume * 100.0f);
            tempSoundInfo.RangeInSectors = (int)range;
            tempSoundInfo.Chance         = (int)Math.Round(chance * 100.0f);
            tempSoundInfo.PitchFactor    = (int)Math.Round((pitch - 1.0f) * 100.0f);

            // Try to get the old ID
            tempSoundInfo.Id = TrCatalog.TryGetSoundInfoIdByDescription(wad.GameVersion, tempSoundInfo.Name);

            if (string.IsNullOrWhiteSpace(tempSoundInfo.Name))
            {
                tempSoundInfo.Name = TrCatalog.GetOriginalSoundName(wad.GameVersion, unchecked ((uint)tempIndex));
            }

            index     = tempIndex;
            soundInfo = tempSoundInfo;

            return(true);
        }
Пример #17
0
        internal static WadStatic ConvertTr4StaticMeshToWadStatic(Wad2 wad, Tr4Wad oldWad, int staticIndex, /*List<WadMesh> meshes*/
                                                                  Dictionary <int, WadTexture> textures)
        {
            var oldStaticMesh = oldWad.Statics[staticIndex];
            var newId         = new WadStaticId(oldStaticMesh.ObjectId);

            // A workaround to find out duplicated item IDs produced by StrPix unpatched for v130 wads.
            // In such case, a legacy name is used to guess real item ID, if this fails - broken item is filtered out.
            if (wad.Statics.ContainsKey(newId))
            {
                var message = "Duplicated static ID " + oldStaticMesh.ObjectId + " was identified while loading " + oldWad.BaseName + ". ";
                if (oldWad.LegacyNames.Count - oldWad.Moveables.Count < staticIndex)
                {
                    logger.Warn(message + "Can't restore real ID by name, name table is too short. Ignoring static.");
                    return(null);
                }

                bool isMoveable;
                var  guessedId = TrCatalog.GetItemIndex(TRVersion.Game.TR4, oldWad.LegacyNames[oldWad.Moveables.Count + staticIndex], out isMoveable);

                if (!isMoveable && guessedId.HasValue)
                {
                    newId = new WadStaticId(guessedId.Value);
                    if (wad.Statics.ContainsKey(newId))
                    {
                        logger.Warn(message + "Can't restore real ID by name, name table is inconsistent. Ignoring static.");
                        return(null);
                    }
                    else
                    {
                        logger.Warn(message + "Successfully restored real ID by name.");
                    }
                }
                else
                {
                    logger.Warn(message + "Can't find provided name in catalog. Ignoring static.");
                    return(null);
                }
            }

            var staticMesh = new WadStatic(newId);

            //staticMesh.Name = TrCatalog.GetStaticName(WadTombRaiderVersion.TR4, oldStaticMesh.ObjectId);

            // First setup collisional and visibility bounding boxes
            staticMesh.CollisionBox = new BoundingBox(new Vector3(oldStaticMesh.CollisionX1,
                                                                  -oldStaticMesh.CollisionY1,
                                                                  oldStaticMesh.CollisionZ1),
                                                      new Vector3(oldStaticMesh.CollisionX2,
                                                                  -oldStaticMesh.CollisionY2,
                                                                  oldStaticMesh.CollisionZ2));

            staticMesh.VisibilityBox = new BoundingBox(new Vector3(oldStaticMesh.VisibilityX1,
                                                                   -oldStaticMesh.VisibilityY1,
                                                                   oldStaticMesh.VisibilityZ1),
                                                       new Vector3(oldStaticMesh.VisibilityX2,
                                                                   -oldStaticMesh.VisibilityY2,
                                                                   oldStaticMesh.VisibilityZ2));

            staticMesh.Mesh = ConvertTr4MeshToWadMesh(wad, oldWad, textures,
                                                      oldWad.Meshes[(int)oldWad.RealPointers[oldStaticMesh.PointersIndex]],
                                                      (int)oldStaticMesh.ObjectId);

            wad.Statics.Add(staticMesh.Id, staticMesh);

            return(staticMesh);
        }
Пример #18
0
        internal static WadMoveable ConvertTr4MoveableToWadMoveable(Wad2 wad, Tr4Wad oldWad, int moveableIndex,
                                                                    Dictionary <int, WadTexture> textures)
        {
            wad_moveable oldMoveable = oldWad.Moveables[moveableIndex];
            var          newId       = new WadMoveableId(oldMoveable.ObjectID);

            // A workaround to find out duplicated item IDs produced by StrPix unpatched for v130 wads.
            // In such case, a legacy name is used to guess real item ID, if this fails - broken item is filtered out.
            if (wad.Moveables.ContainsKey(newId))
            {
                var message = "Duplicated moveable ID " + oldMoveable.ObjectID + " was identified while loading " + oldWad.BaseName + ". ";
                if (oldWad.LegacyNames.Count - oldWad.Statics.Count < moveableIndex)
                {
                    logger.Warn(message + "Can't restore real ID by name, name table is too short. Ignoring moveable.");
                    return(null);
                }

                bool isMoveable;
                var  guessedId = TrCatalog.GetItemIndex(TRVersion.Game.TR4, oldWad.LegacyNames[moveableIndex], out isMoveable);

                if (isMoveable && guessedId.HasValue)
                {
                    newId = new WadMoveableId(guessedId.Value);
                    if (wad.Moveables.ContainsKey(newId))
                    {
                        logger.Warn(message + "Can't restore real ID by name, name table is inconsistent. Ignoring moveable.");
                        return(null);
                    }
                    else
                    {
                        logger.Warn(message + "Successfully restored real ID by name.");
                    }
                }
                else
                {
                    logger.Warn(message + "Can't find provided name in catalog. Ignoring moveable.");
                    return(null);
                }
            }

            WadMoveable newMoveable = new WadMoveable(newId);
            var         frameBases  = new Dictionary <WadAnimation, ushort[]>();

            // Load meshes
            var meshes = new List <WadMesh>();

            for (int j = 0; j < oldMoveable.NumPointers; j++)
            {
                meshes.Add(ConvertTr4MeshToWadMesh(wad, oldWad, textures,
                                                   oldWad.Meshes[(int)oldWad.RealPointers[oldMoveable.PointerIndex + j]],
                                                   (int)oldMoveable.ObjectID));
            }

            // Build the skeleton
            var root = new WadBone();

            root.Name        = "bone_0_root";
            root.Parent      = null;
            root.Translation = Vector3.Zero;
            root.Mesh        = meshes[0];

            newMoveable.Bones.Add(root);

            for (int j = 0; j < oldMoveable.NumPointers - 1; j++)
            {
                WadBone bone = new WadBone();
                bone.Name        = "bone_" + (j + 1).ToString();
                bone.Parent      = null;
                bone.Translation = Vector3.Zero;
                bone.Mesh        = meshes[j + 1];
                newMoveable.Bones.Add(bone);
            }

            for (int mi = 0; mi < (oldMoveable.NumPointers - 1); mi++)
            {
                int j = mi + 1;

                var opcode = (WadLinkOpcode)oldWad.Links[(int)(oldMoveable.LinksIndex + mi * 4)];
                int linkX  = oldWad.Links[(int)(oldMoveable.LinksIndex + mi * 4) + 1];
                int linkY  = -oldWad.Links[(int)(oldMoveable.LinksIndex + mi * 4) + 2];
                int linkZ  = oldWad.Links[(int)(oldMoveable.LinksIndex + mi * 4) + 3];

                newMoveable.Bones[j].OpCode      = opcode;
                newMoveable.Bones[j].Translation = new Vector3(linkX, linkY, linkZ);
            }

            // Convert animations
            int numAnimations = 0;
            int nextMoveable  = oldWad.GetNextMoveableWithAnimations(moveableIndex);

            if (nextMoveable == -1)
            {
                numAnimations = oldWad.Animations.Count - oldMoveable.AnimationIndex;
            }
            else
            {
                numAnimations = oldWad.Moveables[nextMoveable].AnimationIndex - oldMoveable.AnimationIndex;
            }

            for (int j = 0; j < numAnimations; j++)
            {
                if (oldMoveable.AnimationIndex == -1)
                {
                    break;
                }

                wad_animation oldAnimation = oldWad.Animations[j + oldMoveable.AnimationIndex];
                WadAnimation  newAnimation = new WadAnimation();
                newAnimation.StateId       = oldAnimation.StateId;
                newAnimation.FrameRate     = oldAnimation.FrameDuration;
                newAnimation.NextAnimation = (ushort)(oldAnimation.NextAnimation - oldMoveable.AnimationIndex);
                newAnimation.NextFrame     = oldAnimation.NextFrame;
                newAnimation.Name          = TrCatalog.GetAnimationName(TRVersion.Game.TR4, oldMoveable.ObjectID, (uint)j);

                // Fix wadmerger/wad format bug with inverted frame start/end on single-frame anims
                ushort newFrameStart = oldAnimation.FrameStart < oldAnimation.FrameEnd ? oldAnimation.FrameStart : oldAnimation.FrameEnd;
                ushort newFrameEnd   = oldAnimation.FrameStart < oldAnimation.FrameEnd ? oldAnimation.FrameEnd : newFrameStart;
                newAnimation.EndFrame = (ushort)(newFrameEnd - newFrameStart);

                for (int k = 0; k < oldAnimation.NumStateChanges; k++)
                {
                    WadStateChange   sc    = new WadStateChange();
                    wad_state_change wadSc = oldWad.Changes[(int)oldAnimation.ChangesIndex + k];
                    sc.StateId = (ushort)wadSc.StateId;

                    for (int n = 0; n < wadSc.NumDispatches; n++)
                    {
                        WadAnimDispatch   ad    = new WadAnimDispatch();
                        wad_anim_dispatch wadAd = oldWad.Dispatches[(int)wadSc.DispatchesIndex + n];

                        ad.InFrame       = (ushort)(wadAd.Low - newFrameStart);
                        ad.OutFrame      = (ushort)(wadAd.High - newFrameStart);
                        ad.NextAnimation = (ushort)((wadAd.NextAnimation - oldMoveable.AnimationIndex) % numAnimations);
                        ad.NextFrame     = (ushort)wadAd.NextFrame;

                        sc.Dispatches.Add(ad);
                    }

                    newAnimation.StateChanges.Add(sc);
                }

                if (oldAnimation.NumCommands < oldWad.Commands.Count)
                {
                    int lastCommand = oldAnimation.CommandOffset;

                    for (int k = 0; k < oldAnimation.NumCommands; k++)
                    {
                        short commandType = oldWad.Commands[lastCommand];

                        WadAnimCommand command = new WadAnimCommand();
                        command.Type = (WadAnimCommandType)commandType;
                        switch (command.Type)
                        {
                        case WadAnimCommandType.SetPosition:
                            command.Parameter1 = (short)oldWad.Commands[lastCommand + 1];
                            command.Parameter2 = (short)oldWad.Commands[lastCommand + 2];
                            command.Parameter3 = (short)oldWad.Commands[lastCommand + 3];
                            lastCommand       += 4;
                            break;

                        case WadAnimCommandType.SetJumpDistance:
                            command.Parameter1 = (short)oldWad.Commands[lastCommand + 1];
                            command.Parameter2 = (short)oldWad.Commands[lastCommand + 2];
                            lastCommand       += 3;
                            break;

                        case WadAnimCommandType.EmptyHands:
                        case WadAnimCommandType.KillEntity:
                            lastCommand += 1;
                            break;

                        case WadAnimCommandType.PlaySound:
                        case WadAnimCommandType.FlipEffect:
                            command.Parameter1 = (short)(oldWad.Commands[lastCommand + 1] - newFrameStart);
                            command.Parameter2 = (short)oldWad.Commands[lastCommand + 2];

                            // For single-frame anims, clamp frame number to first frame (another fix for WM/wad format range inversion bug)
                            if (newAnimation.EndFrame == 0 && command.Parameter1 > 0)
                            {
                                command.Parameter1 = 0;
                            }

                            lastCommand += 3;
                            break;

                        default:     // Ignore invalid anim commands (see for example karnak.wad)
                            logger.Warn("Invalid anim command " + commandType);
                            goto ExitForLoop;
                        }

                        newAnimation.AnimCommands.Add(command);
                    }
ExitForLoop:
                    ;
                }

                int  frames    = (int)oldAnimation.KeyFrameOffset / 2;
                uint numFrames = 0;
                if (oldAnimation.KeyFrameSize != 0)
                {
                    if ((j + oldMoveable.AnimationIndex) == (oldWad.Animations.Count - 1))
                    {
                        numFrames = ((uint)(2 * oldWad.KeyFrames.Count) - oldAnimation.KeyFrameOffset) / (uint)(2 * oldAnimation.KeyFrameSize);
                    }
                    else
                    {
                        numFrames = (oldWad.Animations[oldMoveable.AnimationIndex + j + 1].KeyFrameOffset - oldAnimation.KeyFrameOffset) / (uint)(2 * oldAnimation.KeyFrameSize);
                    }
                }

                for (int f = 0; f < numFrames; f++)
                {
                    WadKeyFrame frame        = new WadKeyFrame();
                    int         startOfFrame = frames;

                    frame.BoundingBox = new BoundingBox(new Vector3(oldWad.KeyFrames[frames],
                                                                    -oldWad.KeyFrames[frames + 2],
                                                                    oldWad.KeyFrames[frames + 4]),
                                                        new Vector3(oldWad.KeyFrames[frames + 1],
                                                                    -oldWad.KeyFrames[frames + 3],
                                                                    oldWad.KeyFrames[frames + 5]));
                    frames += 6;

                    frame.Offset = new Vector3(oldWad.KeyFrames[frames],
                                               (short)(-oldWad.KeyFrames[frames + 1]),
                                               oldWad.KeyFrames[frames + 2]);
                    frames += 3;

                    for (int n = 0; n < oldMoveable.NumPointers; n++)
                    {
                        frame.Angles.Add(WadKeyFrameRotation.FromTrAngle(ref frames, oldWad.KeyFrames, false, true));
                    }
                    if ((frames - startOfFrame) < oldAnimation.KeyFrameSize)
                    {
                        frames += ((int)oldAnimation.KeyFrameSize - (frames - startOfFrame));
                    }

                    newAnimation.KeyFrames.Add(frame);
                }

                // New velocities
                float acceleration = oldAnimation.Accel / 65536.0f;
                newAnimation.StartVelocity = oldAnimation.Speed / 65536.0f;

                float lateralAcceleration = oldAnimation.AccelLateral / 65536.0f;
                newAnimation.StartLateralVelocity = oldAnimation.SpeedLateral / 65536.0f;

                if (newAnimation.KeyFrames.Count != 0 && newAnimation.FrameRate != 0)
                {
                    newAnimation.EndVelocity = newAnimation.StartVelocity + acceleration *
                                               (newAnimation.KeyFrames.Count - 1) * newAnimation.FrameRate;
                    newAnimation.EndLateralVelocity = newAnimation.StartLateralVelocity + lateralAcceleration *
                                                      (newAnimation.KeyFrames.Count - 1) * newAnimation.FrameRate;
                }
                else
                {
                    // Basic foolproofness for potentially broken animations
                    newAnimation.EndVelocity        = newAnimation.StartVelocity;
                    newAnimation.EndLateralVelocity = newAnimation.StartLateralVelocity;
                }

                // Deduce real maximum frame number, based on interpolation and keyframes.
                // We need to refer this value in NextFrame-related fixes (below) because of epic WadMerger bug,
                // which incorrectly calculates NextFrame and "steals" last frame from every custom animation.
                ushort maxFrameCount = (ushort)((newAnimation.FrameRate == 1 || numFrames <= 2) ? numFrames : ((numFrames - 1) * newAnimation.FrameRate) + 1);

                // Also correct animation out-point
                if (newAnimation.EndFrame >= maxFrameCount)
                {
                    newAnimation.EndFrame = (ushort)(maxFrameCount - 1);
                }

                frameBases.Add(newAnimation, new ushort[] { newFrameStart, (ushort)(maxFrameCount - 1) });
                newMoveable.Animations.Add(newAnimation);
            }

            for (int i = 0; i < newMoveable.Animations.Count; i++)
            {
                var animation = newMoveable.Animations[i];

                if (animation.KeyFrames.Count == 0)
                {
                    animation.EndFrame = 0;
                }

                // HACK: this fixes some invalid NextAnimations values
                animation.NextAnimation %= (ushort)newMoveable.Animations.Count;

                newMoveable.Animations[i] = animation;
            }

            for (int i = 0; i < newMoveable.Animations.Count; i++)
            {
                var animation = newMoveable.Animations[i];

                // HACK: this fixes some invalid NextFrame values
                if (frameBases[newMoveable.Animations[animation.NextAnimation]][0] != 0)
                {
                    animation.NextFrame -= frameBases[newMoveable.Animations[animation.NextAnimation]][0];
                    if (animation.NextFrame > frameBases[newMoveable.Animations[animation.NextAnimation]][1])
                    {
                        animation.NextFrame = frameBases[newMoveable.Animations[animation.NextAnimation]][1];
                    }
                }

                foreach (var stateChange in animation.StateChanges)
                {
                    for (int j = 0; j < stateChange.Dispatches.Count; ++j)
                    {
                        WadAnimDispatch animDispatch = stateChange.Dispatches[j];

                        // HACK: Probably WadMerger's bug
                        if (animDispatch.NextAnimation > 32767)
                        {
                            animDispatch.NextAnimation = 0;
                            animDispatch.NextFrame     = 0;
                            continue;
                        }

                        if (frameBases[newMoveable.Animations[animDispatch.NextAnimation]][0] != 0)
                        {
                            // HACK: In some cases dispatches have invalid NextFrame.
                            // From tests it seems that's ok to make NextFrame equal to max frame number.
                            animDispatch.NextFrame -= frameBases[newMoveable.Animations[animDispatch.NextAnimation]][0];
                            if (animDispatch.NextFrame > frameBases[newMoveable.Animations[animDispatch.NextAnimation]][1])
                            {
                                animDispatch.NextFrame = frameBases[newMoveable.Animations[animDispatch.NextAnimation]][1];
                            }
                        }
                        stateChange.Dispatches[j] = animDispatch;
                    }
                }
            }

            wad.Moveables.Add(newMoveable.Id, newMoveable);
            return(newMoveable);
        }
Пример #19
0
 public string ToString(TRVersion.Game gameVersion)
 {
     return("(" + TypeId + ") " + TrCatalog.GetSpriteSequenceName(gameVersion, TypeId));
 }
Пример #20
0
        public static void Main(string[] args)
        {
            var extensions = new List <string>()
            {
                ".xml", ".txt"
            };

            string startFile = null;
            string refLevel  = null;

            if (args.Length > 0) // Open files on start
            {
                bool loadAsRefLevel = false;

                foreach (var arg in args)
                {
                    if (arg.Equals("-r", StringComparison.InvariantCultureIgnoreCase))
                    {
                        loadAsRefLevel = true;
                    }
                    else
                    {
                        if (!File.Exists(arg))
                        {
                            continue; // No file and no valid argument, don't even try to load anything
                        }
                        if (loadAsRefLevel)
                        {
                            if (arg.EndsWith("prj2", StringComparison.InvariantCultureIgnoreCase))
                            {
                                refLevel = arg;
                            }
                        }
                        else
                        {
                            foreach (var ext in extensions)
                            {
                                if (arg.EndsWith(ext, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    startFile = arg;
                                }
                            }
                        }

                        loadAsRefLevel = false; // Reset arg mode if no expected path was found next to it
                    }
                }
            }

            // Load configuration
            var configuration = new Configuration().LoadOrUseDefault <Configuration>();

            // Update DarkUI configuration
            Colors.Brightness = configuration.UI_FormColor_Brightness / 100.0f;

            if (configuration.SoundTool_AllowMultipleInstances ||
                mutex.WaitOne(TimeSpan.Zero, true))
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.AddMessageFilter(new ControlScrollFilter());

                if (!File.Exists(Application.StartupPath + "\\Catalogs\\TrCatalog.xml"))
                {
                    MessageBox.Show("TrCatalog.xml is missing.\nMake sure you have TrCatalog.xml in /Catalogs/ subfolder.");
                    Environment.Exit(1);
                }
                TrCatalog.LoadCatalog(Application.StartupPath + "\\Catalogs\\TRCatalog.xml");

                using (FormMain form = new FormMain(configuration, startFile, refLevel))
                {
                    form.Show();
                    Application.Run(form);
                }
            }
            else if (startFile != null)
            {
                SingleInstanceManagement.Send(Process.GetCurrentProcess(), extensions, startFile);
            }
            else
            {
                SingleInstanceManagement.Bump(Process.GetCurrentProcess());
            }
        }
Пример #21
0
        public static List <IWadObjectId> CopyObject(WadToolClass tool, IWin32Window owner, List <IWadObjectId> objectIdsToMove, bool alwaysChooseId)
        {
            Wad2 sourceWad      = tool.SourceWad;
            Wad2 destinationWad = tool.DestinationWad;

            if (destinationWad == null || sourceWad == null || objectIdsToMove.Count == 0)
            {
                tool.SendMessage("You must have two wads loaded and at least one source object selected.", PopupType.Error);
                return(null);
            }

            var listInProgress = new List <uint>();

            // Figure out the new ids if there are any id collisions
            IWadObjectId[] newIds = objectIdsToMove.ToArray();

            // If destination is TR5Main, try to remap object IDs
            if (destinationWad.GameVersion == TRVersion.Game.TR5Main)
            {
                for (int i = 0; i < objectIdsToMove.Count; ++i)
                {
                    var objectId = objectIdsToMove[i];
                    if (objectId is WadMoveableId)
                    {
                        var moveableId = (WadMoveableId)objectId;

                        // Try to get a compatible slot
                        var newSlot = TrCatalog.GetMoveableTR5MainSlot(sourceWad.GameVersion, moveableId.TypeId);
                        if (newSlot == "")
                        {
                            continue;
                        }

                        // Get the new ID
                        bool isMoveable;
                        var  newId = TrCatalog.GetItemIndex(destinationWad.GameVersion, newSlot, out isMoveable);
                        if (!newId.HasValue)
                        {
                            continue;
                        }

                        // Save the new ID
                        newIds[i] = new WadMoveableId(newId.Value);
                    }
                }
            }

            for (int i = 0; i < objectIdsToMove.Count; ++i)
            {
                if (!sourceWad.Contains(objectIdsToMove[i]))
                {
                    continue;
                }
                if (!alwaysChooseId)
                {
                    if (!destinationWad.Contains(newIds[i]))
                    {
                        if (!newIds.Take(i).Contains(newIds[i])) // There also must not be collisions with the other custom assigned ids.
                        {
                            continue;
                        }
                    }
                }

                bool askConfirm = !alwaysChooseId;

                // Ask for the new slot
                do
                {
                    DialogResult dialogResult;
                    if (askConfirm)
                    {
                        dialogResult = DarkMessageBox.Show(owner, "The id " + newIds[i].ToString(destinationWad.GameVersion) + " is already occupied in the destination wad.\n" +
                                                           "Do you want to replace it (Yes) or to select another Id (No)?",
                                                           "Occupied slot", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                    }
                    else
                    {
                        dialogResult = DialogResult.No;
                    }

                    // From this time, always ask for confirm
                    askConfirm = true;

                    if (dialogResult == DialogResult.Cancel)
                    {
                        return(null);
                    }
                    else if (dialogResult == DialogResult.No)
                    {
                        using (var form = new FormSelectSlot(destinationWad, newIds[i], listInProgress))
                        {
                            if (form.ShowDialog(owner) != DialogResult.OK)
                            {
                                return(null);
                            }
                            if (destinationWad.Contains(form.NewId) || newIds.Take(i).Contains(form.NewId))
                            {
                                destinationWad.Remove(form.NewId);
                                tool.WadChanged(WadArea.Destination);
                            }
                            newIds[i] = form.NewId;

                            if (form.NewId is WadStaticId)
                            {
                                listInProgress.Add(((WadStaticId)form.NewId).TypeId);
                            }
                            if (form.NewId is WadMoveableId)
                            {
                                listInProgress.Add(((WadMoveableId)form.NewId).TypeId);
                            }
                            if (form.NewId is WadSpriteSequenceId)
                            {
                                listInProgress.Add(((WadSpriteSequenceId)form.NewId).TypeId);
                            }

                            break;
                        }
                    }
                    else
                    {
                        destinationWad.Remove(objectIdsToMove[i]);
                        tool.WadChanged(WadArea.Destination);
                        break;
                    }
                } while (destinationWad.Contains(newIds[i]) || newIds.Take(i).Contains(newIds[i])); // There also must not be collisions with the other custom assigned ids.
            }

            // HACK: Until this is fixed... https://github.com/MontyTRC89/Tomb-Editor/issues/413
            // We just block copying of same object to another slot and warn user it's in another slot.
            var failedIdList = new List <IWadObjectId>();

            // Move objects
            for (int i = 0; i < objectIdsToMove.Count; ++i)
            {
                IWadObject obj = sourceWad.TryGet(objectIdsToMove[i]);
                if (obj == null)
                {
                    continue;
                }

                if (destinationWad.Contains(obj)) // Aforementioned HACK continues here - just add object which failed to copy to failed list
                {
                    failedIdList.Add(newIds[i]);
                }
                else
                {
                    destinationWad.Add(newIds[i], obj);

                    if (destinationWad.GameVersion == TRVersion.Game.TR5Main && obj is WadMoveable)
                    {
                        var moveable = obj as WadMoveable;
                        foreach (var animation in moveable.Animations)
                        {
                            foreach (var command in animation.AnimCommands)
                            {
                                if (command.Type == WadAnimCommandType.PlaySound)
                                {
                                    int id = command.Parameter2 & 0x3FFF;
                                    id += TrCatalog.GetTR5MainSoundMapStart(sourceWad.GameVersion);
                                    command.Parameter2 = (short)((command.Parameter2 & 0xC000) | (id & 0x3FFF));
                                }
                            }
                        }
                    }
                }
            }

            // Aforementioned HACK continues here - count amount of actually copied objects
            int actualAmountOfCopiedObjects = objectIdsToMove.Count - failedIdList.Count;

            if (actualAmountOfCopiedObjects > 0)
            {
                // Update the situation
                tool.WadChanged(WadArea.Destination);

                string infoString = (objectIdsToMove.Count == 1 ? "Object" : "Objects") + " successfully copied.";

                // Aforementioned HACK continues here - additionally inform user that some objects are in different slots.
                if (failedIdList.Count > 0)
                {
                    infoString += "\n" + failedIdList.Count + " object" +
                                  (failedIdList.Count > 1 ? "s" : "") + " weren't copied because " +
                                  (failedIdList.Count > 1 ? "they are" : "it's") + " already in different slot" +
                                  (failedIdList.Count > 1 ? "s." : ".");
                }

                // Indicate that object is copied
                tool.SendMessage(infoString, PopupType.Info);
            }

            return(newIds.Where(item => !failedIdList.Any(failed => failed == item)).ToList());
        }
Пример #22
0
 public string ShortName(TRVersion.Game gameVersion) => TrCatalog.GetStaticName(gameVersion, TypeId);
Пример #23
0
 public string ShortName(TRVersion.Game gameVersion) => TrCatalog.GetMoveableName(gameVersion, TypeId);
Пример #24
0
        private void PrepareItems()
        {
            bool isNewTR = _level.Settings.GameVersion > TRVersion.Game.TR3;

            ReportProgress(42, "Building items table");

            _moveablesTable = new Dictionary <MoveableInstance, int>(new ReferenceEqualityComparer <MoveableInstance>());
            _aiObjectsTable = new Dictionary <MoveableInstance, int>(new ReferenceEqualityComparer <MoveableInstance>());

            _luaIdToItems = new Dictionary <int, int>();

            foreach (Room room in _level.Rooms.Where(room => room != null))
            {
                foreach (var instance in room.Objects.OfType <MoveableInstance>())
                {
                    WadMoveable wadMoveable = _level.Settings.WadTryGetMoveable(instance.WadObjectId);
                    if (wadMoveable == null)
                    {
                        _progressReporter.ReportWarn("Moveable '" + instance + "' was not included in the level because it is missing the *.wad file.");
                        continue;
                    }

                    Vector3 position = instance.Room.WorldPos + instance.Position;
                    double  angle    = Math.Round(instance.RotationY * (65536.0 / 360.0));
                    ushort  angleInt = unchecked ((ushort)Math.Max(0, Math.Min(ushort.MaxValue, angle)));

                    // Split AI objects and normal objects (only for TR4+)
                    if (isNewTR && TrCatalog.IsMoveableAI(_level.Settings.GameVersion, wadMoveable.Id.TypeId))
                    {
                        _aiItems.Add(new tr_ai_item
                        {
                            X        = (int)Math.Round(position.X),
                            Y        = (int)-Math.Round(position.Y),
                            Z        = (int)Math.Round(position.Z),
                            ObjectID = checked ((ushort)instance.WadObjectId.TypeId),
                            Room     = (ushort)_roomsRemappingDictionary[instance.Room],
                            Angle    = angleInt,
                            OCB      = instance.Ocb,
                            Flags    = (ushort)(instance.CodeBits << 1)
                        });
                        _aiObjectsTable.Add(instance, _aiObjectsTable.Count);
                    }
                    else
                    {
                        int    flags = (instance.CodeBits << 9) | (instance.ClearBody ? 0x80 : 0) | (instance.Invisible ? 0x100 : 0);
                        ushort color = instance.Color.Equals(Vector3.One) ? (ushort)0xFFFF : PackColorTo16Bit(instance.Color);

                        // Substitute ID is needed to convert visible menu items to pick-up sprites in TR1-2
                        var realID = TrCatalog.GetSubstituteID(_level.Settings.GameVersion, instance.WadObjectId.TypeId);

                        _items.Add(new tr_item
                        {
                            X          = (int)Math.Round(position.X),
                            Y          = (int)-Math.Round(position.Y),
                            Z          = (int)Math.Round(position.Z),
                            ObjectID   = checked ((ushort)realID),
                            Room       = (short)_roomsRemappingDictionary[instance.Room],
                            Angle      = angleInt,
                            Intensity1 = color,
                            Ocb        = isNewTR ? instance.Ocb : unchecked ((short)color),
                            Flags      = unchecked ((ushort)flags)
                        });
                        _moveablesTable.Add(instance, _moveablesTable.Count);

                        if (_level.Settings.GameVersion == TRVersion.Game.TR5Main)
                        {
                            if (!_luaIdToItems.ContainsKey(instance.LuaId))
                            {
                                _luaIdToItems.Add(instance.LuaId, _items.Count - 1);
                            }
                        }
                    }
                }
            }

            // Sort AI objects and put all LARA_START_POS objects (last AI object by ID) in front
            if (_level.Settings.GameVersion > TRVersion.Game.TR3)
            {
                _aiItems = _aiItems.OrderByDescending(item => item.ObjectID).ThenBy(item => item.OCB).ToList();
                ReportProgress(45, "    Number of AI objects: " + _aiItems.Count);
            }

            ReportProgress(45, "    Number of items: " + _items.Count);

            int maxSafeItemCount, maxItemCount;

            switch (_level.Settings.GameVersion)
            {
            case TRVersion.Game.TRNG:
                maxSafeItemCount = 255;
                maxItemCount     = 1023;
                break;

            case TRVersion.Game.TR5Main:
                maxSafeItemCount = 1023;
                maxItemCount     = 32767;
                break;

            default:
                maxSafeItemCount = 255;
                maxItemCount     = 255;
                break;
            }

            if (_items.Count > maxItemCount)
            {
                var warnString = "Level has more than " + maxItemCount + " moveables. This will lead to crash" +
                                 (_level.Settings.GameVersion == TRVersion.Game.TR4 ? ", unless you're using TREP." : ".");
                _progressReporter.ReportWarn(warnString);
            }

            if (_items.Count > maxSafeItemCount)
            {
                _progressReporter.ReportWarn("Moveable count is beyond " + maxSafeItemCount + ", which may lead to savegame handling issues.");
            }
        }
Пример #25
0
 public string ToString(TRVersion.Game gameVersion)
 {
     return("(" + TypeId + ") " + TrCatalog.GetStaticName(gameVersion, TypeId));
 }
Пример #26
0
        public static void Main(string[] args)
        {
            // Load configuration
            var initialEvents = new List <LogEventInfo>();
            var configuration = new Configuration().LoadOrUseDefault <Configuration>(initialEvents);

            // Update DarkUI configuration
            Colors.Brightness = configuration.UI_FormColor_Brightness / 100.0f;

            // Setup logging
            using (var log = new Logging(configuration.Log_MinLevel, configuration.Log_WriteToFile, configuration.Log_ArchiveN, initialEvents))
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
                Application.ThreadException += (sender, e) =>
                {
                    log.HandleException(e.Exception);
                    using (var dialog = new ThreadExceptionDialog(e.Exception))
                        if (dialog.ShowDialog() == DialogResult.Abort)
                        {
                            Environment.Exit(1);
                        }
                };

                // Show startup help
                if (configuration.StartUpHelp_Show)
                {
                    var help = new FormStartupHelp();
                    Application.Run(help);
                    switch (help.DialogResult)
                    {
                    case DialogResult.Cancel:
                        return;

                    case DialogResult.OK:
                        configuration.StartUpHelp_Show = false;
                        break;
                    }
                }
                configuration.SaveTry();

                // Run
                if (!File.Exists(Application.StartupPath + "\\Catalogs\\TrCatalog.xml"))
                {
                    MessageBox.Show("TrCatalog.xml is missing.\nMake sure you have TrCatalog.xml in /Catalogs/ subfolder.");
                    Environment.Exit(1);
                }
                TrCatalog.LoadCatalog(Application.StartupPath + "\\Catalogs\\TRCatalog.xml");

                Application.AddMessageFilter(new ControlScrollFilter());
                SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());

                using (WadToolClass tool = new WadToolClass(configuration))
                {
                    string startWad = null;
                    string refLevel = null;

                    if (args.Length > 0)
                    {
                        bool loadAsRefLevel = false;

                        foreach (var arg in args)
                        {
                            if (arg.Equals("-r", StringComparison.InvariantCultureIgnoreCase))
                            {
                                loadAsRefLevel = true;
                            }
                            else
                            {
                                if (!File.Exists(arg))
                                {
                                    continue; // No file and no valid argument, don't even try to load anything
                                }
                                if (loadAsRefLevel)
                                {
                                    if (arg.EndsWith("prj2", StringComparison.InvariantCultureIgnoreCase))
                                    {
                                        refLevel = arg;
                                    }
                                }
                                else
                                {
                                    startWad = arg;
                                }

                                loadAsRefLevel = false; // Reset arg mode if no expected path was found next to it
                            }
                        }
                    }

                    using (FormMain form = new FormMain(tool))
                    {
                        form.Show();

                        if (!string.IsNullOrEmpty(refLevel))
                        {
                            WadActions.LoadReferenceLevel(tool, form, refLevel);
                        }
                        if (!string.IsNullOrEmpty(startWad))
                        {
                            WadActions.LoadWad(tool, form, true, startWad);
                        }

                        Application.Run(form);
                    }
                }
            }
        }
Пример #27
0
 private void debugAction5ToolStripMenuItem_Click(object sender, EventArgs e)
 {
     TrCatalog.LoadCatalog("Editor\\TRCatalog.xml");
 }
Пример #28
0
        private static WadSounds ReadFromSfx(string filename)
        {
            var samples = new List <string>();

            // Read version
            int version = 129;

            using (var readerVersion = new BinaryReaderEx(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)))
                version = readerVersion.ReadInt32();

            // Read samples
            var samPath = Path.GetDirectoryName(filename) + "\\" + Path.GetFileNameWithoutExtension(filename) + ".sam";

            using (var readerSounds = new StreamReader(new FileStream(samPath, FileMode.Open, FileAccess.Read, FileShare.Read)))
                while (!readerSounds.EndOfStream)
                {
                    samples.Add(readerSounds.ReadLine());
                }

            // Read sounds
            int soundMapSize = 0;

            short[] soundMap;
            var     wadSoundInfos = new List <wad_sound_info>();
            var     soundInfos    = new List <WadSoundInfo>();

            var sfxPath = Path.GetDirectoryName(filename) + "\\" + Path.GetFileNameWithoutExtension(filename) + ".sfx";

            using (var readerSfx = new BinaryReaderEx(new FileStream(sfxPath, FileMode.Open, FileAccess.Read, FileShare.Read)))
            {
                // Try to guess the WAD version
                readerSfx.BaseStream.Seek(740, SeekOrigin.Begin);
                short first  = readerSfx.ReadInt16();
                short second = readerSfx.ReadInt16();
                version = ((first == -1 || second == (first + 1)) ? 130 : 129);
                readerSfx.BaseStream.Seek(0, SeekOrigin.Begin);

                soundMapSize = (version == 130 ? 2048 : 370);
                soundMap     = new short[soundMapSize];
                for (var i = 0; i < soundMapSize; i++)
                {
                    soundMap[i] = readerSfx.ReadInt16();
                }

                var numSounds = readerSfx.ReadUInt32();

                for (var i = 0; i < numSounds; i++)
                {
                    var info = new wad_sound_info();
                    info.Sample          = readerSfx.ReadUInt16();
                    info.Volume          = readerSfx.ReadByte();
                    info.Range           = readerSfx.ReadByte();
                    info.Chance          = readerSfx.ReadByte();
                    info.Pitch           = readerSfx.ReadByte();
                    info.Characteristics = readerSfx.ReadUInt16();
                    wadSoundInfos.Add(info);
                }
            }

            // Convert old data to new format
            for (int i = 0; i < soundMapSize; i++)
            {
                // Check if sound is defined at all
                if (soundMap[i] == -1 || soundMap[i] >= wadSoundInfos.Count)
                {
                    continue;
                }

                // Fill the new sound info
                var oldInfo = wadSoundInfos[soundMap[i]];
                var newInfo = new WadSoundInfo(i);
                newInfo.Name           = TrCatalog.GetOriginalSoundName(TRVersion.Game.TR4, (uint)i);
                newInfo.Volume         = (int)Math.Round(oldInfo.Volume * 100.0f / 255.0f);
                newInfo.RangeInSectors = oldInfo.Range;
                newInfo.Chance         = (int)Math.Round(oldInfo.Chance * 100.0f / 255.0f);
                if (newInfo.Chance == 0)
                {
                    newInfo.Chance = 100;                      // Convert legacy chance value
                }
                newInfo.PitchFactor     = (int)Math.Round((oldInfo.Pitch > 127 ? oldInfo.Pitch - 256 : oldInfo.Pitch) * 100.0f / 128.0f);
                newInfo.RandomizePitch  = ((oldInfo.Characteristics & 0x2000) != 0);
                newInfo.RandomizeVolume = ((oldInfo.Characteristics & 0x4000) != 0);
                newInfo.DisablePanning  = ((oldInfo.Characteristics & 0x1000) != 0);
                newInfo.LoopBehaviour   = (WadSoundLoopBehaviour)(oldInfo.Characteristics & 0x03);
                newInfo.SoundCatalog    = sfxPath;

                // Read all samples linked to this sound info (for example footstep has 4 samples)
                int numSamplesInGroup = (oldInfo.Characteristics & 0x00fc) >> 2;
                for (int j = oldInfo.Sample; j < oldInfo.Sample + numSamplesInGroup; j++)
                {
                    newInfo.Samples.Add(new WadSample(samples[j]));
                }
                soundInfos.Add(newInfo);
            }

            var sounds = new WadSounds(soundInfos);

            return(sounds);
        }
Пример #29
0
        private void DgvSoundInfos_CellValidated(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex != 1)
            {
                return;
            }

            DataGridViewRow row = dgvSoundInfos.Rows[e.RowIndex];

            int id;

            if (!int.TryParse(row.Cells[1].Value.ToString(), out id))
            {
                row.DefaultCellStyle.BackColor = dgvSoundInfos.BackColor;
                row.Cells[1].Value             = "";
                row.Cells[2].Value             = "";
            }
            else
            {
                // Search if this Id was already assigned
                foreach (DataGridViewRow row2 in dgvSoundInfos.Rows)
                {
                    // Ignore the same row
                    if (row2.Index == row.Index)
                    {
                        continue;
                    }

                    // Ignore empty values
                    int id2;
                    if (!int.TryParse(row2.Cells[1].Value.ToString(), out id2))
                    {
                        continue;
                    }

                    // If is the same then warn the user
                    if (id == id2)
                    {
                        row.DefaultCellStyle.BackColor = dgvSoundInfos.BackColor;
                        row.Cells[1].Value             = "";
                        row.Cells[2].Value             = "";

                        DarkMessageBox.Show(this, "The selected Id " + id + " was already assigned to sound '" +
                                            row2.Cells[0].Value + "'", "Error",
                                            MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                }

                // If additional catalog is loaded, this has the priority
                string name = "";
                if (Sounds != null)
                {
                    var info = Sounds.TryGetSoundInfo(id);
                    if (info != null)
                    {
                        name = info.Name;
                    }
                    else
                    {
                        name = TrCatalog.GetOriginalSoundName(_version, (uint)id);
                    }
                }
                else
                {
                    name = TrCatalog.GetOriginalSoundName(_version, (uint)id);
                }

                if (name == null || name == "")
                {
                    row.DefaultCellStyle.BackColor = dgvSoundInfos.BackColor;
                    row.Cells[1].Value             = "";
                    row.Cells[2].Value             = "";
                }
                else
                {
                    row.DefaultCellStyle.BackColor = Color.DarkGreen;
                    row.Cells[2].Value             = name;
                }
            }

            dgvSoundInfos.InvalidateRow(e.RowIndex);
            UpdateStatus();
        }
Пример #30
0
        public static bool ConvertPrj2ToNewSoundFormat(Level level, string src, string dest, string soundsCatalog, bool save)
        {
            /* PROCEDURE:
             * 1. Collect all sound sources of level: if embedded sound info is null, then it's a sound source
             *    that is referencing a Wad file and we should just remap it, otherwise it's a custom sound source
             *    created inside Tomb Editor and we must export it to Xml and we must export samples too
             * 2. Try to guess the ID for Wad sounds and generate instead a new ID above 602 for custom sounds
             *    (ID = 602 is the start of TR1 area of extended soundmap of TRNG and it should be rarely used)
             * 3. Show the dialog to the user. Here he can load an additional catalog if he changed sounds via TRLE tools
             * 4. Assign new IDs and new names to sound infos
             * 5. Remap sound sources
             * 6. Export samples if needed
             * 7. Save Prj2 + Xml if custom sounds are present
             */

            try
            {
                // Check for sound system
                if (level.Settings.SoundSystem == SoundSystem.Xml)
                {
                    return(true);
                }

                // Infer the wad version from level version
                TRVersion.Game version = level.Settings.GameVersion.Native();

                // Collect all sounds to remap
                var conversionList = new List <SoundInfoConversionRow>();

                // We start from sound id = 602 which is the TR1 sound area of TRNG extended soundmap.
                // This area is reserved for TR1 enemies and so it *** should *** be used rarely
                int lastSoundId = 602;

                foreach (var room in level.Rooms)
                {
                    if (room != null)
                    {
                        foreach (var obj in room.Objects)
                        {
                            if (obj is SoundSourceInstance)
                            {
                                SoundSourceInstance soundSource = obj as SoundSourceInstance;

                                if (soundSource.WadReferencedSoundName != null && soundSource.WadReferencedSoundName != "")
                                {
                                    if (!conversionList.Select(f => f.OldName).Contains(soundSource.WadReferencedSoundName))
                                    {
                                        // First try to get sound name from TrCatalog
                                        int newId = TrCatalog.TryGetSoundInfoIdByDescription(version, soundSource.WadReferencedSoundName);

                                        var row = new SoundInfoConversionRow(null, soundSource.WadReferencedSoundName);
                                        if (newId == -1)
                                        {
                                            // If sound was not found in catalog, then assign a generic Id and ask to the user
                                            row.NewName = Regex.Replace(soundSource.WadReferencedSoundName, "[^A-Za-z0-9 _]", "").ToUpper();
                                            row.NewId   = lastSoundId++;
                                        }
                                        else
                                        {
                                            // Otherwise, we are lucky, and we can just assign the correct Id
                                            row.NewName = TrCatalog.GetOriginalSoundName(version, (uint)newId);
                                            row.NewId   = newId;
                                        }

                                        conversionList.Add(row);
                                    }
                                }
                                else if (soundSource.EmbeddedSoundInfo != null)
                                {
                                    bool found = false;
                                    foreach (var r in conversionList)
                                    {
                                        if (r.SoundInfo != null && r.SoundInfo == soundSource.EmbeddedSoundInfo)
                                        {
                                            found = true;
                                            break;
                                        }
                                    }

                                    if (found)
                                    {
                                        continue;
                                    }

                                    // Let's first try a search in TrCatalog, maybe we are lucky
                                    // First try to get sound name from TrCatalog

                                    /*int newId = TrCatalog.TryGetSoundInfoIdByDescription(version, soundSource.EmbeddedSoundInfo.Name);
                                     *
                                     * var row = new SoundInfoConversionRow(soundSource.EmbeddedSoundInfo, soundSource.EmbeddedSoundInfo.Name);
                                     * if (newId == -1)
                                     * {
                                     *  // If sound was not found in catalog, then assign a generic Id and ask to the user
                                     *  row.NewName = Regex.Replace(soundSource.EmbeddedSoundInfo.Name, "[^A-Za-z0-9 _]", "").ToUpper();
                                     *  row.NewId = lastSoundId++;
                                     * }
                                     * else
                                     * {
                                     *  // Otherwise, we are lucky, and we can just assign the correct Id
                                     *  row.NewName = TrCatalog.GetOriginalSoundName(version, (uint)newId);
                                     *  row.NewId = newId;
                                     * }*/

                                    // TODO: Lwmte proposed to also there check in TrCatalog, but we should assume that
                                    // embedded sound sources are custom sound sources, created by thhe user with custom samples
                                    // and we should think carrefully about this
                                    var row = new SoundInfoConversionRow(soundSource.EmbeddedSoundInfo, soundSource.EmbeddedSoundInfo.Name);
                                    row.NewName = Regex.Replace(soundSource.EmbeddedSoundInfo.Name, "[^A-Za-z0-9 _]", "").ToUpper();
                                    row.NewId   = lastSoundId++;

                                    // These flags are handle by Tomb Editor and set only for embedded sound sources
                                    row.SaveToXml     = true;
                                    row.ExportSamples = true;

                                    conversionList.Add(row);
                                }
                            }
                        }
                    }
                }

                WadSounds sounds = null;

                // Now we'll show a dialog with all conversion rows and the user will need to make some choices
                if (conversionList.Count != 0)
                {
                    using (var form = new Prj2SoundsConversionDialog(version, conversionList))
                    {
                        if (form.ShowDialog() == DialogResult.Cancel)
                        {
                            return(false);
                        }

                        // If the user has loaded an additional catalog, let's get a pointer to it
                        if (form.Sounds != null)
                        {
                            sounds = form.Sounds;
                        }
                    }
                }

                // Assign new Id and name
                foreach (var row in conversionList)
                {
                    if (row.SoundInfo != null)
                    {
                        row.SoundInfo.Id   = row.NewId;
                        row.SoundInfo.Name = row.NewName;
                    }
                }

                // We'll export only embedded sound sources
                var newSounds = new WadSounds();

                // Remap sound sources
                foreach (var room in level.Rooms)
                {
                    if (room != null)
                    {
                        foreach (var obj in room.Objects)
                        {
                            if (obj is SoundSourceInstance)
                            {
                                SoundSourceInstance soundSource = obj as SoundSourceInstance;
                                if (soundSource.WadReferencedSoundName != null && soundSource.WadReferencedSoundName != "")
                                {
                                    soundSource.SoundId = -1;
                                    foreach (var row in conversionList)
                                    {
                                        if (row.OldName == soundSource.WadReferencedSoundName && row.NewId != -1)
                                        {
                                            soundSource.SoundId = row.NewId;
                                            break;
                                        }
                                    }

                                    soundSource.WadReferencedSoundName = "";
                                    soundSource.EmbeddedSoundInfo      = null;
                                }
                                else if (soundSource.EmbeddedSoundInfo != null)
                                {
                                    // We export embedded sound infos
                                    if (!newSounds.SoundInfos.Contains(soundSource.EmbeddedSoundInfo))
                                    {
                                        newSounds.SoundInfos.Add(soundSource.EmbeddedSoundInfo);
                                    }

                                    soundSource.SoundId = -1;
                                    foreach (var row in conversionList)
                                    {
                                        if (row.SoundInfo == soundSource.EmbeddedSoundInfo && row.NewId != -1)
                                        {
                                            soundSource.SoundId = row.NewId;

                                            // Try to bind samples from additional catalog, if loaded

                                            /*if (sounds != null)
                                             * {
                                             *  WadSoundInfo catalogInfo = sounds.TryGetSoundInfo(row.NewId);
                                             *  if (catalogInfo != null && catalogInfo.Samples.Count > 0)
                                             *  {
                                             *      soundSource.EmbeddedSoundInfo.Samples.Clear();
                                             *      soundSource.EmbeddedSoundInfo.Samples.AddRange(catalogInfo.Samples);
                                             *      // TODO: in theory if valid samples are found in catalog, we shouldn't need to
                                             *      // export them
                                             *      row.ExportSamples = false;
                                             *  }
                                             * }*/

                                            break;
                                        }
                                    }

                                    soundSource.WadReferencedSoundName = "";
                                    soundSource.EmbeddedSoundInfo      = null;
                                }
                            }
                        }
                    }
                }

                // Export samples
                foreach (var row in conversionList)
                {
                    if (row.SoundInfo != null && row.ExportSamples)
                    {
                        var samples = new List <string>();
                        foreach (var sample in row.SoundInfo.Samples)
                        {
                            if (sample.IsLoaded)
                            {
                                string sampleName = Path.GetFileNameWithoutExtension(dest) + "_" + row.NewName.ToLower() + "_" + row.SoundInfo.Samples.IndexOf(sample) + ".wav";
                                samples.Add(sampleName);
                                File.WriteAllBytes(Path.GetDirectoryName(dest) + "\\" + sampleName, sample.Data);
                            }
                        }

                        row.SoundInfo.Samples.Clear();
                        foreach (var sample in samples)
                        {
                            row.SoundInfo.Samples.Add(new WadSample(sample));
                        }
                    }
                }

                // Sort sound infos
                newSounds.SoundInfos.Sort((a, b) => a.Id.CompareTo(b.Id));

                // Make a backup copy
                if (save && src == dest)
                {
                    int    index          = 0;
                    string backupFilename = "";
                    while (true)
                    {
                        backupFilename = dest + "." + index + ".bak";
                        if (!File.Exists(backupFilename))
                        {
                            break;
                        }
                        index++;
                    }

                    File.Copy(src, backupFilename, true);
                }

                // Save Xml to file
                if (newSounds.SoundInfos.Count != 0)
                {
                    string xmlFileName = Path.GetDirectoryName(dest) + "\\" + Path.GetFileNameWithoutExtension(dest) + ".xml";
                    WadSounds.SaveToXml(xmlFileName, newSounds);

                    // Assign Xml to level settings
                    level.Settings.SoundsCatalogs.Add(new ReferencedSoundsCatalog(level.Settings,
                                                                                  level.Settings.MakeRelative(xmlFileName, VariableType.LevelDirectory)));
                }

                level.Settings.SoundSystem = SoundSystem.Xml;

                // Try to get Xml and SFX files
                foreach (var wadRef in level.Settings.Wads)
                {
                    if (wadRef != null && wadRef.LoadException == null)
                    {
                        string wadPath   = level.Settings.MakeAbsolute(wadRef.Path);
                        string extension = Path.GetExtension(wadPath).ToLower();

                        if (extension == ".wad")
                        {
                            string sfxPath = Path.GetDirectoryName(wadPath) + "\\" + Path.GetFileNameWithoutExtension(wadPath) + ".sfx";
                            if (File.Exists(sfxPath))
                            {
                                sounds = WadSounds.ReadFromFile(sfxPath);
                                if (sounds != null)
                                {
                                    level.Settings.SoundsCatalogs.Add(new ReferencedSoundsCatalog(level.Settings,
                                                                                                  level.Settings.MakeRelative(sfxPath, VariableType.LevelDirectory)));
                                }
                            }
                        }
                        else if (extension == ".wad2")
                        {
                            string xmlPath = Path.GetDirectoryName(wadPath) + "\\" + Path.GetFileNameWithoutExtension(wadPath) + ".xml";
                            if (File.Exists(xmlPath))
                            {
                                sounds = WadSounds.ReadFromFile(xmlPath);
                                if (sounds != null)
                                {
                                    level.Settings.SoundsCatalogs.Add(new ReferencedSoundsCatalog(level.Settings,
                                                                                                  level.Settings.MakeRelative(xmlPath, VariableType.LevelDirectory)));
                                }
                            }
                        }
                    }
                }

                // Assign sounds if possible
                foreach (var soundRef in level.Settings.SoundsCatalogs)
                {
                    if (soundRef.LoadException == null)
                    {
                        foreach (var sound in soundRef.Sounds.SoundInfos)
                        {
                            if (!level.Settings.SelectedSounds.Contains(sound.Id))
                            {
                                level.Settings.SelectedSounds.Add(sound.Id);
                            }
                        }
                    }
                }

                // Save Prj2 with Xml sounds
                if (save)
                {
                    using (var stream = File.OpenWrite(dest))
                        Prj2Writer.SaveToPrj2(stream, level);
                }

                return(true);
            }
            catch (Exception ex)
            {
                return(false);
            }
        }