/// <summary> /// After basic matching, some alignment points will still remain unmatched. This function takes /// a single chunk of unmatched alignment points (not the whole list), and attempts to re-match them. /// </summary> /// <param name="unmatchedSequence"></param> /// <returns></returns> public static List <AlignmentPoint> ReMatchUnmatchedDialogue(List <AlignmentPoint> unmatchedSequence) { //if empty list given, just return an empty list if (unmatchedSequence.Count == 0) { return(new List <AlignmentPoint>()); } List <MangaGamerDialogue> unmatchedMGs = new List <MangaGamerDialogue>(); List <PS3DialogueFragment> unmatchedPS3Fragments = new List <PS3DialogueFragment>(); List <PS3DialogueInstruction> unmatchedPS3s = new List <PS3DialogueInstruction>(); HashSet <int> alreadySeenPS3ParentIDs = new HashSet <int>(); Dictionary <int, PS3DialogueFragment> ps3DialogueIDToFirstFragmentMapping = new Dictionary <int, PS3DialogueFragment>(); DebugUtils.Print("------------------------------------"); foreach (AlignmentPoint ap in unmatchedSequence) { if (ap.mangaGamerDialogue != null) { DebugUtils.Print($"MG line: {ap.mangaGamerDialogue.data}"); unmatchedMGs.Add(ap.mangaGamerDialogue); } if (ap.ps3DialogFragment != null) { unmatchedPS3Fragments.Add(ap.ps3DialogFragment); if (!alreadySeenPS3ParentIDs.Contains(ap.ps3DialogFragment.parent.ID)) { ps3DialogueIDToFirstFragmentMapping.Add(ap.ps3DialogFragment.parent.ID, ap.ps3DialogFragment); alreadySeenPS3ParentIDs.Add(ap.ps3DialogFragment.parent.ID); DebugUtils.Print($"PS3 parent of below missing fragments [{ap.ps3DialogFragment.parent.ID}]: {ap.ps3DialogFragment.parent.data}"); unmatchedPS3s.Add(ap.ps3DialogFragment.parent); } DebugUtils.Print($"PS3 child [{ap.ps3DialogFragment.parent.ID}]: {ap.ps3DialogFragment.data}"); } } //Try and match the unmatched lines List <InOrderLevenshteinMatcher.LevenshteinResult> greedyMatchResults = InOrderLevenshteinMatcher.DoMatching(unmatchedMGs, unmatchedPS3s); //Use the match results to set associations foreach (var result in greedyMatchResults) { MangaGamerDialogue mgToAssign = unmatchedMGs[result.mgIndex]; //want to get the first ps3 fragment associated with the Dialogue. Use hashmap we made earlier. PS3DialogueFragment ps3FragmentToAssign = ps3DialogueIDToFirstFragmentMapping[unmatchedPS3s[result.ps3Index].ID]; mgToAssign.Associate(ps3FragmentToAssign); } //iterate through the list and add alignment points appropriately List <AlignmentPoint> reAssociatedAlignmentPoints = GetAlignmentPointsFromMGPS3Array(unmatchedMGs, unmatchedPS3Fragments); //Debug: Print out re-assigned alignment points for debugging foreach (AlignmentPoint ap in reAssociatedAlignmentPoints) { DebugUtils.Print(ap.ToString()); } return(reAssociatedAlignmentPoints); }
//This function performs the diff given the two lists of dialogue. //It then UPDATES the values in the mangaGamerDialogueList and the ps3DialogueList (the DialogueBase.other value is updated on each dialogue object!) //If a dialogue cannot be associated, it is set to NULL. public static List <AlignmentPoint> DoDiff(string tempFolderPath, List <MangaGamerDialogue> mangaGamerDialogueList, List <PS3DialogueFragment> ps3DialogueFragments, string debugFilenamePrefix = "") { //Convert PS3 Dialogue list into list of subsections before performing diff - this can be re-assembled later! string mangaGamerDiffInputPath = Path.Combine(tempFolderPath, debugFilenamePrefix + "_diffInputA.txt"); string PS3DiffInputPath = Path.Combine(tempFolderPath, debugFilenamePrefix + "_diffInputB.txt"); string diffOutputPath = Path.Combine(tempFolderPath, debugFilenamePrefix + "_diffOutput.txt"); //write the diff-prepared manga gamer dialogue to a file WriteListOfDialogueToFile(mangaGamerDialogueList, mangaGamerDiffInputPath); //write the diff-prepared ps3 dialogue to a file WriteListOfDialogueToFile(ps3DialogueFragments, PS3DiffInputPath); //do the diff string diffResult = RunDiffTool(mangaGamerDiffInputPath, PS3DiffInputPath); //save the diff to file for debugging using (StreamWriter sw = FileUtils.CreateDirectoriesAndOpen(diffOutputPath, FileMode.Create)) { sw.Write(diffResult); } List <AlignmentPoint> alignmentPoints = new List <AlignmentPoint>(); using (StringReader reader = new StringReader(diffResult)) { string line; //skip the header information while ((line = reader.ReadLine()) != null) { if (line[0] == '@') { break; } } //TODO: need to think of the best way to categorize all mangagamer lines... //a ' ' means lines are the same //a '+' means line is present in PS3 ONLY (it was 'added' in the ps3 version) //a '-' means line is present in mangagamer ONLY (it was 'removed' from the ps3 version) List <AlignmentPoint> unmatchedSequence = new List <AlignmentPoint>(); int mgIndex = 0; int ps3Index = 0; while ((line = reader.ReadLine()) != null) { //classify the line type: char lineType = line[0]; if (lineType == ' ') //lines match { PS3DialogueFragment dummyPS3Instruction = ps3DialogueFragments[ps3Index]; MangaGamerDialogue currentMangaGamer = mangaGamerDialogueList[mgIndex]; //associate the fragment with the mangagamer dialogue currentMangaGamer.Associate(dummyPS3Instruction); //re-match the unmatched sequence, then clear it for next iteration alignmentPoints.AddRange(DialogueReMatcher.ReMatchUnmatchedDialogue(unmatchedSequence)); unmatchedSequence.Clear(); //add the just found instruction alignmentPoints.Add(new AlignmentPoint(currentMangaGamer, dummyPS3Instruction)); mgIndex++; ps3Index++; } else if (lineType == '+') //only exist in ps3 { PS3DialogueFragment currentDialog = ps3DialogueFragments[ps3Index]; unmatchedSequence.Add(new AlignmentPoint(null, currentDialog)); ps3Index++; } else if (lineType == '-') //only exist in mangagamer { MangaGamerDialogue currentDialog = mangaGamerDialogueList[mgIndex]; unmatchedSequence.Add(new AlignmentPoint(currentDialog, null)); mgIndex++; } } //Deal with any leftover unmatched sequences at the end of the file alignmentPoints.AddRange(DialogueReMatcher.ReMatchUnmatchedDialogue(unmatchedSequence)); } return(alignmentPoints); }