예제 #1
0
        // Delete a mod by its index. The event is invoked before the mod is removed from the list.
        // Deletes from filesystem as well as from internal data.
        // Updates indices of later mods.
        public void DeleteMod(int idx)
        {
            var mod = this[idx];

            if (Directory.Exists(mod.ModPath.FullName))
            {
                try
                {
                    Directory.Delete(mod.ModPath.FullName, true);
                    PluginLog.Debug("Deleted directory {Directory:l} for {Name:l}.", mod.ModPath.FullName, mod.Name);
                }
                catch (Exception e)
                {
                    PluginLog.Error($"Could not delete the mod {mod.ModPath.Name}:\n{e}");
                }
            }

            ModPathChanged.Invoke(ModPathChangeType.Deleted, mod, mod.ModPath, null);
            _mods.RemoveAt(idx);
            foreach (var remainingMod in _mods.Skip(idx))
            {
                --remainingMod.Index;
            }

            PluginLog.Debug("Deleted mod {Name:l}.", mod.Name);
        }
예제 #2
0
        private unsafe void ReloadCharacterResources()
        {
            var oldResources = new IntPtr[NumResources];
            var resources    = new IntPtr(&CharacterUtility->Resources);
            var pResources   = ( void ** )resources.ToPointer();

            Marshal.Copy(resources, oldResources, 0, NumResources);

            LoadDataFiles(CharacterUtility);

            for (var i = 0; i < NumResources; i++)
            {
                var handle = ( ResourceHandle * )oldResources[i];
                if (oldResources[i].ToPointer() == pResources[i])
                {
                    PluginLog.Debug($"Unchanged resource: {ResourceToPath( ( byte* )oldResources[ i ].ToPointer() )}");
                    (( ResourceHandle * )oldResources[i])->DecRef();
                    continue;
                }

                PluginLog.Debug("Freeing "
                                + $"{ResourceToPath( ( byte* )oldResources[ i ].ToPointer() )}, replaced with "
                                + $"{ResourceToPath( ( byte* )pResources[ i ] )}");

                UnloadCharacterResource(oldResources[i]);

                // Temporary fix against crashes?
                if (handle->RefCount <= 0)
                {
                    handle->RefCount = 1;
                    handle->IncRef();
                    handle->RefCount = 1;
                }
            }
        }
 private void StoreLog(string msg)
 {
     if (_config.DebugLogEnabled)
     {
         PluginLog.Debug($"[Store] {msg}");
     }
 }
        public unsafe void ReloadCharacterResources()
        {
            var oldResources = new IntPtr[NumResources];
            var resources    = new IntPtr(&CharacterResourceManagerPtr->Resources);
            var pResources   = ( void ** )resources.ToPointer();

            Marshal.Copy(resources, oldResources, 0, NumResources);

            LoadCharacterResources(CharacterResourceManagerPtr);

            for (var i = 0; i < NumResources; i++)
            {
                if (oldResources[i].ToPointer() == pResources[i])
                {
                    PluginLog.Debug($"Unchanged resource: {ResourceToPath( ( byte* )oldResources[ i ].ToPointer() )}");
                    continue;
                }

                PluginLog.Debug("Freeing " +
                                $"{ResourceToPath( ( byte* )oldResources[ i ].ToPointer() )}, replaced with " +
                                $"{ResourceToPath( ( byte* )pResources[ i ] )}");

                UnloadCharacterResource(oldResources[i]);
            }
        }
예제 #5
0
 public static void Log(object message)
 {
     foreach (var m in SplitMessage(message))
     {
         PluginLog.Debug($"{m}");
     }
 }
예제 #6
0
    /// <inheritdoc/>
    public async override Task Execute(CancellationToken token)
    {
        PluginLog.Debug($"Executing: {this.Text}");

        Service.ChatManager.SendMessage(this.Text);

        await this.PerformWait(token);
    }
