public static void GenerateEnFiles(bool prefer_ps3_over_ps4) { // we pretty much do the same as JP here but we have to do an ID remapping, because the PS4 internal IDs for the PC audio files are different VoiceTable canonicalEnVoiceTable; using (var fs = new HyoutaUtils.Streams.DuplicatableFileStream(@"c:\_tmp_a\_cs1-voicetiming\__pc-senpatcher\t_voice_US.tbl")) { canonicalEnVoiceTable = new VoiceTable(fs, EndianUtils.Endianness.LittleEndian); } VoiceTable ps4EnVoiceTable; using (var fs = new HyoutaUtils.Streams.DuplicatableFileStream(@"c:\_tmp_a\_cs1-voicetiming\_ps4-us\t_voice.tbl")) { ps4EnVoiceTable = new VoiceTable(fs, EndianUtils.Endianness.LittleEndian); } Dictionary <string, List <VoiceTableEntry> > pcFilenameToInternalIdMap = new Dictionary <string, List <VoiceTableEntry> >(); foreach (var a in canonicalEnVoiceTable.Entries) { if (!pcFilenameToInternalIdMap.ContainsKey(a.Name)) { pcFilenameToInternalIdMap.Add(a.Name, new List <VoiceTableEntry>()); } pcFilenameToInternalIdMap[a.Name].Add(a); } Dictionary <string, List <VoiceTableEntry> > ps4FilenameToInternalIdMap = new Dictionary <string, List <VoiceTableEntry> >(); foreach (var a in ps4EnVoiceTable.Entries) { string n = a.Name == "pc8v10286" ? "pc8v10299" : a.Name == "pc8v10286_6" ? "pc8v10286" : a.Name; // need to match my remapping of the overwritten sara line if (!ps4FilenameToInternalIdMap.ContainsKey(n)) { ps4FilenameToInternalIdMap.Add(n, new List <VoiceTableEntry>()); } ps4FilenameToInternalIdMap[n].Add(a); } if (pcFilenameToInternalIdMap.Count != ps4FilenameToInternalIdMap.Count) { // shouldn't happen with correct input Console.WriteLine($"global count mismatch PC {pcFilenameToInternalIdMap.Count} <=> PS4 {ps4FilenameToInternalIdMap.Count}"); } foreach (var kvp in pcFilenameToInternalIdMap) { var pc = kvp.Value; var ps4 = ps4FilenameToInternalIdMap[kvp.Key]; if (pc.Count != ps4.Count) { if (pc.Count == 2 && ps4.Count == 1) { // just dupe the PS4 entry to get it to match 1:1 ps4.Add(new VoiceTableEntry(ps4[0])); } else { // shouldn't happen with correct input Console.WriteLine($"count mismatch PC {pc.Count} <=> PS4 {ps4.Count}"); } } } // okay now we have a 1:1 mapping between PC and PS4 IDs; we can apply this on the PS4 voice timing file to get a PC-compatible one VoiceTiming enPs3PcVoiceTiming; // is the same file in both versions, just endian-flipped using (var fs = new HyoutaUtils.Streams.DuplicatableFileStream(@"c:\_tmp_a\_cs1-voicetiming\_pc-us\t_vctiming.tbl")) { enPs3PcVoiceTiming = new VoiceTiming(fs, EndianUtils.Endianness.LittleEndian); } VoiceTiming enPs4USVoiceTiming; using (var fs = new HyoutaUtils.Streams.DuplicatableFileStream(@"c:\_tmp_a\_cs1-voicetiming\_ps4-us\t_vctiming_us.tbl")) { enPs4USVoiceTiming = new VoiceTiming(fs, EndianUtils.Endianness.LittleEndian); } SortedDictionary <ushort, VoiceTimingEntry> voiceTimingDict = new SortedDictionary <ushort, VoiceTimingEntry>(); foreach (VoiceTimingEntry vte in enPs3PcVoiceTiming.Entries) { if (voiceTimingDict.ContainsKey(vte.Index)) { Console.WriteLine($"index {vte.Index} mapped more than once?"); var existingMapping = voiceTimingDict[vte.Index]; if (existingMapping.TimingData != vte.TimingData) { Console.WriteLine($"timing data mismatch: {existingMapping.TimingData.ToString("x16")} <=> {vte.TimingData.ToString("x16")}"); } voiceTimingDict.Remove(vte.Index); } voiceTimingDict.Add(vte.Index, vte); } HashSet <ushort> mappedIndices = new HashSet <ushort>(); foreach (VoiceTableEntry vte in canonicalEnVoiceTable.Entries) { if (!mappedIndices.Contains(vte.Index)) { mappedIndices.Add(vte.Index); } } foreach (VoiceTimingEntry vte in enPs4USVoiceTiming.Entries) { List <VoiceTableEntry> specialPcEntries = null; List <VoiceTableEntry> specialPs4Entries = null; ushort remappedIndex = RemapPs4ToPc(vte.Index, pcFilenameToInternalIdMap, ps4FilenameToInternalIdMap, ref specialPcEntries, ref specialPs4Entries); ushort[] indices; if (specialPcEntries != null) { indices = new ushort[2] { specialPcEntries[0].Index, specialPcEntries[1].Index }; } else { indices = new ushort[1] { remappedIndex }; } foreach (ushort index in indices) { if (mappedIndices.Contains(index)) { if (prefer_ps3_over_ps4) { // only insert if there's not already an entry if (!voiceTimingDict.ContainsKey(index)) { VoiceTimingEntry vte2 = new VoiceTimingEntry(vte); vte2.Index = index; voiceTimingDict.Add(index, vte2); } } else { // overwrite existing entries if (voiceTimingDict.ContainsKey(index)) { voiceTimingDict.Remove(index); } VoiceTimingEntry vte2 = new VoiceTimingEntry(vte); vte2.Index = index; voiceTimingDict.Add(index, vte2); } } } } enPs3PcVoiceTiming.Entries.Clear(); foreach (var kvp in voiceTimingDict) { enPs3PcVoiceTiming.Entries.Add(kvp.Value); } using (var fs = new FileStream(@"c:\_tmp_a\_cs1-voicetiming\__pc-senpatcher\__gen_vctiming_us_ps" + (prefer_ps3_over_ps4 ? "3" : "4") + "_variant.tbl", FileMode.Create)) { enPs3PcVoiceTiming.WriteToStream(fs, EndianUtils.Endianness.LittleEndian); } }
public static void GenerateJpFiles(bool prefer_ps3_over_ps4) { VoiceTable canonicalJpVoiceTable; using (var fs = new HyoutaUtils.Streams.DuplicatableFileStream(@"c:\_tmp_a\_cs1-voicetiming\__pc-senpatcher\t_voice_JP.tbl")) { canonicalJpVoiceTable = new VoiceTable(fs, EndianUtils.Endianness.LittleEndian); } VoiceTiming jpPs3VoiceTiming; using (var fs = new HyoutaUtils.Streams.DuplicatableFileStream(@"c:\_tmp_a\_cs1-voicetiming\_ps3-jp\t_vctiming.tbl")) { jpPs3VoiceTiming = new VoiceTiming(fs, EndianUtils.Endianness.BigEndian); } VoiceTiming jpPs4USVoiceTiming; using (var fs = new HyoutaUtils.Streams.DuplicatableFileStream(@"c:\_tmp_a\_cs1-voicetiming\_ps4-us\t_vctiming.tbl")) { jpPs4USVoiceTiming = new VoiceTiming(fs, EndianUtils.Endianness.LittleEndian); } SortedDictionary <ushort, VoiceTimingEntry> voiceTimingDict = new SortedDictionary <ushort, VoiceTimingEntry>(); foreach (VoiceTimingEntry vte in jpPs3VoiceTiming.Entries) { if (voiceTimingDict.ContainsKey(vte.Index)) { Console.WriteLine($"index {vte.Index} mapped more than once?"); voiceTimingDict.Remove(vte.Index); } voiceTimingDict.Add(vte.Index, vte); } HashSet <ushort> mappedIndices = new HashSet <ushort>(); foreach (VoiceTableEntry vte in canonicalJpVoiceTable.Entries) { if (!mappedIndices.Contains(vte.Index)) { mappedIndices.Add(vte.Index); } } foreach (VoiceTimingEntry vte in jpPs4USVoiceTiming.Entries) { if (mappedIndices.Contains(vte.Index)) { if (prefer_ps3_over_ps4) { // only insert if there's not already an entry if (!voiceTimingDict.ContainsKey(vte.Index)) { voiceTimingDict.Add(vte.Index, vte); } } else { // overwrite existing entries if (voiceTimingDict.ContainsKey(vte.Index)) { voiceTimingDict.Remove(vte.Index); } voiceTimingDict.Add(vte.Index, vte); } } } jpPs3VoiceTiming.Entries.Clear(); foreach (var kvp in voiceTimingDict) { jpPs3VoiceTiming.Entries.Add(kvp.Value); } using (var fs = new FileStream(@"c:\_tmp_a\_cs1-voicetiming\__pc-senpatcher\__gen_vctiming_jp_ps" + (prefer_ps3_over_ps4 ? "3" : "4") + "_variant.tbl", FileMode.Create)) { jpPs3VoiceTiming.WriteToStream(fs, EndianUtils.Endianness.LittleEndian); } }