public static Dictionary <string, ProgressionUnlocks> GetUnlocks() { Dictionary <string, ProgressionUnlocks> @return = new Dictionary <string, ProgressionUnlocks>(); foreach (ulong key in TrackedFiles[0x75]) { STUHero hero = GetInstance <STUHero>(key); if (hero == null) { continue; } string name = GetString(hero.m_0EDCE350); if (name == null) { continue; } ProgressionUnlocks unlocks = new ProgressionUnlocks(hero); @return[name] = unlocks; } return(@return); }
public void Parse(ICLIFlags toolFlags) { string basePath; if (toolFlags is ExtractFlags flags) { basePath = Path.Combine(flags.OutputPath, Container); } else { throw new Exception("no output path"); } // Do normal heroes first then NPCs, this is because NPCs have a lot of duplicate sounds and normal heroes (should) have none // so any duplicate sounds would only come up while processing NPCs which can be ignored as they (probably) belong to heroes var heroes = Program.TrackedFiles[0x75] .Select(x => new Hero(x)) .OrderBy(x => !x.IsHero) .ThenBy(x => x.GUID.GUID) .ToArray(); foreach (var hero in heroes) { var heroStu = GetInstance <STUHero>(hero.GUID); string heroName = GetValidFilename((GetString(heroStu.m_0EDCE350) ?? $"Unknown{teResourceGUID.Index(hero.GUID)}").TrimEnd(' ')); Logger.Log($"Processing {heroName}"); Combo.ComboInfo baseInfo = default; var heroVoiceSetGuid = GetInstance <STUVoiceSetComponent>(heroStu.m_gameplayEntity)?.m_voiceDefinition; if (SaveVoiceSet(flags, basePath, heroName, heroVoiceSetGuid, ref baseInfo)) { var skins = new ProgressionUnlocks(heroStu).GetUnlocksOfType(UnlockType.Skin); foreach (var unlock in skins) { TACTLib.Logger.Debug("Tool", $"Processing skin {unlock.Name}"); if (!(unlock.STU is STUUnlock_SkinTheme unlockSkinTheme)) { return; } if (unlockSkinTheme.m_0B1BA7C1 != 0) { continue; } Combo.ComboInfo info = default; var skinTheme = GetInstance <STUSkinTheme>(unlockSkinTheme.m_skinTheme); if (skinTheme == null) { continue; } SaveVoiceSet(flags, basePath, heroName, heroVoiceSetGuid, ref info, baseInfo, SkinTheme.GetReplacements(skinTheme)); } } } }
public static List <Replay> ParseReplay(string filePath) { Console.Out.WriteLine($"Processing file: {Path.GetFileName(filePath)}"); List <Replay> replays = new List <Replay>(); var buffer = (Memory <byte>)File.ReadAllBytes(filePath); if (buffer.Length == 0) { return(replays); } foreach (var(filename, b64Str) in ProcessAtoms(buffer)) // hash, payload, settinghash? { byte[] bytes = Convert.FromBase64String(b64Str[1]); // string hex = BitConverter.ToString(bytes); var replayInfo = new Mp4Replay(); replayInfo.Parse(bytes); var heroStu = STUHelper.GetInstance <STUHero>(replayInfo.Header.HeroGuid); var hero = new Hero(heroStu); var unlocks = new ProgressionUnlocks(heroStu); var skins = unlocks.GetUnlocksOfType(UnlockType.Skin); var skinTheme = skins.FirstOrDefault(skin => ((STUUnlock_SkinTheme)skin.STU)?.m_skinTheme == replayInfo.Header.SkinGuid); ulong mapHeaderGuid = (replayInfo.Header.MapGuid & ~0xFFFFFFFF00000000ul) | 0x0790000000000000ul; var mapData = new MapHeader(mapHeaderGuid); var replay = new Replay { Title = filename, Hero = hero.Name, Map = mapData.Name, Skin = skinTheme?.Name ?? "Unknown", RecordedAt = $"{DateTimeOffset.FromUnixTimeSeconds(replayInfo.Header.Timestamp).ToLocalTime()}", HighlightType = $"{replayInfo.Header.Type:G}", Quality = $"{replayInfo.Header.QualityPct}% ({(ReplayQuality)replayInfo.Header.QualityPct})", FilePath = filePath }; replays.Add(replay); } return(replays); }
public void Parse(ICLIFlags toolFlags) { var allUnlocks = new Dictionary <teResourceGUID, UnlockAll>(); foreach (var key in TrackedFiles[0xA5]) { var guid = (teResourceGUID)key; if (!allUnlocks.ContainsKey(guid)) { var unlock = new UnlockAll(key); if (unlock.GUID != 0) { allUnlocks[guid] = unlock; } } } var heroes = TrackedFiles[0x75].Select(STUHelper.GetInstance <STUHero>); foreach (var stuHero in heroes) { var hero = new Hero(stuHero); var progression = new ProgressionUnlocks(stuHero); foreach (var unlock in progression.IterateUnlocks()) { if (allUnlocks.ContainsKey(unlock.GUID)) { allUnlocks[unlock.GUID].Hero = hero.Name; } } } if (toolFlags is ListFlags flags) { OutputJSON(allUnlocks, flags); } }
public void SaveUnlocksForHeroes(ICLIFlags flags, IEnumerable <STUHero> heroes, string basePath, bool npc = false) { if (flags.Positionals.Length < 4) { QueryHelp(QueryTypes); return; } Dictionary <string, Dictionary <string, ParsedArg> > parsedTypes = ParseQuery(flags, QueryTypes, QueryNameOverrides); if (parsedTypes == null) { return; } foreach (STUHero hero in heroes) { if (hero == null) { continue; } string heroNameActual = GetString(hero.m_0EDCE350); if (heroNameActual == null) { continue; } Dictionary <string, ParsedArg> config = GetQuery(parsedTypes, heroNameActual.ToLowerInvariant(), "*"); heroNameActual = heroNameActual.TrimEnd(' '); string heroFileName = GetValidFilename(heroNameActual); if (config.Count == 0) { continue; } string heroPath = Path.Combine(basePath, RootDir, heroFileName); VoiceSet voiceSet = new VoiceSet(hero); ProgressionUnlocks progressionUnlocks = new ProgressionUnlocks(hero); if (progressionUnlocks.LevelUnlocks == null && !npc) { continue; } if (progressionUnlocks.LootBoxesUnlocks != null && npc) { continue; } Log($"Processing unlocks for {heroNameActual}"); { Combo.ComboInfo guiInfo = new Combo.ComboInfo(); foreach (STU_1A496D3C tex in hero.m_8203BFE1) { Combo.Find(guiInfo, tex.m_texture); guiInfo.SetTextureName(tex.m_texture, teResourceGUID.AsString(tex.m_id)); } SaveLogic.Combo.SaveLooseTextures(flags, Path.Combine(heroPath, "GUI"), guiInfo); } if (progressionUnlocks.OtherUnlocks != null) // achievements and stuff { Dictionary <string, TagExpectedValue> tags = new Dictionary <string, TagExpectedValue> { { "event", new TagExpectedValue("base") } }; SaveUnlocks(flags, progressionUnlocks.OtherUnlocks, heroPath, "Achievement", config, tags, voiceSet, hero); } if (npc) { foreach (var skin in hero.m_skinThemes) { if (!config.ContainsKey("skin") || !config["skin"].ShouldDo(GetFileName(skin.m_5E9665E3))) { continue; } SkinTheme.Save(flags, Path.Combine(heroPath, Unlock.GetTypeName(typeof(STUUnlock_SkinTheme)), string.Empty, GetFileName(skin.m_5E9665E3)), skin, hero); } continue; } if (progressionUnlocks.LevelUnlocks != null) // default unlocks { Dictionary <string, TagExpectedValue> tags = new Dictionary <string, TagExpectedValue> { { "event", new TagExpectedValue("base") } }; foreach (LevelUnlocks levelUnlocks in progressionUnlocks.LevelUnlocks) { SaveUnlocks(flags, levelUnlocks.Unlocks, heroPath, "Default", config, tags, voiceSet, hero); } } if (progressionUnlocks.LootBoxesUnlocks != null) { foreach (LootBoxUnlocks lootBoxUnlocks in progressionUnlocks.LootBoxesUnlocks) { if (lootBoxUnlocks.Unlocks == null) { continue; } string lootboxName = LootBox.GetName(lootBoxUnlocks.LootBoxType); var tags = new Dictionary <string, TagExpectedValue> { { "event", new TagExpectedValue(LootBox.GetBasicName(lootBoxUnlocks.LootBoxType)) } }; SaveUnlocks(flags, lootBoxUnlocks.Unlocks, heroPath, lootboxName, config, tags, voiceSet, hero); } } } }
public void Parse(ICLIFlags toolFlags) { string basePath; if (toolFlags is BetterVoiceFlags flags) { basePath = flags.OutputPath; } else { throw new Exception("no output path"); } foreach (var key in Program.TrackedFiles[0x75]) { var hero = GetInstance <STUHero>(key); var progression = new ProgressionUnlocks(hero); if (progression.LootBoxesUnlocks == null) { continue; // no NPCs thanks } string heroNameActual = GetValidFilename((GetString(hero.m_0EDCE350) ?? $"Unknown{teResourceGUID.Index(key)}").TrimEnd(' ')); var voiceSetComponent = GetInstance <STUVoiceSetComponent>(hero.m_gameplayEntity); if (voiceSetComponent?.m_voiceDefinition == null) { continue; } var voiceSetsCombo = new Combo.ComboInfo(); Combo.Find(voiceSetsCombo, voiceSetComponent.m_voiceDefinition); foreach (var voiceSet in voiceSetsCombo.VoiceSets) { if (voiceSet.Value.VoiceLineInstances == null) { continue; } foreach (var voicelineInstanceInfo in voiceSet.Value.VoiceLineInstances) { foreach (var voiceLineInstance in voicelineInstanceInfo.Value) { var soundFilesCombo = new Combo.ComboInfo();; var stimulus = GetInstance <STUVoiceStimulus>(voiceLineInstance.VoiceStimulus); if (stimulus == null) { continue; } var groupName = GetVoiceGroup(voiceLineInstance.VoiceStimulus, stimulus.m_category, stimulus.m_87DCD58E) ?? "Unknown"; foreach (var soundFile in voiceLineInstance.SoundFiles) { Combo.Find(soundFilesCombo, soundFile); } var path = flags.GroupByHero && flags.GroupByType ? Path.Combine(basePath, Container, heroNameActual, groupName) : Path.Combine(basePath, Container, flags.GroupByHero ? Path.Combine(groupName, heroNameActual) : groupName); foreach (var soundInfo in soundFilesCombo.VoiceSoundFiles.Values) { var filename = soundInfo.GetName(); if (!flags.GroupByHero) { filename = $"{heroNameActual}-{soundInfo.GetName()}"; } SaveLogic.Combo.SaveSoundFile(flags, path, soundFilesCombo, soundInfo.GUID, true, filename); } } } } } }
public void SaveHeroSounds(ICLIFlags toolFlags) { string basePath; if (toolFlags is ExtractFlags flags) { basePath = flags.OutputPath; } else { throw new Exception("no output path"); } if (flags.Positionals.Length < 4) { QueryHelp(QueryTypes); return; } Dictionary <string, Dictionary <string, ParsedArg> > parsedTypes = ParseQuery(flags, QueryTypes, QueryNameOverrides); if (parsedTypes == null) { return; } foreach (ulong heroFile in TrackedFiles[0x75]) { STUHero hero = GetInstance <STUHero>(heroFile); if (hero == null) { continue; } string heroNameActual = (GetString(hero.m_0EDCE350) ?? $"Unknown{teResourceGUID.Index(heroFile)}").TrimEnd(' '); Dictionary <string, ParsedArg> config = GetQuery(parsedTypes, heroNameActual.ToLowerInvariant(), "*"); if (config.Count == 0) { continue; } Log($"Processing data for {heroNameActual}"); STUVoiceSetComponent baseComponent = default; Combo.ComboInfo baseInfo = default; string heroFileName = GetValidFilename(heroNameActual); if (SaveSet(flags, basePath, hero.m_gameplayEntity, heroFileName, "Default", ref baseComponent, ref baseInfo)) { if (hero.m_heroProgression == 0) { continue; } var progression = new ProgressionUnlocks(hero); bool npc = progression.LootBoxesUnlocks == null; foreach (Unlock itemInfo in progression.OtherUnlocks) { ProcessUnlock(itemInfo, flags, basePath, heroFileName, hero, baseComponent, baseInfo); } if (npc) { foreach (var skin in hero.m_skinThemes) { SaveSkin(flags, skin.m_5E9665E3, basePath, hero, heroFileName, GetFileName(skin.m_5E9665E3), baseComponent, baseInfo); } continue; } foreach (var defaultUnlocks in progression.LevelUnlocks) { foreach (Unlock unlock in defaultUnlocks.Unlocks) { ProcessUnlock(unlock, flags, basePath, heroFileName, hero, baseComponent, baseInfo); } } foreach (var eventUnlocks in progression.LootBoxesUnlocks) { if (eventUnlocks?.Unlocks == null) { continue; } foreach (Unlock unlock in eventUnlocks.Unlocks) { ProcessUnlock(unlock, flags, basePath, heroFileName, hero, baseComponent, baseInfo); } } } } }
private static void ProcessHeroNames(DynamicChoicesContainer container) { var heroContainer = container.GetType(VALID_HERO_NAMES); var npcContainer = container.GetType(VALID_NPC_NAMES); var owlTeams = container.GetType(VALID_OWL_TEAMS); HashSet <string> handledTeams = new HashSet <string>(); Dictionary <ulong, STUHero> heroes = new Dictionary <ulong, STUHero>(); Dictionary <string, int> nameOccurrances = new Dictionary <string, int>(); foreach (ulong heroGUID in Program.TrackedFiles[0x75]) { var hero = STUHelper.GetInstance <STUHero>(heroGUID); if (hero == null) { continue; } string heroNameActual = Hero.GetCleanName(hero); if (heroNameActual == null) { continue; } heroes[heroGUID] = hero; if (nameOccurrances.TryGetValue(heroNameActual, out _)) { nameOccurrances[heroNameActual]++; } else { nameOccurrances[heroNameActual] = 0; } } foreach (ulong heroGUID in Program.TrackedFiles[0x75]) { if (!heroes.TryGetValue(heroGUID, out var hero)) { continue; } string heroNameActual = Hero.GetCleanName(hero); var doGuidName = nameOccurrances[heroNameActual] > 1; if (doGuidName) { heroNameActual += $" ({teResourceGUID.Index(heroGUID):X})"; } ProgressionUnlocks progressionUnlocks = new ProgressionUnlocks(hero); var choice = new DynamicChoice { DisplayName = heroNameActual, QueryName = $"{teResourceGUID.Index(heroGUID):X}" }; if (progressionUnlocks.LevelUnlocks == null) { npcContainer.Choices.Add(choice); } else { heroContainer.Choices.Add(choice); } foreach (var unlock in progressionUnlocks.IterateUnlocks()) { if (string.IsNullOrWhiteSpace(unlock.Name)) { continue; } if (unlock.STU.m_0B1BA7C1 != null) { TeamDefinition teamDef = new TeamDefinition(unlock.STU.m_0B1BA7C1); if (!(teamDef.Division == Enum_5A789F71.None && teamDef.Location == null)) { if (handledTeams.Add(teamDef.FullName)) { owlTeams.Choices.Add(new DynamicChoice { DisplayName = teamDef.FullName, QueryName = teamDef.FullName }); } continue; } } var key = "datatool.ux.valid_" + Unlock.GetTypeName(unlock.STU.GetType()).ToLowerInvariant() + "_names"; if (choice.Children == null) { choice.Children = new DynamicChoicesContainer(); } var test = choice.Children.GetType(key); test.Choices.Add(new DynamicChoice { QueryName = unlock.Name, DisplayName = unlock.Name }); } } }
public static Task <Control> Get(ProgressWorker a1, SynchronizationContext context, Window window, bool npc) { var source = new TaskCompletionSource <Control>(); context.Send(obj => { var control = new ImageGridView(); var t = new Thread(() => { if (!(obj is Tuple <ProgressWorker, TaskCompletionSource <Control> > tuple)) { return; } var worker = tuple.Item1; var tcs = tuple.Item2; try { var i = 0; worker.ReportProgress(0, "Loading heroes..."); if (TrackedFiles == null || !TrackedFiles.ContainsKey(0x75)) { throw new DataToolWpfException("Open storage first"); } var max = TrackedFiles[0x75].Count; foreach (var key in TrackedFiles[0x75]) { try { var hero = GetInstance <STUHero>(key); if (hero == null) { continue; } string heroNameActual = GetString(hero.m_0EDCE350) ?? teResourceGUID.Index(key).ToString("X"); heroNameActual = heroNameActual.TrimEnd(' '); ProgressionUnlocks progressionUnlocks = new ProgressionUnlocks(hero); if (progressionUnlocks.LevelUnlocks == null && !npc) { continue; } if (progressionUnlocks.LootBoxesUnlocks != null && npc) { continue; } var tex = hero.m_8203BFE1.FirstOrDefault(x => teResourceGUID.Index(x.m_id) == 0x40C9 || teResourceGUID.Index(x.m_id) == 0x40CA)?.m_texture; if (tex == 0) { tex = hero.m_8203BFE1.FirstOrDefault()?.m_texture; } var image = new byte[] { }; var width = 128; var height = 128; if (tex != 0) { teTexture texture = new teTexture(OpenFile(tex)); if (texture.PayloadRequired) { ulong payload = texture.GetPayloadGUID(tex); Stream payloadStream = OpenFile(payload); if (payloadStream != null) { texture.LoadPayload(payloadStream); } else { continue; } } width = texture.Header.Width; height = texture.Header.Height; Stream ms = texture.SaveToDDS(); image = DDSConverter.ConvertDDS(ms, DXGI_FORMAT.R8G8B8A8_UNORM, DDSConverter.ImageFormat.PNG, 0); } var entry = control.Add(heroNameActual, image, 128, (int)ImagingHelper.CalculateSizeAS(height, width, 128)); entry.Payload = progressionUnlocks; entry.OnClick += (sender, args) => { window.Close(); }; } catch { // ignored } finally { i += 1; worker.ReportProgress((int)(i / (float)max * 100)); } } tcs.SetResult(control); } catch (Exception e) { tcs.SetException(e); } finally { worker.ReportProgress(100); } }); t.SetApartmentState(ApartmentState.STA); t.Start(); }, new Tuple <ProgressWorker, TaskCompletionSource <Control> >(a1, source)); return(source.Task); }
// Update is called once per frame void Start() { pu = this; pc = PlayerController.pc; picked = false; }
public void Parse(ICLIFlags toolFlags) { string basePath; if (toolFlags is ExtractFlags flags) { basePath = flags.OutputPath; } else { throw new Exception("no output path"); } foreach (var key in Program.TrackedFiles[0x75]) { var hero = GetInstance <STUHero>(key); var progression = new ProgressionUnlocks(hero); if (progression.LootBoxesUnlocks == null) { continue; // no NPCs thanks } string heroNameActual = GetValidFilename((GetString(hero.m_0EDCE350) ?? $"Unknown{teResourceGUID.Index(key)}").TrimEnd(' ')); var voiceSetComponent = GetInstance <STUVoiceSetComponent>(hero.m_gameplayEntity); if (voiceSetComponent?.m_voiceDefinition == null) { continue; } var voiceSetsCombo = new Combo.ComboInfo(); Combo.Find(voiceSetsCombo, voiceSetComponent.m_voiceDefinition); foreach (var voiceSet in voiceSetsCombo.VoiceSets) { if (voiceSet.Value.VoiceLineInstances == null) { continue; } foreach (var voicelineInstanceInfo in voiceSet.Value.VoiceLineInstances) { foreach (var voiceLineInstance in voicelineInstanceInfo.Value) { var soundFilesCombo = new Combo.ComboInfo();; var stimulus = GetInstance <STUVoiceStimulus>(voiceLineInstance.VoiceStimulus); if (stimulus == null) { continue; } var stimulusId = teResourceGUID.AsString(voiceLineInstance.VoiceStimulus); var stimulusCategoryId = teResourceGUID.AsString(stimulus.m_category); var stimulusUnknownId = teResourceGUID.AsString(stimulus.m_87DCD58E); // Some voices can be classified in multiple groups but we can only have one, we use use the Stimulus name first, then the unknown and finally the category var stimMatches = VoiceGroups.FirstOrDefault(x => x.StimulusSetIds.Contains(stimulusId)); var stimCatMatches = VoiceGroups.FirstOrDefault(x => x.StimulusCategoryIds.Contains(stimulusCategoryId)); var stimUnkMatches = VoiceGroups.FirstOrDefault(x => x.StimulusUnknownIds.Contains(stimulusUnknownId)); if (stimMatches == null && stimCatMatches == null && stimUnkMatches == null) { continue; } var groupName = stimMatches?.Name ?? stimUnkMatches?.Name ?? stimCatMatches?.Name; foreach (var soundFile in voiceLineInstance.SoundFiles) { Combo.Find(soundFilesCombo, soundFile); } SaveLogic.Combo.SaveAllVoiceSoundFiles(toolFlags, Path.Combine(basePath, Container, groupName, heroNameActual), soundFilesCombo); } } } } }