예제 #7
0
 public override void Render(AdvRadialMenu radialMenu)
 {
     if (radialMenu.RadialMenuItem(this.Title))
     {
         PluginLog.Debug($"{cljb.Where(x => x.NameEnglish.ToString().Equals(this.Title)).First().NameEnglish.ToString()}");
         MZRadialMenu.Instance.ExecuteCommand($"/gs change \"{cljb.Where(x => x.NameEnglish.ToString().Equals(this.Title)).First().NameEnglish.ToString()}\"");
     }
 }
예제 #8
0
    public MusicManager()
    {
        SignatureHelper.Initialise(this);
        var framework          = Dalamud.Framework.Address.BaseAddress;
        var musicManagerOffset = *( int * )(_musicInitCallLocation + 3);

        _musicManager = *( IntPtr * )(framework + musicManagerOffset);
        PluginLog.Debug("MusicManager found at 0x{Location:X16}", _musicManager.ToInt64());
    }
예제 #9
0
    // Reload the whole filesystem from currently loaded mods and the current sort order file.
    // Used on construction and on mod rediscoveries.
    private void Reload()
    {
        if (Load(new FileInfo(ModFileSystemFile), Penumbra.ModManager, ModToIdentifier, ModToName))
        {
            Save();
        }

        PluginLog.Debug("Reloaded mod filesystem.");
    }
예제 #10
0
    public void SetPose(int which, byte toWhat)
    {
        if (toWhat == UnchangedPose)
            return;

        if (toWhat == DefaultPose)
        {
            toWhat = _defaultPoses[which];
        }
        else if (toWhat >= NumPoses[which])
        {
            PluginLog.Error($"Higher pose requested than possible for {PoseNames[which]}: {toWhat} / {NumPoses[which]}.");
            return;
        }

        if (PlayerPointer == IntPtr.Zero)
            return;

        var currentState = GetSeatingState();
        currentState = TranslateState(currentState, WeaponDrawn);
        var pose = GetPose(which);
        if (currentState == which)
        {
            if (toWhat == GetCPoseActorState())
            {
                if (pose != toWhat)
                {
                    WritePose(which, toWhat);
                    PluginLog.Debug("Overwrote {OldPose} with {NewPose} for {WhichPose:l}, currently in {CurrentState:l}.", pose, toWhat,
                        PoseNames[which], PoseNames[currentState]);
                }
            }
            else
            {
                Task.Run(() =>
                {
                    var i = 0;
                    do
                    {
                        PluginLog.Debug("Execute /cpose to get from {OldPose} to {NewPose} of {CurrentState:l}.", pose, toWhat,
                            PoseNames[currentState]);
                        _commandManager.Execute("/cpose");
                        Task.Delay(50);
                    } while (toWhat != GetCPoseActorState() && i++ < 8);

                    if (i > 8)
                        PluginLog.Error("Could not change pose of {CurrentState:l}.", PoseNames[GetCPoseActorState()]);
                });
            }
        }
        else if (pose != toWhat)
        {
            WritePose(which, toWhat);
            PluginLog.Debug("Overwrote {OldPose} with {NewPose} for {WhichPose:l}, currently in {CurrentState:l}.", pose, toWhat,
                PoseNames[which], PoseNames[currentState]);
        }
    }
