/// <summary> /// Generates Parameters that can be passed into a method /// /// Memory must be Reset() between runs or else string will remain allocated in the heap /// </summary> /// <param name="values"></param> /// <returns></returns> protected List <ushort> GenerateParameters(object[] values) { var parameters = new List <ushort>(); foreach (var v in values) { switch (v) { case string @parameterString: { var stringParameterPointer = mbbsEmuMemoryCore.AllocateVariable(Guid.NewGuid().ToString(), (ushort)(@parameterString.Length + 1)); mbbsEmuMemoryCore.SetArray(stringParameterPointer, Encoding.ASCII.GetBytes(@parameterString)); parameters.Add(stringParameterPointer.Offset); parameters.Add(stringParameterPointer.Segment); break; } case uint @parameterULong: { var longBytes = BitConverter.GetBytes(@parameterULong); parameters.Add(BitConverter.ToUInt16(longBytes, 0)); parameters.Add(BitConverter.ToUInt16(longBytes, 2)); break; } case int @parameterLong: { var longBytes = BitConverter.GetBytes(@parameterLong); parameters.Add(BitConverter.ToUInt16(longBytes, 0)); parameters.Add(BitConverter.ToUInt16(longBytes, 2)); break; } case ushort @parameterUInt: parameters.Add(@parameterUInt); break; case short @parameterInt: parameters.Add((ushort)@parameterInt); break; } } return(parameters); }
/// <summary> /// Constructor for MbbsModule /// /// Pass in an empty/blank moduleIdentifier for a Unit Test/Fake Module /// </summary> /// <param name="logger"></param> /// <param name="moduleIdentifier">Will be null in a test</param> /// <param name="path"></param> /// <param name="memoryCore"></param> /// <param name="fileUtility"></param> public MbbsModule(IFileUtility fileUtility, IClock clock, ILogger logger, string moduleIdentifier, string path = "", MemoryCore memoryCore = null) { _fileUtility = fileUtility; _logger = logger; _clock = clock; ModuleIdentifier = moduleIdentifier; ModuleDlls = new List <MbbsDll>(); //Sanitize and setup Path if (string.IsNullOrEmpty(path)) { path = Directory.GetCurrentDirectory(); } if (!Path.EndsInDirectorySeparator(path)) { path += Path.DirectorySeparatorChar; } ModulePath = path; // will be null in tests if (string.IsNullOrEmpty(ModuleIdentifier)) { Mdf = MdfFile.createForTest(); ModuleDlls.Add(new MbbsDll(fileUtility, logger) { File = NEFile.createForTest() }); } else { //Verify MDF File Exists var mdfFile = fileUtility.FindFile(ModulePath, $"{ModuleIdentifier}.MDF"); var fullMdfFilePath = Path.Combine(ModulePath, mdfFile); if (!System.IO.File.Exists(fullMdfFilePath)) { throw new FileNotFoundException($"Unable to locate Module: {fullMdfFilePath}"); } Mdf = new MdfFile(fullMdfFilePath); var moduleDll = new MbbsDll(fileUtility, logger); moduleDll.Load(Mdf.DLLFiles[0].Trim(), ModulePath); ModuleDlls.Add(moduleDll); if (Mdf.Requires.Count > 0) { foreach (var r in Mdf.Requires) { var requiredDll = new MbbsDll(fileUtility, logger); if (requiredDll.Load(r.Trim(), ModulePath)) { requiredDll.SegmentOffset = (ushort)(ModuleDlls.Sum(x => x.File.SegmentTable.Count) + 1); ModuleDlls.Add(requiredDll); } } } if (Mdf.MSGFiles.Count > 0) { Msgs = new List <MsgFile>(Mdf.MSGFiles.Count); foreach (var m in Mdf.MSGFiles) { Msgs.Add(new MsgFile(ModulePath, m)); } } } //Set Initial Values RtkickRoutines = new PointerDictionary <RealTimeRoutine>(); RtihdlrRoutines = new PointerDictionary <RealTimeRoutine>(); TaskRoutines = new PointerDictionary <RealTimeRoutine>(); TextVariables = new Dictionary <string, FarPtr>(); GlobalCommandHandlers = new List <FarPtr>(); ExportedModuleDictionary = new Dictionary <ushort, IExportedModule>(6); ExecutionUnits = new Queue <ExecutionUnit>(2); Memory = memoryCore ?? new MemoryCore(); //Declare PSP Segment var psp = new PSPStruct { NextSegOffset = 0x9FFF, EnvSeg = 0xFFFF }; Memory.AddSegment(0x4000); Memory.SetArray(0x4000, 0, psp.Data); Memory.AllocateVariable("Int21h-PSP", sizeof(ushort)); Memory.SetWord("Int21h-PSP", 0x4000); //Find _INIT_ values if any foreach (var dll in ModuleDlls) { //If it's a Test, setup a fake _INIT_ if (string.IsNullOrEmpty(ModuleIdentifier)) { dll.EntryPoints["_INIT_"] = null; return; } //Setup _INIT_ Entrypoint FarPtr initEntryPointPointer; var initResidentName = dll.File.ResidentNameTable.FirstOrDefault(x => x.Name.StartsWith("_INIT__")); if (initResidentName == null) { //This only happens with MajorMUD -- I have no idea why it's a special little snowflake ¯\_(ツ)_/¯ _logger.Warn($"({moduleIdentifier}) Unable to locate _INIT_ in Resident Name Table, checking Non-Resident Name Table..."); var initNonResidentName = dll.File.NonResidentNameTable.FirstOrDefault(x => x.Name.StartsWith("_INIT__")); if (initNonResidentName == null) { throw new Exception("Unable to locate _INIT__ entry in Resident Name Table"); } var initEntryPoint = dll.File.EntryTable.First(x => x.Ordinal == initNonResidentName.IndexIntoEntryTable); initEntryPointPointer = new FarPtr((ushort)(initEntryPoint.SegmentNumber + dll.SegmentOffset), initEntryPoint.Offset); } else { var initEntryPoint = dll.File.EntryTable.First(x => x.Ordinal == initResidentName.IndexIntoEntryTable); initEntryPointPointer = new FarPtr((ushort)(initEntryPoint.SegmentNumber + dll.SegmentOffset), initEntryPoint.Offset); } _logger.Debug($"({ModuleIdentifier}) Located _INIT__: {initEntryPointPointer}"); dll.EntryPoints["_INIT_"] = initEntryPointPointer; } }
/// <summary> /// Constructor for MbbsModule /// /// Pass in an empty/blank moduleIdentifier for a Unit Test/Fake Module /// </summary> /// <param name="fileUtility"></param> /// <param name="clock"></param> /// <param name="logger"></param> /// <param name="moduleConfig"></param> /// <param name="memoryCore"></param> public MbbsModule(IFileUtility fileUtility, IClock clock, ILogger logger, ModuleConfiguration moduleConfig, ProtectedModeMemoryCore memoryCore = null) { _fileUtility = fileUtility; _logger = logger; _clock = clock; ModuleConfig = moduleConfig; ModuleIdentifier = moduleConfig.ModuleIdentifier; ModuleDlls = new List <MbbsDll>(); //Sanitize and setup Path if (string.IsNullOrEmpty(moduleConfig.ModulePath)) { moduleConfig.ModulePath = Directory.GetCurrentDirectory(); } if (!Path.EndsInDirectorySeparator(moduleConfig.ModulePath)) { moduleConfig.ModulePath += Path.DirectorySeparatorChar; } ModulePath = moduleConfig.ModulePath; // will be null in tests if (string.IsNullOrEmpty(ModuleIdentifier)) { Mdf = MdfFile.createForTest(); ModuleDlls.Add(new MbbsDll(fileUtility, logger) { File = NEFile.createForTest() }); } else { //Verify Module Path Exists if (!Directory.Exists(ModulePath)) { _logger.Error($"Unable to find the specified directory for the module {ModuleIdentifier.ToUpper()}: {ModulePath}"); _logger.Error("Please verify your Command Line Argument or the path specified in your Module JSON File and try again."); throw new DirectoryNotFoundException($"Unable to locate {ModulePath}"); } //Verify MDF File Exists var mdfFile = fileUtility.FindFile(ModulePath, $"{ModuleIdentifier}.MDF"); var fullMdfFilePath = Path.Combine(ModulePath, mdfFile); if (!File.Exists(fullMdfFilePath)) { _logger.Error($"Unable to locate {fullMdfFilePath}"); _logger.Error($"Please verify your Command Line Argument or the Module JSON File to ensure {ModuleIdentifier} is the correct Module Identifier and that the Module is installed properly."); throw new FileNotFoundException($"Unable to locate Module: {fullMdfFilePath}"); } Mdf = new MdfFile(fullMdfFilePath); LoadModuleDll(Mdf.DLLFiles[0].Trim()); if (Mdf.MSGFiles.Count > 0) { Msgs = new List <MsgFile>(Mdf.MSGFiles.Count); foreach (var m in Mdf.MSGFiles) { Msgs.Add(new MsgFile(_fileUtility, ModulePath, m)); } } } //Set Initial Values RtkickRoutines = new PointerDictionary <RealTimeRoutine>(); RtihdlrRoutines = new PointerDictionary <RealTimeRoutine>(); TaskRoutines = new PointerDictionary <RealTimeRoutine>(); GlobalCommandHandlers = new List <FarPtr>(); ExportedModuleDictionary = new Dictionary <ushort, IExportedModule>(6); ExecutionUnits = new Queue <ExecutionUnit>(2); Memory = memoryCore ?? new ProtectedModeMemoryCore(logger); ProtectedMemory = (ProtectedModeMemoryCore)Memory; //Declare PSP Segment var psp = new PSPStruct { NextSegOffset = 0x9FFF, EnvSeg = 0xFFFF }; ProtectedMemory.AddSegment(0x4000); Memory.SetArray(0x4000, 0, psp.Data); Memory.AllocateVariable("Int21h-PSP", sizeof(ushort)); Memory.SetWord("Int21h-PSP", 0x4000); //Find _INIT_ values if any foreach (var dll in ModuleDlls) { //If it's a Test, setup a fake _INIT_ if (string.IsNullOrEmpty(ModuleIdentifier)) { dll.EntryPoints["_INIT_"] = null; return; } //Setup _INIT_ Entrypoint FarPtr initEntryPointPointer; var initResidentName = dll.File.ResidentNameTable.FirstOrDefault(x => x.Name.StartsWith("_INIT__")); if (initResidentName == null) { //This only happens with MajorMUD -- I have no idea why it's a special little snowflake ¯\_(ツ)_/¯ _logger.Warn($"({moduleConfig.ModuleIdentifier}) Unable to locate _INIT_ in Resident Name Table, checking Non-Resident Name Table..."); var initNonResidentName = dll.File.NonResidentNameTable.FirstOrDefault(x => x.Name.StartsWith("_INIT__")); if (initNonResidentName == null) { _logger.Error($"Unable to locate _INIT__ entry in Resident Name Table for {dll.File.FileName}"); continue; } var initEntryPoint = dll.File.EntryTable.First(x => x.Ordinal == initNonResidentName.IndexIntoEntryTable); initEntryPointPointer = new FarPtr((ushort)(initEntryPoint.SegmentNumber + dll.SegmentOffset), initEntryPoint.Offset); } else { var initEntryPoint = dll.File.EntryTable.First(x => x.Ordinal == initResidentName.IndexIntoEntryTable); initEntryPointPointer = new FarPtr((ushort)(initEntryPoint.SegmentNumber + dll.SegmentOffset), initEntryPoint.Offset); } _logger.Debug($"({ModuleIdentifier}) Located _INIT__: {initEntryPointPointer}"); dll.EntryPoints["_INIT_"] = initEntryPointPointer; } }