private BgmToneKeyReferences GetToneIdKeyReferences(string toneId, string filename = null)
        {
            if (_toneIdKeyReferences.ContainsKey(toneId))
            {
                return(_toneIdKeyReferences[toneId]);
            }
            var output = new BgmToneKeyReferences(toneId, filename);

            _toneIdKeyReferences.Add(toneId, output);
            return(output);
        }
        private void UpdateBgmEntryInStateManager(BgmToneKeyReferences keyRefs, BgmEntry bgmEntry)
        {
            _logger.LogInformation("Updating Bgm Entry to State Service: {ToneId}", keyRefs.ToneId);

            var paramBgmDatabase           = _state.LoadResource <PrcUiBgmDatabase>(Constants.GameResources.PRC_UI_BGM_DB_PATH);
            var paramGameTitleDatabaseRoot = _state.LoadResource <PrcUiGameTitleDatabase>(Constants.GameResources.PRC_UI_GAMETITLE_DB_PATH).DbRootEntries;
            var binBgmPropertyEntries      = _state.LoadResource <BinBgmProperty>(Constants.GameResources.PRC_BGM_PROPERTY_PATH).Entries;

            var defaultLocale   = _config.Value.Sm5shMusic.DefaultLocale;
            var coreSeriesGames = paramGameTitleDatabaseRoot.Values.Select(p => p.UiSeriesId.StringValue).Distinct(); //Not handling series addition right now.

            var toneId = keyRefs.ToneId;

            if (!paramBgmDatabase.DbRootEntries.ContainsKey(keyRefs.DbRootKey))
            {
                throw new Exception($"BGM ID {keyRefs.DbRootKey} does not exist in the DBRoot");
            }

            //BGM PRC
            var dbRootEntry         = paramBgmDatabase.DbRootEntries[keyRefs.DbRootKey];
            var setStreamEntry      = paramBgmDatabase.StreamSetEntries[keyRefs.StreamSetKey];
            var assignedInfoEntry   = paramBgmDatabase.AssignedInfoEntries[keyRefs.AssignedInfoKey];
            var streamPropertyEntry = paramBgmDatabase.StreamPropertyEntries[keyRefs.StreamPropertyKey];

            //DB Root
            dbRootEntry.UiGameTitleId = new PrcHash40(bgmEntry.GameTitle.GameTitleId);
            dbRootEntry.RecordType    = new PrcHash40(bgmEntry.RecordType);
            dbRootEntry.IsPatch       = bgmEntry.IsPatch;
            dbRootEntry.IsDlc         = bgmEntry.IsDlc;
            //Stream Set
            setStreamEntry = FromSpecialCategory(setStreamEntry, bgmEntry?.SpecialCategory);

            //GameTitle PRC
            if (!paramGameTitleDatabaseRoot.ContainsKey(bgmEntry.GameTitle.GameTitleId))
            {
                string seriesId = bgmEntry.GameTitle.SeriesId;
                if (!coreSeriesGames.Contains(seriesId))
                {
                    seriesId = Constants.InternalIds.GAME_SERIES_ID_DEFAULT;
                }
                paramGameTitleDatabaseRoot.Add(bgmEntry.GameTitle.GameTitleId, new PrcGameTitleDbRootEntry()
                {
                    NameId        = bgmEntry.GameTitle.NameId,
                    Release       = paramGameTitleDatabaseRoot.Values.OrderByDescending(p => p.Release).First().Release + 1,
                    UiGameTitleId = new PrcHash40(bgmEntry.GameTitle.GameTitleId),
                    UiSeriesId    = new PrcHash40(seriesId)
                });
            }

            //Bin Property
            binBgmPropertyEntries[toneId].TotalSamples    = bgmEntry.AudioCuePoints.TotalSamples;
            binBgmPropertyEntries[toneId].LoopEndMs       = bgmEntry.AudioCuePoints.LoopEndMs;
            binBgmPropertyEntries[toneId].LoopEndSample   = bgmEntry.AudioCuePoints.LoopEndSample;
            binBgmPropertyEntries[toneId].LoopStartMs     = bgmEntry.AudioCuePoints.LoopStartMs;
            binBgmPropertyEntries[toneId].LoopStartSample = bgmEntry.AudioCuePoints.LoopStartSample;
            binBgmPropertyEntries[toneId].TotalTimeMs     = bgmEntry.AudioCuePoints.TotalTimeMs;
            binBgmPropertyEntries[toneId].NameId          = toneId;

            //Playlists
            if (bgmEntry.Playlists != null)
            {
                foreach (var playlistId in bgmEntry.Playlists)
                {
                    var paramBgmPlaylist = paramBgmDatabase.PlaylistEntries.FirstOrDefault(p => p.Id.StringValue == playlistId.Id)?.Values;
                    if (paramBgmPlaylist == null)
                    {
                        paramBgmPlaylist = new List <PrcBgmPlaylistEntry>();
                        paramBgmDatabase.PlaylistEntries.Add(new PcrFilterStruct <PrcBgmPlaylistEntry>()
                        {
                            Id     = new PrcHash40(playlistId.Id),
                            Values = paramBgmPlaylist
                        });
                    }

                    var newPlaylistEntry = new PrcBgmPlaylistEntry()
                    {
                        UiBgmId = new PrcHash40(dbRootEntry.UiBgmId.StringValue)
                    };
                    newPlaylistEntry.SetOrder((short)paramBgmPlaylist.Count);
                    newPlaylistEntry.SetIncidence(500);
                    paramBgmPlaylist.Add(newPlaylistEntry);
                }
            }

            //MSBT
            var nameId         = dbRootEntry.NameId;
            var gameTitleEntry = paramGameTitleDatabaseRoot[dbRootEntry.UiGameTitleId.StringValue];
            var gameTitleId    = gameTitleEntry.NameId;
            var gameTitleLabel = string.Format(Constants.InternalIds.MSBT_GAME_TITLE, gameTitleId);
            var titleLabel     = string.Format(Constants.InternalIds.MSBT_BGM_TITLE, nameId);
            var authorLabel    = string.Format(Constants.InternalIds.MSBT_BGM_AUTHOR, nameId);
            var copyrightLabel = string.Format(Constants.InternalIds.MSBT_BGM_COPYRIGHT, nameId);

            foreach (var msbtDb in GetBgmDatabases())
            {
                var entries = msbtDb.Value.Entries;

                if (bgmEntry.Title != null && bgmEntry.Title.ContainsKey(msbtDb.Key) && !string.IsNullOrEmpty(bgmEntry.Title[msbtDb.Key]))
                {
                    entries[titleLabel] = bgmEntry.Title[msbtDb.Key];
                }
                else if (bgmEntry.Title != null && bgmEntry.Title.ContainsKey(defaultLocale) && !string.IsNullOrEmpty(bgmEntry.Title[msbtDb.Key]))
                {
                    entries[titleLabel] = bgmEntry.Title[defaultLocale];
                }
                else
                {
                    entries[titleLabel] = "MISSING";
                }

                if (bgmEntry.Author != null)
                {
                    if (bgmEntry.Author.ContainsKey(msbtDb.Key) && !string.IsNullOrEmpty(bgmEntry.Author[msbtDb.Key]))
                    {
                        entries[authorLabel] = bgmEntry.Author[msbtDb.Key];
                    }
                    else if (bgmEntry.Author.ContainsKey(defaultLocale) && !string.IsNullOrEmpty(bgmEntry.Author[msbtDb.Key]))
                    {
                        entries[authorLabel] = bgmEntry.Author[defaultLocale];
                    }
                }

                if (bgmEntry.Copyright != null)
                {
                    if (bgmEntry.Copyright.ContainsKey(msbtDb.Key) && !string.IsNullOrEmpty(bgmEntry.Copyright[msbtDb.Key]))
                    {
                        entries[copyrightLabel] = bgmEntry.Copyright[msbtDb.Key];
                    }
                    else if (bgmEntry.Copyright.ContainsKey(defaultLocale) && !string.IsNullOrEmpty(bgmEntry.Copyright[msbtDb.Key]))
                    {
                        entries[copyrightLabel] = bgmEntry.Copyright[defaultLocale];
                    }
                }
            }
            foreach (var msbtDb in GetGameTitleDatabases())
            {
                var entries = msbtDb.Value.Entries;
                if (bgmEntry.GameTitle.Title.ContainsKey(msbtDb.Key))
                {
                    entries[gameTitleLabel] = bgmEntry.GameTitle.Title[msbtDb.Key];
                }
                else if (bgmEntry.GameTitle.Title.ContainsKey(defaultLocale))
                {
                    entries[gameTitleLabel] = bgmEntry.GameTitle.Title[defaultLocale];
                }
                else
                {
                    entries[gameTitleLabel] = "MISSING";
                }
            }
        }
        private void CreateNewBgmEntryInStateManager(BgmToneKeyReferences keyRefs)
        {
            _logger.LogInformation("Adding Bgm Entry to State Service: {ToneId}", keyRefs.ToneId);

            var paramBgmDatabase  = _state.LoadResource <PrcUiBgmDatabase>(Constants.GameResources.PRC_UI_BGM_DB_PATH);
            var paramBgmDbRoot    = paramBgmDatabase.DbRootEntries;
            var daoBinBgmProperty = _state.LoadResource <BinBgmProperty>(Constants.GameResources.PRC_BGM_PROPERTY_PATH);

            //var saveNoIndex = (short)(_daoUiBgmDbRootEntries.Values.OrderByDescending(p => p.SaveNo).First().SaveNo + 1); //Not working past top save_no id
            var testDispOrderIndex = (short)(paramBgmDbRoot.Values.OrderByDescending(p => p.TestDispOrder).First().TestDispOrder + 1);
            var menuValueIndex     = paramBgmDbRoot.Values.OrderByDescending(p => p.MenuValue).First().MenuValue + 1;

            //New entry - with default values
            paramBgmDbRoot.Add(keyRefs.DbRootKey, new PrcBgmDbRootEntry()
            {
                UiBgmId               = new PrcHash40(keyRefs.DbRootKey),
                StreamSetId           = new PrcHash40(keyRefs.StreamSetKey),
                Rarity                = new PrcHash40(Constants.InternalIds.RARITY_DEFAULT),
                RecordType            = new PrcHash40(Constants.InternalIds.RECORD_TYPE_DEFAULT),
                UiGameTitleId         = new PrcHash40(Constants.InternalIds.GAME_TITLE_ID_DEFAULT),
                UiGameTitleId1        = new PrcHash40(Constants.InternalIds.GAME_TITLE_ID_DEFAULT),
                UiGameTitleId2        = new PrcHash40(Constants.InternalIds.GAME_TITLE_ID_DEFAULT),
                UiGameTitleId3        = new PrcHash40(Constants.InternalIds.GAME_TITLE_ID_DEFAULT),
                UiGameTitleId4        = new PrcHash40(Constants.InternalIds.GAME_TITLE_ID_DEFAULT),
                NameId                = GetNewBgmId(),
                SaveNo                = 0,
                TestDispOrder         = testDispOrderIndex,
                MenuValue             = menuValueIndex,
                JpRegion              = true,
                OtherRegion           = true,
                Possessed             = true,
                PrizeLottery          = false,
                ShopPrice             = 0,
                CountTarget           = true,
                MenuLoop              = 1,
                IsSelectableStageMake = true,
                Unk1    = true,
                Unk2    = true,
                IsDlc   = false,
                IsPatch = false
            });
            paramBgmDatabase.StreamSetEntries.Add(keyRefs.StreamSetKey, new PrcBgmStreamSetEntry()
            {
                StreamSetId     = new PrcHash40(keyRefs.StreamSetKey),
                SpecialCategory = new PrcHash40(0),
                Info0           = new PrcHash40(keyRefs.AssignedInfoKey)
            });
            paramBgmDatabase.AssignedInfoEntries.Add(keyRefs.AssignedInfoKey, new PrcBgmAssignedInfoEntry()
            {
                InfoId                 = new PrcHash40(keyRefs.AssignedInfoKey),
                StreamId               = new PrcHash40(keyRefs.StreamPropertyKey),
                Condition              = new PrcHash40(Constants.InternalIds.SOUND_CONDITION),
                ConditionProcess       = new PrcHash40(0x1b9fe75d3f),
                ChangeFadoutFrame      = 55,
                MenuChangeFadeOutFrame = 55
            });
            paramBgmDatabase.StreamPropertyEntries.Add(keyRefs.StreamPropertyKey, new PrcBgmStreamPropertyEntry()
            {
                StreamId             = new PrcHash40(keyRefs.StreamPropertyKey),
                DateName0            = keyRefs.ToneId,
                Loop                 = 1,
                EndPoint             = "00:00:15.000",
                FadeOutFrame         = 400,
                StartPointTransition = "00:00:04.000"
            });
            daoBinBgmProperty.Entries.Add(keyRefs.ToneId, new BgmPropertyEntry());
        }