예제 #11
0
        // private static bool CurrentSongShouldBeIgnored()
        // {
        //     if (OrchestrionPlugin.Configuration.SongReplacements.TryGetValue(CurrentSongId, out var potentialReplacement))
        //     {
        //         if (PlayingSongId != 0 && potentialReplacement.ReplacementId == SongReplacement.NoChangeId)
        //             return true;
        //     }
        //
        //     return false;
        // }

        public static void SetSong(ushort songId, int priority = 0)
        {
            if (priority < 0 || priority >= SceneCount)
            {
                throw new IndexOutOfRangeException();
            }

            if (BGMAddressResolver.BGMSceneList != IntPtr.Zero)
            {
                unsafe
                {
                    var bgms = (BGMScene *)BGMAddressResolver.BGMSceneList.ToPointer();
                    // sometimes we only have to set the first and it will set the other 2
                    // but particularly on stop/clear, the 2nd seems important as well
                    bgms[priority].bgmReference  = songId;
                    bgms[priority].bgmId         = songId;
                    bgms[priority].previousBgmId = songId;

                    if (songId == 0 && priority == 0)
                    {
                        bgms[priority].flags = SceneZeroFlags;
                    }

                    // these are probably not necessary, but clear them to be safe
                    bgms[priority].timer       = 0;
                    bgms[priority].timerEnable = 0;

                    PlayingSongId = songId;
                    PlayingScene  = priority;

                    // unk5 is set to 0x100 by the game in some cases for priority 0
                    // but I wasn't able to see that it did anything

                    if (!SongList.TryGetSong(songId, out var song))
                    {
                        return;
                    }

                    // I hate my life
                    if (song.DisableRestart)
                    {
                        bgms[priority].flags = SceneFlags.EnableDisableRestart;
                        var disableRestart = AddDisableRestartId(&bgms[priority], songId);
                        PluginLog.Debug($"AddDisableRestartId: {(ulong) disableRestart:X}");
                        bgms[priority].flags = SceneZeroFlags;

                        // A lot.
                        Task.Delay(500).ContinueWith(_ =>
                        {
                            bgms[priority].flags = GetSceneFlagsNeededForBgm(song);
                        });
                    }
                }
            }
        }
