/// <summary> /// Create our SubjectData by parsing the stock "experiment@situation" subject id string. /// Used for asteroid samples, for compatibility with RnD archive data of removed mods and for converting stock ScienceData into SubjectData /// </summary> public static SubjectData GetSubjectDataFromStockId(string stockSubjectId, ScienceSubject RnDSubject = null) { SubjectData subjectData = null; if (unknownSubjectDatas.TryGetValue(stockSubjectId, out subjectData)) { return(subjectData); } string[] expAndSit = stockSubjectId.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries); if (expAndSit.Length != 2) { Lib.Log("Could not parse the SubjectData from subjectId '" + stockSubjectId + "' : bad format"); return(null); } // the recovery experiment subject are created in ResearchAndDevelopment.reverseEngineerRecoveredVessel, called on the vessel recovery event // it use a non-standard situation system ("body" + a situation enum "RecoverySituations"). We ignore those. if (expAndSit[0] == "recovery") { return(null); } ExperimentInfo expInfo = GetExperimentInfo(expAndSit[0]); if (expInfo == null) { Lib.Log("Could not parse the SubjectData from subjectId '" + stockSubjectId + "' : the experiment id '" + expAndSit[0] + "' doesn't exists"); return(null); } // for subject ids created with the ResearchAndDevelopment.GetExperimentSubject overload that take a "sourceUId" string, // the sourceUId is added to the situation after a "_" // in stock this seems to be used only for asteroids, and I don't think any mod use it. string extraSituationInfo = string.Empty; if (expAndSit[1].Contains("_")) { string[] sitAndAsteroid = expAndSit[1].Split('_'); // remove expAndSit[1] = sitAndAsteroid[0]; // asteroid are saved as "part.partInfo.name + part.flightID", and the part name always end with a randomly generated "AAA-000" string extraSituationInfo = Regex.Match(sitAndAsteroid[1], ".*?-[0-9][0-9][0-9]").Value; // if no match, just use the unmodified string if (extraSituationInfo == string.Empty) { extraSituationInfo = sitAndAsteroid[1]; } } string[] bodyAndBiome = expAndSit[1].Split(ScienceSituationUtils.validSituationsStrings, StringSplitOptions.RemoveEmptyEntries); string situation; if (bodyAndBiome.Length == 1) { situation = expAndSit[1].Substring(bodyAndBiome[0].Length); } else if (bodyAndBiome.Length == 2) { situation = expAndSit[1].Substring(bodyAndBiome[0].Length, expAndSit[1].Length - bodyAndBiome[0].Length - bodyAndBiome[1].Length); } else { Lib.Log("Could not parse the SubjectData from subjectId '" + stockSubjectId + "' : the situation doesn't exists"); return(null); } CelestialBody subjectBody = null; foreach (CelestialBody body in FlightGlobals.Bodies) { if (body.name == bodyAndBiome[0]) { subjectBody = body; break; } } if (subjectBody == null) { // TODO : DMOS asteroid experiments are doing : "magScan@AsteroidInSpaceLowCarbonaceous7051371", those subjects will be discarded entirely here // because the body "Asteroid" doesn't exists, consequently it's impossible to create the Situation object. // To handle that, maybe we could implement a derived class "UnknownSituation" from Situation that can handle a completely random subject format Lib.Log("Could not parse the SubjectData from subjectId '" + stockSubjectId + "' : the body '" + bodyAndBiome[0] + "' doesn't exist"); return(null); } ScienceSituation scienceSituation = ScienceSituationUtils.ScienceSituationDeserialize(situation); int biomeIndex = -1; if (bodyAndBiome.Length == 2 && ScienceSituationUtils.IsBodyBiomesRelevantForExperiment(scienceSituation, expInfo) && subjectBody.BiomeMap != null) { for (int i = 0; i < subjectBody.BiomeMap.Attributes.Length; i++) { // Note : a stock subject has its spaces in the biome name removed but prior versions of kerbalism didn't do that, // so we try to fix it, in order not to create duplicates in the RnD archives. // TODO : also, we need to remove the "reentry" subjects, as stock is failing to parse them, altough this is in a try/catch block and handled gracefully. string sanitizedBiome = bodyAndBiome[1].Replace(" ", string.Empty); if (RnDSubject != null && extraSituationInfo == string.Empty && sanitizedBiome != bodyAndBiome[1]) { string correctedSubjectId = expAndSit[0] + "@" + bodyAndBiome[0] + situation + sanitizedBiome; RnDSubject.id = correctedSubjectId; Dictionary <string, ScienceSubject> stockSubjects = Lib.ReflectionValue <Dictionary <string, ScienceSubject> >(ResearchAndDevelopment.Instance, "scienceSubjects"); if (stockSubjects.Remove(stockSubjectId) && !stockSubjects.ContainsKey(correctedSubjectId)) { stockSubjects.Add(correctedSubjectId, RnDSubject); } Lib.Log("RnD subject load : misformatted subject '" + stockSubjectId + "' was corrected to '" + correctedSubjectId + "'"); } if (subjectBody.BiomeMap.Attributes[i].name.Replace(" ", string.Empty).Equals(sanitizedBiome, StringComparison.OrdinalIgnoreCase)) { biomeIndex = i; break; } } } int bodyIndex = subjectBody.flightGlobalsIndex; Situation vesselSituation = new Situation(bodyIndex, scienceSituation, biomeIndex); // if the subject is a "doable" subject, we should have it in the DB. if (extraSituationInfo == string.Empty) { subjectData = GetSubjectData(expInfo, vesselSituation); } // else create the subjectdata. this can happen either because : // - it's a subject using the stock "extra id" system (asteroid samples) // - the subject was created in RnD prior to an experiment definition config change // - it was created by a mod that does things in a non-stock way (ex : DMOS anomaly scans uses the anomaly name as biomes) if (subjectData == null) { if (bodyAndBiome.Length == 2 && bodyAndBiome[1] != string.Empty && string.IsNullOrEmpty(extraSituationInfo)) { extraSituationInfo = bodyAndBiome[1]; } UnknownSubjectData unknownSubjectData = new UnknownSubjectData(expInfo, vesselSituation, stockSubjectId, RnDSubject, extraSituationInfo); subjectData = unknownSubjectData; unknownSubjectDatas.Add(stockSubjectId, unknownSubjectData); expBodiesSituationsBiomesSubject.AddSubject(subjectData.ExpInfo, bodyIndex, scienceSituation, biomeIndex, subjectData); } return(subjectData); }