예제 #12
0
        public Penumbra(DalamudPluginInterface pluginInterface)
        {
            FFXIVClientStructs.Resolver.Initialize();
            Dalamud.Initialize(pluginInterface);
            GameData.GameData.GetIdentifier(Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
            Config = Configuration.Load();

            MusicManager = new MusicManager();
            MusicManager.DisableStreaming();

            var gameUtils = Service <ResidentResources> .Set();

            PlayerWatcher = PlayerWatchFactory.Create(Dalamud.Framework, Dalamud.ClientState, Dalamud.Objects);
            Service <MetaDefaults> .Set();

            var modManager = Service <ModManager> .Set();

            modManager.DiscoverMods();

            ObjectReloader = new ObjectReloader(modManager, Config.WaitFrames);

            ResourceLoader = new ResourceLoader(this);

            Dalamud.Commands.AddHandler(CommandName, new CommandInfo(OnCommand)
            {
                HelpMessage = "/penumbra - toggle ui\n/penumbra reload - reload mod file lists & discover any new mods",
            });

            ResourceLoader.Init();
            ResourceLoader.Enable();

            gameUtils.ReloadPlayerResources();

            SettingsInterface = new SettingsInterface(this);

            if (Config.EnableHttpApi)
            {
                CreateWebServer();
            }

            if (!Config.EnablePlayerWatch || !Config.IsEnabled)
            {
                PlayerWatcher.Disable();
            }

            PlayerWatcher.PlayerChanged += p =>
            {
                PluginLog.Debug("Triggered Redraw of {Player}.", p.Name);
                ObjectReloader.RedrawObject(p, RedrawType.OnlyWithSettings);
            };

            Api = new PenumbraApi(this);
            SubscribeItemLinks();
            Ipc = new PenumbraIpc(pluginInterface, Api);
        }
예제 #13
0
        public SeAddressBase(SigScanner sigScanner, string signature, int offset = 0)
        {
            Address = sigScanner.GetStaticAddressFromSig(signature);
            if (Address != IntPtr.Zero)
            {
                Address += offset;
            }
            var baseOffset = (ulong)Address.ToInt64() - (ulong)sigScanner.Module.BaseAddress.ToInt64();

            PluginLog.Debug($"{GetType().Name} address 0x{Address.ToInt64():X16}, baseOffset 0x{baseOffset:X16}.");
        }
예제 #14
0
        public Hook <T>?CreateHook(T detour, object callback)
        {
            if (Address != IntPtr.Zero)
            {
                var hook = new Hook <T>(Address, (T)detour, callback);
                hook.Enable();
                PluginLog.Debug($"Hooked onto {GetType().Name} at address 0x{Address.ToInt64():X16}.");
                return(hook);
            }

            PluginLog.Error($"Trying to create Hook for {GetType().Name}, but no pointer available.");
            return(null);
        }
예제 #15
0
 // Create a backup. Overwrites pre-existing backups.
 private void Create()
 {
     try
     {
         Delete();
         ZipFile.CreateFromDirectory(_mod.ModPath.FullName, Name, CompressionLevel.Optimal, false);
         PluginLog.Debug("Created backup file {backupName} from {modDirectory}.", Name, _mod.ModPath.FullName);
     }
     catch (Exception e)
     {
         PluginLog.Error($"Could not backup mod {_mod.Name} to \"{Name}\":\n{e}");
     }
 }
예제 #16
0
        // Remove the given collection if it exists and is neither the empty nor the default-named collection.
        // If the removed collection was active, it also sets the corresponding collection to the appropriate default.
        // Also removes the collection from inheritances of all other collections.
        public bool RemoveCollection(int idx)
        {
            if (idx <= Empty.Index || idx >= _collections.Count)
            {
                PluginLog.Error("Can not remove the empty collection.");
                return(false);
            }

            if (idx == DefaultName.Index)
            {
                PluginLog.Error("Can not remove the default collection.");
                return(false);
            }

            if (idx == Current.Index)
            {
                SetCollection(DefaultName, Type.Current);
            }

            if (idx == Default.Index)
            {
                SetCollection(Empty, Type.Default);
            }

            foreach (var(characterName, _) in _characters.Where(c => c.Value.Index == idx).ToList())
            {
                SetCollection(Empty, Type.Character, characterName);
            }

            var collection = _collections[idx];

            collection.Delete();
            _collections.RemoveAt(idx);
            foreach (var c in _collections)
            {
                var inheritedIdx = c._inheritance.IndexOf(collection);
                if (inheritedIdx >= 0)
                {
                    c.RemoveInheritance(inheritedIdx);
                }

                if (c.Index > idx)
                {
                    --c.Index;
                }
            }

            PluginLog.Debug("Removed collection {Name:l}.", collection.Name);
            CollectionChanged.Invoke(Type.Inactive, collection, null);
            return(true);
        }
예제 #17
0
        public static void Init(SigScanner sig)
        {
            _baseAddress    = sig.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 85 C0 74 37 83 78 08 04", 2);
            _addRestartId   = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 30 48 8B 41 20 48 8D 79 18");
            _getSpecialMode = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 8B 41 10 33 DB");

            PluginLog.Debug($"BGMAddressResolver init: baseaddress at {_baseAddress.ToInt64():X}");

            var musicLoc    = sig.ScanText("48 8B 8E ?? ?? ?? ?? 39 78 20 0F 94 C2 45 33 C0");
            var musicOffset = Marshal.ReadInt32(musicLoc + 3);

            _musicManager = Marshal.ReadIntPtr(OrchestrionPlugin.Framework.Address.BaseAddress + musicOffset);
            PluginLog.Debug($"MusicManager found at {_musicManager.ToInt64():X}");
        }
예제 #18
0
 public void CalculateEffectiveFileList(DirectoryInfo modDir, bool withMetaManipulations, bool activeCollection)
 {
     PluginLog.Debug("Recalculating effective file list for {CollectionName} [{WithMetaManipulations}] [{IsActiveCollection}]", Name,
                     withMetaManipulations, activeCollection);
     Cache ??= new ModCollectionCache(Name, modDir);
     UpdateSettings(false);
     Cache.CalculateEffectiveFileList();
     if (withMetaManipulations)
     {
         Cache.UpdateMetaManipulations();
         if (activeCollection)
         {
             Service <ResidentResources> .Get().ReloadPlayerResources();
         }
     }
 }
예제 #19
0
    // Delete a pre-existing backup.
    public void Delete()
    {
        if (!Exists)
        {
            return;
        }

        try
        {
            File.Delete(Name);
            PluginLog.Debug("Deleted backup file {backupName}.", Name);
        }
        catch (Exception e)
        {
            PluginLog.Error($"Could not delete file \"{Name}\":\n{e}");
        }
    }
예제 #20
0
 internal static void DisposeDevice()
 {
     try
     {
         CurrentInputDevice.EventReceived -= InputDevice_EventReceived;
         CurrentInputDevice.Reset();
     }
     catch (Exception e)
     {
         PluginLog.Debug($"possible null inputDevice. {e}");
     }
     finally
     {
         CurrentInputDevice?.Dispose();
         CurrentInputDevice = null;
     }
 }
예제 #21
0
		private static unsafe void DrawButtonPlayPause()
		{
			var PlayPauseIcon =
				IsPlaying ? FontAwesomeIcon.Pause.ToIconString() : FontAwesomeIcon.Play.ToIconString();
			if (ImGui.Button(PlayPauseIcon))
			{
				PluginLog.Debug($"PlayPause pressed. wasplaying: {IsPlaying}");
				if (IsPlaying)
				{
					PlayerControl.Pause();
				}
				else
				{
					PlayerControl.Play();
				}
			}
		}
예제 #22
0
        private IntPtr AddonSelectYesNoOnSetupDetour(IntPtr addon, uint a2, IntPtr dataPtr)
        {
            PluginLog.Debug($"AddonSelectYesNo.OnSetup");
            var result = AddonSelectYesNoOnSetupHook.Original(addon, a2, dataPtr);

            try
            {
                var data = Marshal.PtrToStructure <AddonSelectYesNoOnSetupData>(dataPtr);
                var text = LastSeenDialogText = Marshal.PtrToStringAnsi(data.textPtr).Replace('\n', ' ');

                if (Configuration.Enabled)
                {
                    foreach (var item in Configuration.TextEntries)
                    {
                        if (item.Enabled && !string.IsNullOrEmpty(item.Text))
                        {
                            if ((item.IsRegex && (item.Regex?.IsMatch(text) ?? false)) ||
                                (!item.IsRegex && text.Contains(item.Text)))
                            {
                                unsafe
                                {
                                    var addonObj  = (AddonSelectYesno *)addon;
                                    var yesButton = addonObj->YesButton;
                                    if (yesButton != null && !yesButton->IsEnabled)
                                    {
                                        PluginLog.Debug($"AddonSelectYesNo: Enabling yes button");
                                        yesButton->AtkComponentBase.OwnerNode->AtkResNode.Flags ^= 1 << 5;
                                    }
                                }

                                PluginLog.Debug($"AddonSelectYesNo: Selecting yes");
                                Click.SendClick("select_yes");
                                break;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                PluginLog.Error(ex, "Don't crash the game");
            }

            return(result);
        }
예제 #23
0
        // Add a new collection of the given name.
        // If duplicate is not-null, the new collection will be a duplicate of it.
        // If the name of the collection would result in an already existing filename, skip it.
        // Returns true if the collection was successfully created and fires a Inactive event.
        // Also sets the current collection to the new collection afterwards.
        public bool AddCollection(string name, ModCollection?duplicate)
        {
            if (!CanAddCollection(name, out var fixedName))
            {
                PluginLog.Warning($"The new collection {name} would lead to the same path {fixedName} as one that already exists.");
                return(false);
            }

            var newCollection = duplicate?.Duplicate(name) ?? CreateNewEmpty(name);

            newCollection.Index = _collections.Count;
            _collections.Add(newCollection);
            newCollection.Save();
            PluginLog.Debug("Added collection {Name:l}.", newCollection.Name);
            CollectionChanged.Invoke(Type.Inactive, null, newCollection);
            SetCollection(newCollection.Index, Type.Current);
            return(true);
        }
예제 #24
0
        public MusicManager( )
        {
            var framework = Dalamud.Framework.Address.BaseAddress;

            // the wildcard is basically the framework offset we want (lol)
            // .text:000000000009051A 48 8B 8E 18 2A 00 00      mov rcx,  [rsi+2A18h]
            // .text:0000000000090521 39 78 20                  cmp     [rax+20h], edi
            // .text:0000000000090524 0F 94 C2                  setz    dl
            // .text:0000000000090527 45 33 C0                  xor     r8d, r8d
            // .text:000000000009052A E8 41 1C 15 00            call    musicInit
            var musicInitCallLocation = Dalamud.SigScanner.ScanText("48 8B 8E ?? ?? ?? ?? 39 78 20 0F 94 C2 45 33 C0");
            var musicManagerOffset    = *( int * )(musicInitCallLocation + 3);

            PluginLog.Debug("Found MusicInitCall location at 0x{Location:X16}. Framework offset for MusicManager is 0x{Offset:X8}",
                            musicInitCallLocation.ToInt64(), musicManagerOffset);
            _musicManager = *( IntPtr * )(framework + musicManagerOffset);
            PluginLog.Debug("MusicManager found at 0x{Location:X16}", _musicManager);
        }
예제 #25
0
        internal static async Task WaitSwitchInstrument()
        {
            var match = regex.Match(PlaylistManager.Filelist[PlaylistManager.CurrentPlaying].Item2);

            if (Plugin.config.autoSwitchInstrument && match.Success)
            {
                var wasplaying = Plugin.IsPlaying;
                Plugin.currentPlayback?.Stop();
                var captured = match.Groups[1].Value.ToLowerInvariant();

                Perform possibleInstrument = Plugin.InstrumentSheet.FirstOrDefault(i => i.Instrument.RawString.ToLowerInvariant() == captured);
                Perform possibleGMName     = Plugin.InstrumentSheet.FirstOrDefault(i => i.Name.RawString.ToLowerInvariant().Contains(captured));

                PluginLog.Debug($"{captured} {possibleInstrument} {possibleGMName} {(possibleInstrument ?? possibleGMName)?.Instrument} {(possibleInstrument ?? possibleGMName)?.Name}");

                var key = possibleInstrument ?? possibleGMName;

                if (key != null)
                {
                    if (key.RowId != 0)
                    {
                        await SwitchTo(key.RowId);
                    }
                    else
                    {
                        PluginLog.Debug("key.RowId == 0, not gonna switch instrument.");
                    }
                }
                else
                {
                    PluginLog.Error($"no instrument named {captured} found.");
                }

                if (wasplaying)
                {
                    Plugin.currentPlayback?.Start();
                }

                //PluginLog.Debug($"groups {match.Groups.Count}; captures {match.Captures.Count}");
                //PluginLog.Debug("groups: " + string.Join("/", match.Groups.OfType<Group>().Select(i => $"[{i.Name}] {i.Value}")));
                //PluginLog.Debug("captures: " + string.Join("/", match.Captures.OfType<Capture>().Select(i => i.Value)));
            }
        }
예제 #26
0
        // Load a new mod and add it to the manager if successful.
        public void AddMod(DirectoryInfo modFolder)
        {
            if (_mods.Any(m => m.ModPath.Name == modFolder.Name))
            {
                return;
            }

            var mod = LoadMod(modFolder);

            if (mod == null)
            {
                return;
            }

            mod.Index = _mods.Count;
            _mods.Add(mod);
            ModPathChanged.Invoke(ModPathChangeType.Added, mod, null, mod.ModPath);
            PluginLog.Debug("Added new mod {Name:l} from {Directory:l}.", mod.Name, modFolder.FullName);
        }
예제 #27
0
    // Restore a mod from a pre-existing backup. Does not check if the mod contained in the backup is even similar.
    // Does an automatic reload after extraction.
    public void Restore()
    {
        try
        {
            if (Directory.Exists(_mod.ModPath.FullName))
            {
                Directory.Delete(_mod.ModPath.FullName, true);
                PluginLog.Debug("Deleted mod folder {modFolder}.", _mod.ModPath.FullName);
            }

            ZipFile.ExtractToDirectory(Name, _mod.ModPath.FullName);
            PluginLog.Debug("Extracted backup file {backupName} to {modName}.", Name, _mod.ModPath.FullName);
            Penumbra.ModManager.ReloadMod(_mod.Index);
        }
        catch (Exception e)
        {
            PluginLog.Error($"Could not restore {_mod.Name} from backup \"{Name}\":\n{e}");
        }
    }
예제 #28
0
        private void DrawLogLoadedFilesBox()
        {
            ImGui.Checkbox(LabelLogLoadedFiles, ref _base._penumbra.ResourceLoader.LogAllFiles);
            ImGui.SameLine();
            var regex = _base._penumbra.ResourceLoader.LogFileFilter?.ToString() ?? string.Empty;
            var tmp   = regex;

            if (ImGui.InputTextWithHint("##LogFilter", "Matching this Regex...", ref tmp, 64) && tmp != regex)
            {
                try
                {
                    var newRegex = tmp.Length > 0 ? new Regex(tmp, RegexOptions.Compiled) : null;
                    _base._penumbra.ResourceLoader.LogFileFilter = newRegex;
                }
                catch (Exception e)
                {
                    PluginLog.Debug("Could not create regex:\n{Exception}", e);
                }
            }
        }
예제 #29
0
            public unsafe SafeNamePlateObject GetNamePlateObject(int index)
            {
                if (Pointer == IntPtr.Zero)
                {
                    PluginLog.Debug($"[{GetType().Name}] AddonNamePlate was null");
                    return(null);
                }

                var npObjectArrayPtrPtr = Pointer + Marshal.OffsetOf(typeof(AddonNamePlate), nameof(AddonNamePlate.NamePlateObjectArray)).ToInt32();
                var npObjectArrayPtr    = Marshal.ReadIntPtr(npObjectArrayPtrPtr);

                if (npObjectArrayPtr == IntPtr.Zero)
                {
                    PluginLog.Debug($"[{GetType().Name}] NamePlateObjectArray was null");
                    return(null);
                }

                var npObjectPtr = npObjectArrayPtr + Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject)) * index;

                return(new SafeNamePlateObject(npObjectPtr, index));
            }
예제 #30
0
        public static Playback GetFilePlayback(this(MidiFile, string) fileTuple)
        {
            var file = fileTuple.Item1;

            MidiClockSettings clock = new MidiClockSettings {
                CreateTickGeneratorCallback = () => new HighPrecisionTickGenerator()
            };

            try
            {
                CurrentTMap = file.GetTempoMap();
            }
            catch (Exception e)
            {
                PluginLog.Debug("error when getting tmap, using default tmap instead.");
                CurrentTMap = TempoMap.Default;
            }

            Playback playback;

            try
            {
                CurrentTracks = file.GetTrackChunks()
                                .Where(i => i.GetNotes().Any())
                                .Select(i =>
                {
                    var TrackName = string.Join(", ", i.Events.OfType <SequenceTrackNameEvent>().Select(j => j.Text.Replace("\0", string.Empty).Trim()));
                    //var ProgramChangeEvent = string.Join(", ", i.Events.OfType<ProgramChangeEvent>().Select(j => j.ToString()));
                    if (string.IsNullOrWhiteSpace(TrackName))
                    {
                        TrackName = "Untitled";
                    }
                    //var EventTypes = string.Join(", ", i.Events.GroupBy(j => j.EventType).Select(j => j.Key));
                    //var instrumentsName = string.Join(", ", i.Events.OfType<InstrumentNameEvent>().Select(j => j));

                    //try
                    //{
                    var notes        = i.GetNotes().ToList();
                    var notesCount   = notes.Count;
                    var notesHighest = notes.MaxElement(j => (int)j.NoteNumber).ToString();
                    var notesLowest  = notes.MinElement(j => (int)j.NoteNumber).ToString();
                    //TrackName = "Note Track " + TrackName;
                    var duration = TimeSpan.FromTicks(i.GetPlayback(CurrentTMap).GetDuration <MetricTimeSpan>().TotalMicroseconds * 10);
                    //return (i, $"{TrackName} / {notesCount} notes / {notesLowest}-{notesHighest} / {(int)duration.TotalMinutes:00}:{duration.Seconds:00}.{duration.Milliseconds:000}");
                    return(i, $"{TrackName} / {notesCount} notes / {notesLowest}-{notesHighest}");
                    //}
                    //catch (Exception e)
                    //{
                    //	var eventsCount = i.Events.Count;
                    //	var events = string.Join("\n", i.Events.Select(j => j.ToString().Replace("\0", string.Empty).Trim()));
                    //	var eventTypes = string.Join("", i.Events.GroupBy(j => j.EventType).Select(j => $"\n[{j.Key} {j.Count()}]"));
                    //	TrackName = "Control Track " + TrackName;
                    //	return (i, $"{TrackName} / {eventsCount} events{eventTypes}\n{file.GetDuration<MetricTimeSpan>()} / {i.GetPlayback(CurrentTMap).GetDuration<MetricTimeSpan>()}");
                    //}
                }).ToList();

                List <TrackChunk> SelectedTracks = new List <TrackChunk>();
                if (CurrentTracks.Count > 1)
                {
                    for (int i = 0; i < CurrentTracks.Count; i++)
                    {
                        if (config.EnabledTracks[i])
                        {
                            SelectedTracks.Add(CurrentTracks[i].Item1);
                        }
                    }
                }
                else
                {
                    SelectedTracks = CurrentTracks.Select(i => i.Item1).ToList();
                }

                playback = SelectedTracks.GetPlayback(CurrentTMap, BardPlayer, clock);
            }
            catch (Exception e)
            {
                PluginLog.Debug("error when parsing tracks, falling back to generated MidiEvent playback.");
                try
                {
                    PluginLog.Debug($"file.Chunks.Count {file.Chunks.Count}");
                    var trackChunks = file.GetTrackChunks().ToList();
                    PluginLog.Debug($"file.GetTrackChunks.Count {trackChunks.Count()}");
                    PluginLog.Debug($"file.GetTrackChunks.First {trackChunks.First()}");
                    PluginLog.Debug($"file.GetTrackChunks.Events.Count {trackChunks.First().Events.Count()}");
                    PluginLog.Debug($"file.GetTrackChunks.Events.OfType<NoteEvent>.Count {trackChunks.First().Events.OfType<NoteEvent>().Count()}");
                    CurrentTracks = trackChunks.Select(i =>
                    {
                        var notes        = i.Events.OfType <NoteEvent>().GetNotes().ToList();
                        var notesCount   = notes.Count;
                        var notesHighest = notes.MaxElement(j => (int)j.NoteNumber).ToString();
                        var notesLowest  = notes.MinElement(j => (int)j.NoteNumber).ToString();

                        var s = $"Reconstructed / {notesCount} notes / {notesLowest}-{notesHighest}";
                        return(new TrackChunk(i.Events.OfType <NoteEvent>()), s);
                    }).ToList();
                    List <TrackChunk> SelectedTracks = new List <TrackChunk>();
                    for (int i = 0; i < CurrentTracks.Count; i++)
                    {
                        if (config.EnabledTracks[i])
                        {
                            SelectedTracks.Add(CurrentTracks[i].Item1);
                        }
                    }
                    playback = SelectedTracks.GetPlayback(CurrentTMap, BardPlayer, clock);
                }
                catch (Exception exception)
                {
                    PluginLog.Error(e, "still errors? check your file");
                    throw;
                }
            }
            playback.InterruptNotesOnStop = true;
            playback.Speed     = config.playSpeed;
            playback.Finished += Playback_Finished;

            return(playback);
        }