├── .gitignore ├── ALGeoBuilder ├── ALGeoBuilder.csproj ├── AionLevelsProcessor.cs ├── App.config ├── Exceptions │ ├── AionException.cs │ └── PakFileFormatException.cs ├── NavMeshProcessor.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Utils │ ├── IAionStringComparer.cs │ └── ZipUtils.cs ├── WorldGeoFileBuilder.cs └── packages.config ├── AionClientViewer ├── AionClientViewer.csproj ├── App.config ├── ClientViewerSettings.cs ├── Controls │ ├── AionLevelViewerControl.cs │ └── ImageViewerControl.cs ├── Form1.Designer.cs ├── Form1.cs ├── Form1.resx ├── OpenFolder.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── Image1.png │ ├── Image2.png │ ├── Image3.png │ ├── Image4.png │ ├── Image5.png │ └── LevelImage.png └── packages.config ├── AionGeoViewer ├── AionGeoViewer.csproj ├── Game1.cs ├── Icon.ico ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── app.config └── packages.config ├── AionMonoLib ├── AionLevelViewerImpl.cs ├── AionMonoLib.csproj ├── Camera.cs ├── FontUtil.cs ├── GameInput.cs ├── Properties │ └── AssemblyInfo.cs ├── VertexBufferUtil.cs ├── VertexLoading │ ├── AstarVertexBufferGenerator.cs │ └── ContentLoader.cs ├── calibri10.xnb └── packages.config ├── Common ├── AionXmlStreamReader.cs ├── BBTree.cs ├── ByteHelpers.cs ├── Common.csproj ├── DirManager.cs ├── DoorLoader.cs ├── FileFormats │ ├── BinaryXml │ │ ├── BinaryXmlDecoder.cs │ │ ├── BinaryXmlFile.cs │ │ ├── BinaryXmlFileHelpers.cs │ │ ├── BinaryXmlNode.cs │ │ └── BinaryXmlStringTable.cs │ ├── BrushLstLoader.cs │ ├── CgfLoader.cs │ ├── Config │ │ └── EncryptedConfig.cs │ ├── EncryptedHtml │ │ └── EncryptedHtml.cs │ ├── H32Loader.cs │ ├── LevelDataXmlLoader.cs │ ├── ObjectsLstLoader.cs │ ├── Pak │ │ ├── PakCentralDirEnd.cs │ │ ├── PakCentralDirFile.cs │ │ ├── PakConstants.cs │ │ ├── PakFileEntry.cs │ │ ├── PakReader.cs │ │ └── PakUtil.cs │ └── WorldIdXmlLoader.cs ├── LevelLoadHelper.cs ├── Log.cs ├── Navigation │ ├── CompiledNavMesh.cs │ ├── GeoSpace.cs │ ├── NavMeshBuilder.cs │ ├── NavMeshCompiler.cs │ └── NavMeshUtil.cs ├── PfimDds.cs ├── PriorityQueue.cs ├── Properties │ └── AssemblyInfo.cs ├── Util.cs └── packages.config ├── README.md └── monono2.sln /.gitignore: -------------------------------------------------------------------------------- 1 | ALGeoBuilder/bin/* 2 | ALGeoBuilder/obj/* 3 | AionGeoViewer/bin/* 4 | AionGeoViewer/obj/* 5 | AionClientViewer/bin/* 6 | AionClientViewer/obj/* 7 | AionMonoLib/bin/* 8 | AionMonoLib/obj/* 9 | 10 | Common/bin/* 11 | Common/obj/* 12 | 13 | Packages/* 14 | .vs/* 15 | *.user 16 | -------------------------------------------------------------------------------- /ALGeoBuilder/ALGeoBuilder.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5AFE04CF-A395-4738-8EB5-97F41E8863F0} 8 | Exe 9 | monono2.ALGeoBuilder 10 | ALGeoBuilder 11 | v4.6.1 12 | 512 13 | true 14 | 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | x64 38 | true 39 | full 40 | false 41 | bin\x64\Debug\ 42 | DEBUG;TRACE 43 | prompt 44 | 4 45 | 46 | 47 | x64 48 | pdbonly 49 | true 50 | bin\x64\Release\ 51 | TRACE 52 | prompt 53 | 4 54 | 55 | 56 | 57 | ..\packages\DotNetZip.1.11.0\lib\net20\DotNetZip.dll 58 | 59 | 60 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Forms.DX.dll 61 | 62 | 63 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Framework.dll 64 | 65 | 66 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.dll 67 | 68 | 69 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.Direct3D11.dll 70 | 71 | 72 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.DXGI.dll 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | {7180eef5-dee0-452e-970a-a43ec1ecb8ef} 101 | Common 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /ALGeoBuilder/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ALGeoBuilder/Exceptions/AionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace monono2.ALGeoBuilder 4 | { 5 | class AionException : Exception 6 | { 7 | public AionException(string message) : base(message) { } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ALGeoBuilder/Exceptions/PakFileFormatException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace monono2.ALGeoBuilder 4 | { 5 | class PakFileFormatException : AionException 6 | { 7 | public PakFileFormatException(string message) : base(message) {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ALGeoBuilder/NavMeshProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Microsoft.Xna.Framework; 5 | using monono2.Common; 6 | using monono2.Common.Navigation; 7 | 8 | namespace monono2.ALGeoBuilder 9 | { 10 | public static class NavMeshProcessor 11 | { 12 | // Note: temp buffer makes this class not thread safe 13 | private static List s_tempVertices = new List(); 14 | 15 | // generates navmesh data for the selected levels. 16 | public static void GenerateAllNav(WorldIdXmlLoader worldIdXmlLoader, 17 | DirManager meshesDir, DirManager levelsDir, string levelId, 18 | bool skipExistingNav, string outputPath) 19 | { 20 | int curFolderIndex = -1; 21 | foreach (var pairs in worldIdXmlLoader.FolderNamesById) 22 | { 23 | curFolderIndex++; 24 | 25 | string clientLevelId = pairs.Key; 26 | string levelFolder = pairs.Value; 27 | 28 | if (!string.IsNullOrEmpty(levelId) && levelId != clientLevelId) 29 | { 30 | // skip excluded 31 | continue; 32 | } 33 | 34 | string outputNavFilename = Path.Combine(outputPath, clientLevelId + ".nav"); 35 | 36 | if (skipExistingNav && File.Exists(outputNavFilename)) 37 | { 38 | Console.WriteLine($" ** Skipping (already exists): {clientLevelId} - {levelFolder}"); 39 | continue; 40 | } 41 | 42 | 43 | Console.WriteLine($" Loading meshes for {clientLevelId} - {levelFolder}"); 44 | 45 | var LEVEL_TIME = DateTime.Now; 46 | var TIMER = DateTime.Now; 47 | var geoSpace = new GeoSpace(); 48 | 49 | // brushes 50 | var brushlst = LevelLoadHelper.CreateBrushLstLoader(levelsDir, 51 | Path.Combine(levelFolder, "brush.lst")); 52 | if (brushlst != null) 53 | { 54 | var cgfMap = LevelLoadHelper.CreateBrushLstCgfLoaderMap(meshesDir, brushlst); 55 | 56 | foreach (var brush in brushlst.brushEntries) 57 | { 58 | CgfLoader cgf; 59 | if (!cgfMap.TryGetValue(brush.meshIdx, out cgf)) 60 | continue; 61 | 62 | Matrix brushMatrix = LevelLoadHelper.GetBrushMatrix(brush); 63 | AddCgfToWorld(cgf, ref brushMatrix, brush.position, geoSpace); 64 | } 65 | } 66 | 67 | // objects 68 | var ctx = LevelLoadHelper.LoadObjectsLst(meshesDir, levelsDir, levelFolder); 69 | if (ctx != null) 70 | { 71 | foreach (var o in ctx.objects) 72 | { 73 | CgfLoader cgf; 74 | if (!ctx.cgfMap.TryGetValue(o.ObjectId, out cgf)) 75 | continue; 76 | 77 | var xform = LevelLoadHelper.GetObjectMatrix(o); 78 | AddCgfToWorld(cgf, ref xform, o.Position, geoSpace); 79 | } 80 | } 81 | 82 | // terrain 83 | bool loadedH32 = false; 84 | string h32path = Path.Combine(levelFolder, @"terrain\land_map.h32"); 85 | if (levelsDir.Exists(h32path)) { 86 | using (var landMapStream = levelsDir.OpenFile(h32path)) 87 | new H32Loader(landMapStream).LoadIntoGeoSpace(geoSpace); 88 | loadedH32 = true; 89 | } 90 | 91 | Console.WriteLine(" Data load time: " + (DateTime.Now - TIMER)); 92 | 93 | if (brushlst == null && ctx == null && !loadedH32) { 94 | Console.WriteLine(" ** Skipping (no level data found)"); 95 | continue; 96 | } 97 | 98 | // build geo 99 | TIMER = DateTime.Now; 100 | geoSpace.BuildTree(); 101 | geoSpace.Validate(); 102 | Console.WriteLine(" Geo build time: " + (DateTime.Now - TIMER)); 103 | 104 | // get size of level for scanning 105 | var bb = geoSpace.GetBoundingBox(); 106 | float startX = Math.Max(0, bb.Min.X); 107 | float endX = bb.Max.X; 108 | float startY = Math.Max(0, bb.Min.Y); 109 | float endY = bb.Max.Y; 110 | int top = (int)Math.Ceiling(bb.Max.Z); 111 | int bot = Math.Max(0, (int)bb.Min.Z); 112 | 113 | // TODO - print bounding box 114 | // TODO - save log file 115 | 116 | if (endX <= startX || endY <= startY || top < bot) 117 | throw new InvalidOperationException( 118 | $"unexpected level size for {clientLevelId} {levelFolder} bb: {bb}"); 119 | 120 | // compile mesh 121 | TIMER = DateTime.Now; 122 | float step = 1f; 123 | var navMeshBuilder = new NavMeshBuilder(startX, startY, bot, endX, endY, top, step); 124 | CompiledNavMeshSet compiledMeshSet = navMeshBuilder.ScanFloor(geoSpace); 125 | Console.WriteLine(" Raycast time: " + (DateTime.Now - TIMER)); 126 | 127 | // save to file 128 | using (var fs = File.OpenWrite(outputNavFilename)) 129 | compiledMeshSet.Save(fs); 130 | 131 | Console.WriteLine($" Level {clientLevelId} finished in {DateTime.Now - LEVEL_TIME}"); 132 | } 133 | } 134 | 135 | private static void AddCgfToWorld(CgfLoader cgf, ref Matrix xform, 136 | Vector3 position, GeoSpace geoSpace) 137 | { 138 | s_tempVertices.Clear(); 139 | 140 | // traverse nodes 141 | cgf.TraverseNodes((node, transform) => 142 | NodeHandler(cgf, node, position, transform), ref xform); 143 | 144 | // Add collected vertices as a new mesh. 145 | geoSpace.AddCollidableMeshToTree(s_tempVertices); 146 | } 147 | 148 | private static void NodeHandler(CgfLoader cgf, NodeData node, Vector3 worldPosition, 149 | Matrix transform) 150 | { 151 | if (!cgf.IsNodeCollidable(node)) 152 | return; 153 | 154 | foreach (var vi in node.Mesh.indices) 155 | { 156 | var v0 = Vector3.Transform(node.Mesh.vertices[vi.v0], transform); 157 | var v1 = Vector3.Transform(node.Mesh.vertices[vi.v1], transform); 158 | var v2 = Vector3.Transform(node.Mesh.vertices[vi.v2], transform); 159 | 160 | s_tempVertices.Add(v1); 161 | s_tempVertices.Add(v0); 162 | s_tempVertices.Add(v2); 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /ALGeoBuilder/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | using monono2.Common; 6 | 7 | namespace monono2.ALGeoBuilder 8 | { 9 | class Program 10 | { 11 | static void PrintUsage() 12 | { 13 | Log.WriteLine("Usage: ALGeoBuilder.exe [options] "); 14 | } 15 | 16 | static void Main(string[] args) 17 | { 18 | Log.Init(isConsole: true); 19 | 20 | var aionLevelProcessor = new AionLevelsProcessor(); 21 | 22 | try 23 | { 24 | for (int i = 0; i < args.Length - 1; i++) 25 | { 26 | if (args[i] == "-o") 27 | aionLevelProcessor.outputPath = args[++i]; 28 | else if (args[i] == "-version") 29 | aionLevelProcessor.aionClientVersion = int.Parse(args[++i]); 30 | else if (args[i] == "-lvl") 31 | aionLevelProcessor.levelId = args[++i]; 32 | else if (args[i] == "-nav") 33 | aionLevelProcessor.generateNav = true; 34 | else if (args[i] == "-skip_existing_nav") 35 | aionLevelProcessor.skipExistingNav = true; 36 | else if (args[i] == "-geo") 37 | aionLevelProcessor.generateGeo = true; 38 | else if (args[i] == "-doors") 39 | aionLevelProcessor.generateDoors = true; 40 | else if (args[i] == "-no_h32") 41 | aionLevelProcessor.noH32 = true; 42 | else if (args[i] == "-no_mesh") 43 | aionLevelProcessor.noMesh = true; 44 | else 45 | throw new InvalidOperationException("unknown arg: " + args[i]); 46 | } 47 | aionLevelProcessor.aionClientPath = args.Last(); 48 | if (!Directory.Exists(aionLevelProcessor.aionClientPath)) 49 | throw new InvalidOperationException("error: last argument aionClientPath does not exist"); 50 | } 51 | catch (Exception e) 52 | { 53 | Log.WriteLine(e); 54 | PrintUsage(); 55 | return; 56 | } 57 | 58 | // TODO Plugin(s) loading 59 | 60 | if (aionLevelProcessor.aionClientVersion != 1 && aionLevelProcessor.aionClientVersion != 2) 61 | { 62 | Log.WriteLine("Unsupported Aion version " + aionLevelProcessor.aionClientVersion); 63 | PrintUsage(); 64 | return; 65 | } 66 | 67 | try 68 | { 69 | Log.WriteLine("Start levels processing ..."); 70 | aionLevelProcessor.Process(); 71 | Log.WriteLine("Done."); 72 | } 73 | catch (Exception e) when (!Debugger.IsAttached) 74 | { 75 | Log.WriteLine(e); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ALGeoBuilder/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ALGeoBuilder")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ALGeoBuilder")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5afe04cf-a395-4738-8eb5-97f41e8863f0")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ALGeoBuilder/Utils/IAionStringComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace monono2.ALGeoBuilder.Utils 8 | { 9 | public interface IAionStringComparer 10 | { 11 | bool compare(string s1, string s2); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ALGeoBuilder/Utils/ZipUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | using Ionic.Zip; 9 | 10 | namespace monono2.ALGeoBuilder.Utils 11 | { 12 | public static class ZipUtils 13 | { 14 | public static void zipDirectory(string directory, string zipfile) 15 | { 16 | using (ZipOutputStream zos = new ZipOutputStream(zipfile)) 17 | { 18 | zip(directory, directory, zos); 19 | } 20 | } 21 | 22 | private static void zip(string directory, string basefile, ZipOutputStream zos) 23 | { 24 | var files = Directory.EnumerateFiles(directory); 25 | byte[] buffer = new byte[8192]; 26 | foreach (var file in files) { 27 | if (Directory.Exists(file)) { 28 | zip(file, basefile, zos); 29 | } else { 30 | using (var instream = File.OpenRead(file)) 31 | { 32 | zos.PutNextEntry(file.Substring(basefile.Length + 1)); 33 | while (true) 34 | { 35 | int read = instream.Read(buffer, 0, buffer.Length); 36 | if (read <= 0) 37 | break; 38 | zos.Write(buffer, 0, read); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | public static void unzip(string zipFile, string extractTo) 46 | { 47 | using (ZipFile archive = new ZipFile(zipFile)) 48 | { 49 | foreach (var entry in archive.Entries) 50 | { 51 | string file = Path.Combine(extractTo, entry.FileName); 52 | 53 | Directory.CreateDirectory(Path.GetDirectoryName(file)); 54 | if (!entry.IsDirectory || File.Exists(file)) 55 | { 56 | using (var instream = new BinaryReader(entry.InputStream)) 57 | using (var outstream = new BinaryWriter(File.Open(file, FileMode.Create))) 58 | { 59 | instream.BaseStream.CopyTo(outstream.BaseStream); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | public static void unzipEntry(string zipFile, string filter, Stream outputStream) 67 | { 68 | using (ZipFile archive = new ZipFile(zipFile)) 69 | { 70 | foreach (var entry in archive.Entries) 71 | { 72 | if (entry.IsDirectory) // search only for file 73 | continue; 74 | 75 | String entryName = entry.FileName; 76 | if (!Regex.IsMatch(entryName, filter)) 77 | continue; 78 | 79 | using (var instream = new BinaryReader(entry.InputStream)) 80 | using (var outstream = new BinaryWriter(outputStream)) 81 | instream.BaseStream.CopyTo(outstream.BaseStream); 82 | 83 | return; 84 | } 85 | throw new FileNotFoundException("There is no file in zip archive that match mask: " + filter); 86 | } 87 | } 88 | 89 | public static void unzipEntry(string zipFile, Dictionary filterStreamMap, IAionStringComparer comparer) 90 | { 91 | using (ZipFile archive = new ZipFile(zipFile)) 92 | { 93 | foreach (var entry in archive.Entries) 94 | { 95 | if (entry.IsDirectory) // search only for file 96 | continue; 97 | 98 | String entryName = entry.FileName; 99 | String filter = null; 100 | Stream outputStream = null; 101 | foreach (var filterStream in filterStreamMap) 102 | { 103 | if (comparer.compare(entryName, filterStream.Key)) 104 | { 105 | filter = filterStream.Key; 106 | outputStream = filterStream.Value; 107 | break; 108 | } 109 | } 110 | if (filter == null) 111 | continue; 112 | 113 | filterStreamMap.Remove(filter); 114 | 115 | 116 | using (var instream = new BinaryReader(entry.OpenReader())) 117 | using (var outstream = new BinaryWriter(outputStream)) 118 | { 119 | instream.BaseStream.CopyTo(outstream.BaseStream); 120 | } 121 | 122 | if (filterStreamMap.Count == 0) 123 | break; 124 | } 125 | 126 | if (filterStreamMap.Count != 0) 127 | throw new FileNotFoundException("There are no files in zip archive that match masks: " + string.Join(",", filterStreamMap.Keys)); 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /ALGeoBuilder/WorldGeoFileBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using Microsoft.Xna.Framework; 7 | using monono2.Common; 8 | 9 | namespace monono2.ALGeoBuilder 10 | { 11 | public class WorldGeoFileBuilder 12 | { 13 | public string ClientLevelId { get; private set; } 14 | byte[] m_landMapH32; 15 | BrushLstLoader m_brushLst; 16 | List m_vegetationCgfFilenames; 17 | List m_objectsLst; 18 | 19 | public WorldGeoFileBuilder(string clientLevelId, byte[] landMapH32, BrushLstLoader brushLst, 20 | List vegetationCgfFilenames, List objectsLst) 21 | { 22 | ClientLevelId = clientLevelId; 23 | m_landMapH32 = landMapH32; 24 | m_brushLst = brushLst; 25 | m_vegetationCgfFilenames = vegetationCgfFilenames; 26 | m_objectsLst = objectsLst; 27 | } 28 | 29 | public void CreateWorldGeoFile(string outputPath, bool noH32, HashSet loadedCgfs, HashSet emptyCgfs) 30 | { 31 | string geoFile = Path.Combine(outputPath, ClientLevelId + ".geo"); 32 | using (var geoDataStream = new BinaryWriter(File.Open(geoFile, FileMode.Create))) 33 | { 34 | bool h32DataCopied = false; 35 | if (!noH32 && m_landMapH32 != null) 36 | { 37 | // collect indexes to cutout/removed terrain quads. 38 | var cutoutIndexHash = new HashSet(); 39 | 40 | // mesh exists 41 | geoDataStream.Write((byte)1); 42 | // count of terrain data elements 43 | var origH32FileSize = m_landMapH32.Length; 44 | geoDataStream.Write((int)(origH32FileSize / 3)); 45 | 46 | // Convert terrain 47 | try 48 | { 49 | var convertedh32 = new byte[origH32FileSize / 3 * 2]; // store 2 bytes for every 3 50 | bool isEmpty = true; // ignore flat all-zero terrains such as the abyss 51 | 52 | int src = 0; 53 | int dst = 0; 54 | while (src < m_landMapH32.Length) 55 | { 56 | // short: z height * 32 57 | byte p1 = m_landMapH32[src++]; 58 | byte p2 = m_landMapH32[src++]; 59 | 60 | // extra terrain info. range 0-3F. 61 | // < 3F index to material in level data. 62 | // = 3F is terrain cutout (remove the quad). 63 | byte p3 = m_landMapH32[src++]; 64 | 65 | // collect cutout 66 | if (p3 == 0x3F) { 67 | cutoutIndexHash.Add(src / 3 - 1); 68 | } 69 | 70 | // short z 71 | convertedh32[dst++] = p1; 72 | convertedh32[dst++] = p2; 73 | 74 | if (isEmpty && (p1 != 0 || p2 != 0)) 75 | isEmpty = false; 76 | } 77 | 78 | if (!isEmpty) 79 | { 80 | foreach (var b in convertedh32) 81 | geoDataStream.Write(b); 82 | 83 | h32DataCopied = true; 84 | } 85 | else 86 | { 87 | Console.WriteLine("* skipping empty terrain"); 88 | } 89 | } 90 | catch (Exception e) 91 | { 92 | Console.WriteLine("ERROR: Cannot convert terrain.\nException: " + e); 93 | return; 94 | } 95 | 96 | // write sorted cutout data after the terrain heights. 97 | geoDataStream.Write((int) cutoutIndexHash.Count); 98 | if (cutoutIndexHash.Count > 0) 99 | { 100 | Console.WriteLine($"* terrain cutouts: {cutoutIndexHash.Count}"); 101 | foreach (int cutoutIndex in cutoutIndexHash.OrderBy(o => o)) 102 | { 103 | geoDataStream.Write((int)cutoutIndex); 104 | } 105 | } 106 | } 107 | 108 | if (!h32DataCopied) 109 | { 110 | // reset 111 | geoDataStream.BaseStream.Position = 0; 112 | geoDataStream.BaseStream.SetLength(0); 113 | 114 | geoDataStream.Write((byte)0); // mesh exists 115 | geoDataStream.Write((short)0); // stub 116 | geoDataStream.Write((int)0); // no cutout data 117 | } 118 | 119 | // save meshes info 120 | if (m_brushLst != null) // prison maps have no brush lst 121 | { 122 | foreach (BrushEntry brush in m_brushLst.brushEntries) 123 | { 124 | string meshFileName = m_brushLst.brushInfoList[brush.meshIdx].filename; 125 | 126 | // ignore not loaded or empty 127 | if (ShouldSkipMesh(meshFileName, loadedCgfs, emptyCgfs)) 128 | continue; 129 | 130 | byte[] meshFileNameBytes = Encoding.ASCII.GetBytes(meshFileName); 131 | 132 | // mesh file name size 133 | geoDataStream.Write((short)meshFileNameBytes.Count()); 134 | // file name 135 | geoDataStream.Write(meshFileNameBytes); 136 | 137 | // position 138 | geoDataStream.Write((float)brush.position.X); 139 | geoDataStream.Write((float)brush.position.Y); 140 | geoDataStream.Write((float)brush.position.Z); 141 | 142 | // transform 143 | geoDataStream.Write((float)(brush.rotationMatrix.M11)); 144 | geoDataStream.Write((float)(brush.rotationMatrix.M12)); 145 | geoDataStream.Write((float)(brush.rotationMatrix.M13)); 146 | geoDataStream.Write((float)(brush.rotationMatrix.M21)); 147 | geoDataStream.Write((float)(brush.rotationMatrix.M22)); 148 | geoDataStream.Write((float)(brush.rotationMatrix.M23)); 149 | geoDataStream.Write((float)(brush.rotationMatrix.M31)); 150 | geoDataStream.Write((float)(brush.rotationMatrix.M32)); 151 | geoDataStream.Write((float)(brush.rotationMatrix.M33)); 152 | 153 | // scale - no scale in brush.lst 154 | geoDataStream.Write((float)1.0f); 155 | 156 | // event type 0-4 157 | geoDataStream.Write((byte)brush.eventType); 158 | } 159 | } 160 | 161 | if (m_objectsLst != null) 162 | { 163 | // vegetation - continue outputting in the same format as brush entries 164 | foreach (ObjectsLstItem obj in m_objectsLst) 165 | { 166 | string meshFileName = m_vegetationCgfFilenames[obj.ObjectId]; 167 | 168 | // ignore not loaded or empty 169 | if (ShouldSkipMesh(meshFileName, loadedCgfs, emptyCgfs)) 170 | continue; 171 | 172 | byte[] meshFileNameBytes = Encoding.ASCII.GetBytes(meshFileName); 173 | 174 | // mesh file name size 175 | geoDataStream.Write((short)meshFileNameBytes.Count()); 176 | // file name 177 | geoDataStream.Write(meshFileNameBytes); 178 | 179 | // position 180 | geoDataStream.Write((float)obj.Position.X); 181 | geoDataStream.Write((float)obj.Position.Y); 182 | geoDataStream.Write((float)obj.Position.Z); 183 | 184 | // transform 185 | var rot = Matrix.CreateRotationZ(MathHelper.ToRadians(obj.Heading)); 186 | geoDataStream.Write((float)(rot.M11)); 187 | geoDataStream.Write((float)(rot.M12)); 188 | geoDataStream.Write((float)(rot.M13)); 189 | geoDataStream.Write((float)(rot.M21)); 190 | geoDataStream.Write((float)(rot.M22)); 191 | geoDataStream.Write((float)(rot.M23)); 192 | geoDataStream.Write((float)(rot.M31)); 193 | geoDataStream.Write((float)(rot.M32)); 194 | geoDataStream.Write((float)(rot.M33)); 195 | 196 | // scale 197 | geoDataStream.Write((float)obj.Scale); 198 | 199 | // event type, always 0 for veg 200 | geoDataStream.Write((byte)0); 201 | } 202 | } 203 | } 204 | } 205 | 206 | private bool ShouldSkipMesh(string meshFileName, HashSet loadedCgfs, HashSet emptyCgfs) 207 | { 208 | string normalizedFilename = Util.NormalizeMeshFilename(meshFileName); 209 | if (!loadedCgfs.Contains(normalizedFilename)) 210 | { 211 | Console.WriteLine("* Warning! cgf was not loaded: " + normalizedFilename); 212 | return true; 213 | } 214 | else if (emptyCgfs.Contains(normalizedFilename)) 215 | { 216 | return true; 217 | } 218 | return false; 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /ALGeoBuilder/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AionClientViewer/AionClientViewer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E01A9D4A-2C75-4E2E-BF5D-7C68BC88CFAB} 8 | WinExe 9 | monono2 10 | AionClientViewer 11 | v4.6.1 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | true 36 | bin\x64\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x64 40 | prompt 41 | MinimumRecommendedRules.ruleset 42 | true 43 | 44 | 45 | bin\x64\Release\ 46 | TRACE 47 | true 48 | pdbonly 49 | x64 50 | prompt 51 | MinimumRecommendedRules.ruleset 52 | true 53 | 54 | 55 | 56 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Forms.DX.dll 57 | 58 | 59 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Framework.dll 60 | 61 | 62 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.dll 63 | 64 | 65 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.Direct3D11.dll 66 | 67 | 68 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.DXGI.dll 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Component 86 | 87 | 88 | Component 89 | 90 | 91 | Form 92 | 93 | 94 | Form1.cs 95 | 96 | 97 | 98 | 99 | 100 | Form1.cs 101 | 102 | 103 | ResXFileCodeGenerator 104 | Resources.Designer.cs 105 | Designer 106 | 107 | 108 | True 109 | Resources.resx 110 | True 111 | 112 | 113 | 114 | SettingsSingleFileGenerator 115 | Settings.Designer.cs 116 | 117 | 118 | True 119 | Settings.settings 120 | True 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | {87049253-8E27-4A37-AFCC-7DBCD718CADC} 129 | AionMonoLib 130 | 131 | 132 | {7180EEF5-DEE0-452E-970A-A43EC1ECB8EF} 133 | Common 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /AionClientViewer/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /AionClientViewer/ClientViewerSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace monono2.AionClientViewer 5 | { 6 | public static class ClientViewerSettings 7 | { 8 | public static string GetClientDir() 9 | { 10 | if (!File.Exists(GetConfigFilename())) 11 | return null; 12 | var lines = File.ReadAllLines(GetConfigFilename()); 13 | if (lines.Length == 0) 14 | return null; 15 | if (!Directory.Exists(lines[0])) 16 | return null; 17 | return lines[0]; 18 | } 19 | public static void SetClientDir(string dir) 20 | { 21 | if (dir != "" && !Directory.Exists(dir)) 22 | return; 23 | Directory.CreateDirectory(Path.GetDirectoryName(GetConfigFilename())); 24 | File.WriteAllText(GetConfigFilename(), dir); 25 | } 26 | private static string GetConfigFilename() 27 | { 28 | string local = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 29 | return Path.Combine(local, "monono2", "aionclientviewer.cfg"); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /AionClientViewer/Controls/AionLevelViewerControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Xna.Framework; 4 | using MonoGame.Forms.Controls; 5 | using monono2.AionMonoLib; 6 | using monono2.Common; 7 | 8 | namespace monono2.AionClientViewer 9 | { 10 | public class AionLevelViewerControl : UpdateWindow 11 | { 12 | // HACK - public so it can be swapped out for fake resizing. 13 | public AionLevelViewerImpl m_game; 14 | 15 | // HACK - monogame crashes in Mouse.SetPosition() if no Game has been created, so create a dummy one. 16 | private Game hackmousefix = new Game(); 17 | 18 | private string m_aionClientRoot; 19 | private string m_levelFolder; 20 | 21 | private CgfLoader m_cgf; 22 | 23 | public AionLevelViewerControl(AionLevelViewerImpl game) 24 | { 25 | m_game = game; 26 | } 27 | 28 | // TODO configure better... load level OR cgf, not both 29 | public AionLevelViewerControl(string aionClientRoot, string levelFolder, CgfLoader cgf) 30 | { 31 | m_aionClientRoot = aionClientRoot; 32 | m_levelFolder = levelFolder; 33 | m_cgf = cgf; 34 | } 35 | 36 | protected override void Initialize() 37 | { 38 | if (m_game == null) 39 | { 40 | if (m_cgf != null) 41 | m_game = AionLevelViewerImpl.CreateCgfViewer(GraphicsDevice, Services, m_cgf); 42 | else 43 | m_game = AionLevelViewerImpl.CreateLevelViewer(GraphicsDevice, Services, m_aionClientRoot, m_levelFolder); 44 | } 45 | base.Initialize(); 46 | } 47 | 48 | protected override void OnClientSizeChanged(EventArgs e) 49 | { 50 | base.OnSizeChanged(e); 51 | } 52 | 53 | private int m_updateCount; 54 | public event EventHandler OnUpdateTitle; 55 | 56 | protected override void Update(GameTime gameTime) 57 | { 58 | m_game.HandleInput(Focused, gameTime); 59 | 60 | if (OnUpdateTitle != null && m_updateCount++ % 30 == 0) 61 | { 62 | OnUpdateTitle.Invoke(this, m_game.GetCurrentCameraPosition() 63 | + m_game.GetTestString()); 64 | } 65 | 66 | base.Update(gameTime); 67 | } 68 | 69 | 70 | protected override void Draw() 71 | { 72 | m_game.Draw(Editor.graphics); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /AionClientViewer/Controls/ImageViewerControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Microsoft.Xna.Framework; 8 | using Microsoft.Xna.Framework.Graphics; 9 | using MonoGame.Forms.Controls; 10 | using monono2.Common; 11 | using Pfim; 12 | 13 | namespace monono2.AionClientViewer 14 | { 15 | public class ImageViewerControl : UpdateWindow 16 | { 17 | private Texture2D m_image; 18 | private VertexBuffer m_buffer; 19 | private BasicEffect m_basicEffect; 20 | private Stream m_tempStream; 21 | 22 | private float m_lastScreenWidth; 23 | private float m_lastScreenHeight; 24 | private bool m_imageChanged; 25 | 26 | public void SetImage(Stream s) 27 | { 28 | if (Editor == null || Editor.graphics == null) 29 | { 30 | m_tempStream = new MemoryStream(); 31 | s.CopyTo(m_tempStream); 32 | m_tempStream.Seek(0, SeekOrigin.Begin); 33 | return; 34 | } 35 | SetImageFromStream(s); 36 | } 37 | 38 | private bool TryLoadFromKnownMonoImageFormats(Stream s, out Texture2D result) 39 | { 40 | try 41 | { 42 | result = Texture2D.FromStream(Editor.graphics, s); 43 | } 44 | catch (InvalidOperationException e) 45 | { 46 | result = null; 47 | } 48 | return (result != null); 49 | } 50 | 51 | private bool TryLoadDds(Stream s, out Texture2D result) 52 | { 53 | try 54 | { 55 | s.Seek(0, SeekOrigin.Begin); 56 | var img = Dds.Create(s, true); 57 | 58 | Texture2D tex = new Texture2D(Editor.graphics, img.Width, img.Height, false, SurfaceFormat.Color); 59 | if (img.Format == ImageFormat.Rgb24) 60 | { 61 | // convert from 24bit to 32... 62 | var convertedData = new byte[img.Width * img.Height * 4]; 63 | using (var br = new BinaryReader(new MemoryStream(img.Data))) 64 | using (var bw = new BinaryWriter(new MemoryStream(convertedData))) 65 | { 66 | for (int i = 0; i < convertedData.Length; i += 4) 67 | { 68 | byte r = br.ReadByte(); 69 | byte g = br.ReadByte(); 70 | byte b = br.ReadByte(); 71 | 72 | bw.Write(b); 73 | bw.Write(g); 74 | bw.Write(r); 75 | bw.Write((byte)255); 76 | } 77 | } 78 | 79 | tex.SetData(convertedData); 80 | } 81 | else if (img.Format == ImageFormat.Rgba32) 82 | { 83 | tex.SetData(img.Data); 84 | } 85 | else 86 | throw new InvalidOperationException("unsupported format: " + img.Format); 87 | 88 | result = tex; 89 | } 90 | catch (Exception e) { 91 | result = null; 92 | } 93 | return (result != null); 94 | } 95 | 96 | private void SetImageFromStream(Stream s) 97 | { 98 | Texture2D tex = null; 99 | 100 | // jpg, png, bmp 101 | TryLoadFromKnownMonoImageFormats(s, out tex); 102 | 103 | // dds 104 | if (tex == null) 105 | { 106 | s.Seek(0, SeekOrigin.Begin); 107 | if (TryLoadDds(s, out tex)) 108 | { 109 | m_image = tex; 110 | } 111 | } 112 | 113 | // TODO - pcx, tga 114 | 115 | if (tex == null) 116 | { 117 | // image format not supported... 118 | return; 119 | } 120 | 121 | m_image = tex; 122 | m_basicEffect.Texture = m_image; 123 | m_imageChanged = true; 124 | } 125 | 126 | protected override void Update(GameTime gameTime) 127 | { 128 | if (m_basicEffect == null) 129 | { 130 | m_basicEffect = new BasicEffect(Editor.graphics); 131 | m_basicEffect.View = Matrix.Identity; 132 | m_basicEffect.TextureEnabled = true; 133 | } 134 | 135 | if (m_lastScreenWidth != Editor.graphics.Viewport.Width || 136 | m_lastScreenHeight != Editor.graphics.Viewport.Height) 137 | { 138 | m_basicEffect.Projection = Matrix.CreateOrthographicOffCenter( 139 | 0, Editor.graphics.Viewport.Width, -Editor.graphics.Viewport.Height, 0, -1, 1); 140 | m_lastScreenWidth = Editor.graphics.Viewport.Width; 141 | m_lastScreenHeight = Editor.graphics.Viewport.Height; 142 | } 143 | 144 | if (m_tempStream != null) 145 | { 146 | SetImageFromStream(m_tempStream); 147 | m_tempStream.Dispose(); 148 | m_tempStream = null; 149 | } 150 | 151 | if (m_buffer == null) 152 | { 153 | m_buffer = new VertexBuffer(Editor.graphics, typeof(VertexPositionTexture), 6, BufferUsage.WriteOnly); 154 | } 155 | 156 | if (m_imageChanged) 157 | { 158 | float w = m_image.Width; 159 | float h = m_image.Height; 160 | m_buffer.SetData(new[] { 161 | new VertexPositionTexture(new Vector3(0, -h, 0), new Vector2(0, 1)), 162 | new VertexPositionTexture(new Vector3(0, 0, 0), new Vector2(0, 0)), 163 | new VertexPositionTexture(new Vector3(w, 0, 0), new Vector2(1, 0)), 164 | new VertexPositionTexture(new Vector3(0, -h, 0), new Vector2(0, 1)), 165 | new VertexPositionTexture(new Vector3(w, 0, 0), new Vector2(1, 0)), 166 | new VertexPositionTexture(new Vector3(w, -h, 0), new Vector2(1, 1)), 167 | }); 168 | m_imageChanged = false; 169 | } 170 | } 171 | 172 | protected override void Draw() 173 | { 174 | Editor.graphics.Clear(Color.Black); 175 | 176 | if (m_buffer == null || m_image == null) 177 | return; 178 | 179 | Editor.graphics.SetVertexBuffer(m_buffer); 180 | foreach (EffectPass pass in m_basicEffect.CurrentTechnique.Passes) 181 | { 182 | pass.Apply(); 183 | GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, m_buffer.VertexCount / 3); 184 | } 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /AionClientViewer/Form1.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace monono2.AionClientViewer 2 | { 3 | partial class Form1 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.SuspendLayout(); 32 | // 33 | // Form1 34 | // 35 | this.ClientSize = new System.Drawing.Size(1067, 705); 36 | this.Name = "Form1"; 37 | this.Text = "Aion Client Viewer"; 38 | this.ResumeLayout(false); 39 | 40 | } 41 | 42 | #endregion 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /AionClientViewer/Form1.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /AionClientViewer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | using monono2.Common; 7 | 8 | namespace monono2.AionClientViewer 9 | { 10 | static class Program 11 | { 12 | /// 13 | /// The main entry point for the application. 14 | /// 15 | [STAThread] 16 | static void Main() 17 | { 18 | Log.Init(false); 19 | Application.EnableVisualStyles(); 20 | Application.SetCompatibleTextRenderingDefault(false); 21 | Application.Run(new Form1()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /AionClientViewer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("AionGui")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("AionGui")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e01a9d4a-2c75-4e2e-bf5d-7c68bc88cfab")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /AionClientViewer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace monono2.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("monono2.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap DefaultImage { 67 | get { 68 | object obj = ResourceManager.GetObject("DefaultImage", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap DirImage { 77 | get { 78 | object obj = ResourceManager.GetObject("DirImage", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Bitmap. 85 | /// 86 | internal static System.Drawing.Bitmap ImageImage { 87 | get { 88 | object obj = ResourceManager.GetObject("ImageImage", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Bitmap. 95 | /// 96 | internal static System.Drawing.Bitmap LevelImage { 97 | get { 98 | object obj = ResourceManager.GetObject("LevelImage", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Looks up a localized resource of type System.Drawing.Bitmap. 105 | /// 106 | internal static System.Drawing.Bitmap PakImage { 107 | get { 108 | object obj = ResourceManager.GetObject("PakImage", resourceCulture); 109 | return ((System.Drawing.Bitmap)(obj)); 110 | } 111 | } 112 | 113 | /// 114 | /// Looks up a localized resource of type System.Drawing.Bitmap. 115 | /// 116 | internal static System.Drawing.Bitmap TextImage { 117 | get { 118 | object obj = ResourceManager.GetObject("TextImage", resourceCulture); 119 | return ((System.Drawing.Bitmap)(obj)); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /AionClientViewer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\Image3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\Image1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\Resources\Image5.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\LevelImage.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | 134 | ..\Resources\Image2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 135 | 136 | 137 | ..\Resources\Image4.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 138 | 139 | -------------------------------------------------------------------------------- /AionClientViewer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace monono2.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AionClientViewer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AionClientViewer/Resources/Image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzsort/monono2/25eabe693a02ad999ec7574155e1448611c0a04a/AionClientViewer/Resources/Image1.png -------------------------------------------------------------------------------- /AionClientViewer/Resources/Image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzsort/monono2/25eabe693a02ad999ec7574155e1448611c0a04a/AionClientViewer/Resources/Image2.png -------------------------------------------------------------------------------- /AionClientViewer/Resources/Image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzsort/monono2/25eabe693a02ad999ec7574155e1448611c0a04a/AionClientViewer/Resources/Image3.png -------------------------------------------------------------------------------- /AionClientViewer/Resources/Image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzsort/monono2/25eabe693a02ad999ec7574155e1448611c0a04a/AionClientViewer/Resources/Image4.png -------------------------------------------------------------------------------- /AionClientViewer/Resources/Image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzsort/monono2/25eabe693a02ad999ec7574155e1448611c0a04a/AionClientViewer/Resources/Image5.png -------------------------------------------------------------------------------- /AionClientViewer/Resources/LevelImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzsort/monono2/25eabe693a02ad999ec7574155e1448611c0a04a/AionClientViewer/Resources/LevelImage.png -------------------------------------------------------------------------------- /AionClientViewer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /AionGeoViewer/AionGeoViewer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {C8415F5C-A7FE-4658-A3E8-732B2ACE4F3B} 9 | WinExe 10 | Properties 11 | monono2 12 | monono2 13 | 512 14 | v4.6.1 15 | 16 | publish\ 17 | true 18 | Disk 19 | false 20 | Foreground 21 | 7 22 | Days 23 | false 24 | false 25 | true 26 | 0 27 | 1.0.0.%2a 28 | false 29 | false 30 | true 31 | 32 | 33 | 34 | 35 | x64 36 | true 37 | full 38 | false 39 | bin\Windows\Debug\ 40 | DEBUG;TRACE;WINDOWS 41 | prompt 42 | 4 43 | false 44 | 45 | 46 | x86 47 | pdbonly 48 | true 49 | bin\Windows\Release\ 50 | TRACE;WINDOWS 51 | prompt 52 | 4 53 | false 54 | 55 | 56 | Icon.ico 57 | 58 | 59 | true 60 | bin\x64\Debug\ 61 | DEBUG;TRACE;WINDOWS 62 | full 63 | x64 64 | prompt 65 | MinimumRecommendedRules.ruleset 66 | 67 | 68 | bin\x64\Release\ 69 | TRACE;WINDOWS 70 | true 71 | pdbonly 72 | x64 73 | prompt 74 | MinimumRecommendedRules.ruleset 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Forms.DX.dll 84 | 85 | 86 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Framework.dll 87 | 88 | 89 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.dll 90 | 91 | 92 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.Direct3D11.dll 93 | 94 | 95 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.DXGI.dll 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | False 107 | Microsoft .NET Framework 4.6.1 %28x86 and x64%29 108 | true 109 | 110 | 111 | False 112 | .NET Framework 3.5 SP1 113 | false 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | {87049253-8e27-4a37-afcc-7dbcd718cadc} 123 | AionMonoLib 124 | 125 | 126 | {7180eef5-dee0-452e-970a-a43ec1ecb8ef} 127 | Common 128 | 129 | 130 | 131 | 138 | -------------------------------------------------------------------------------- /AionGeoViewer/Game1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using monono2.AionMonoLib; 5 | 6 | namespace monono2 7 | { 8 | public class Game1 : Game 9 | { 10 | string m_gameDir; 11 | string m_levelFolder; 12 | GraphicsDeviceManager graphics; 13 | AionLevelViewerImpl content; 14 | 15 | public Game1(string gameDir, string levelFolder) 16 | : base() 17 | { 18 | m_gameDir = gameDir; 19 | m_levelFolder = levelFolder; 20 | 21 | graphics = new GraphicsDeviceManager(this); 22 | graphics.PreferredBackBufferWidth = 1600; 23 | graphics.PreferredBackBufferHeight = 1200; 24 | graphics.PreferMultiSampling = true; 25 | Window.AllowUserResizing = true; 26 | IsFixedTimeStep = false; // this fixes random mouse lag problems 27 | } 28 | 29 | /// 30 | /// Allows the game to perform any initialization it needs to before starting to run. 31 | /// This is where it can query for any required services and load any non-graphic 32 | /// related content. Calling base.Initialize will enumerate through any components 33 | /// and initialize them as well. 34 | /// 35 | protected override void Initialize() 36 | { 37 | base.IsMouseVisible = true; 38 | base.Initialize(); 39 | } 40 | 41 | /// 42 | /// LoadContent will be called once per game and is the place to load 43 | /// all of your content. 44 | /// 45 | protected override void LoadContent() 46 | { 47 | content = AionLevelViewerImpl.CreateLevelViewer(GraphicsDevice, Services, m_gameDir, m_levelFolder); 48 | content.SetProjection(GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight); 49 | 50 | content.OnChangeMouseVisibilityRequest += 51 | (sender, vis) => { IsMouseVisible = vis; }; 52 | content.OnExitRequest += 53 | (sender, e) => { Exit(); }; 54 | } 55 | 56 | /*private void InitTexture1() 57 | { 58 | var pixels = new Color[4 * 4]; 59 | for (int i = 0; i < 4 * 4; i += 4) 60 | { 61 | pixels[i] = new Color(255, 255, 255, 255); 62 | pixels[i + 1] = new Color(255, 0, 0, 255); 63 | pixels[i + 2] = new Color(0, 255, 0, 255); 64 | pixels[i + 3] = new Color(0, 0, 255, 255); 65 | } 66 | texture1 = new Texture2D(GraphicsDevice, 4, 4); 67 | texture1.SetData(pixels); 68 | }*/ 69 | 70 | /// 71 | /// UnloadContent will be called once per game and is the place to unload 72 | /// all content. 73 | /// 74 | protected override void UnloadContent() 75 | { 76 | } 77 | 78 | int m_updateCount = 0; 79 | /// 80 | /// Allows the game to run logic such as updating the world, 81 | /// checking for collisions, gathering input, and playing audio. 82 | /// 83 | /// Provides a snapshot of timing values. 84 | protected override void Update(GameTime gameTime) 85 | { 86 | m_updateCount++; 87 | content.HandleInput(IsActive, gameTime); 88 | if (m_updateCount % 30 == 0) 89 | base.Window.Title = content.GetCurrentCameraPosition() 90 | + content.GetTestString(); 91 | } 92 | 93 | /// 94 | /// This is called when the game should draw itself. 95 | /// 96 | /// Provides a snapshot of timing values. 97 | protected override void Draw(GameTime gameTime) 98 | { 99 | content.Draw(GraphicsDevice); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /AionGeoViewer/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzsort/monono2/25eabe693a02ad999ec7574155e1448611c0a04a/AionGeoViewer/Icon.ico -------------------------------------------------------------------------------- /AionGeoViewer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using monono2.Common; 7 | 8 | namespace monono2 9 | { 10 | #if WINDOWS || LINUX 11 | /// 12 | /// The main class. 13 | /// 14 | public static class Program 15 | { 16 | /// 17 | /// The main entry point for the application. 18 | /// 19 | [STAThread] 20 | static void Main(string[] args) 21 | { 22 | Log.Init(false); 23 | 24 | if (args.Length != 2 || !Directory.Exists(Path.Combine(args[0], args[1]))) 25 | { 26 | throw new InvalidOperationException("Usage: aiongeoviewer.exe (client dir) (level folder)"); 27 | } 28 | string gamedir = args[0]; 29 | string level = args[1]; 30 | 31 | using (var game = new Game1(gamedir, level)) 32 | game.Run(); 33 | } 34 | } 35 | #endif 36 | } 37 | -------------------------------------------------------------------------------- /AionGeoViewer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("monono2")] 9 | [assembly: AssemblyProduct("monono2")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("cb846009-3a35-48b0-88c4-eb5123831998")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /AionGeoViewer/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AionGeoViewer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /AionMonoLib/AionMonoLib.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {87049253-8E27-4A37-AFCC-7DBCD718CADC} 8 | Library 9 | Properties 10 | AionMonoLib 11 | AionMonoLib 12 | v4.6.1 13 | 512 14 | 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | true 36 | bin\x64\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x64 40 | prompt 41 | MinimumRecommendedRules.ruleset 42 | 43 | 44 | bin\x64\Release\ 45 | TRACE 46 | true 47 | pdbonly 48 | x64 49 | prompt 50 | MinimumRecommendedRules.ruleset 51 | 52 | 53 | 54 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Forms.DX.dll 55 | 56 | 57 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Framework.dll 58 | 59 | 60 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.dll 61 | 62 | 63 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.Direct3D11.dll 64 | 65 | 66 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.DXGI.dll 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | {7180EEF5-DEE0-452E-970A-A43EC1ECB8EF} 94 | Common 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /AionMonoLib/Camera.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using Microsoft.Xna.Framework; 4 | 5 | namespace monono2.AionMonoLib 6 | { 7 | public class Camera 8 | { 9 | private Vector3 m_cameraPosition; 10 | private float m_yaw; 11 | private float m_pitch; 12 | 13 | public float StepSpeed = 16.0f; 14 | public float UpDownSpeed = 10.0f; 15 | 16 | public Camera(Vector3 initialPosition = new Vector3(), int initialYawDegrees = 135, int initialPitchDegrees = 20) 17 | { 18 | m_cameraPosition = initialPosition; 19 | m_yaw = MathHelper.ToRadians(initialYawDegrees); 20 | m_pitch = MathHelper.ToRadians(initialPitchDegrees); 21 | } 22 | 23 | public void ApplyToView(ref Matrix view) 24 | { 25 | var lookAtVector = new Vector3(0, -1, -m_pitch); 26 | 27 | var yawMatrix = Matrix.CreateRotationZ(m_yaw); 28 | lookAtVector = Vector3.Transform(lookAtVector, yawMatrix); 29 | lookAtVector += m_cameraPosition; 30 | 31 | var upVector = Vector3.UnitZ; 32 | view = Matrix.CreateLookAt(m_cameraPosition, lookAtVector, upVector); 33 | } 34 | 35 | public void Forward(float stepMultipler) 36 | { 37 | var forwardVector = new Vector3(0, StepSpeed * -stepMultipler, 0); 38 | 39 | var rotationMatrix = Matrix.CreateRotationZ(m_yaw); 40 | forwardVector = Vector3.Transform(forwardVector, rotationMatrix); 41 | 42 | m_cameraPosition += forwardVector; 43 | } 44 | 45 | public void Right(float stepMultipler) 46 | { 47 | var forwardVector = new Vector3(0, StepSpeed * -stepMultipler, 0); 48 | 49 | var rotationMatrix = Matrix.CreateRotationZ(m_yaw + MathHelper.ToRadians(90)); 50 | forwardVector = Vector3.Transform(forwardVector, rotationMatrix); 51 | 52 | m_cameraPosition += forwardVector; 53 | } 54 | 55 | public void Turn(float rad) 56 | { 57 | m_yaw -= rad; 58 | 59 | while (m_yaw < 0) m_yaw += 2 * (float)Math.PI; 60 | while (m_yaw > (float)Math.PI) m_yaw -= 2 * (float)Math.PI; 61 | } 62 | 63 | public void Pitch(float rad) 64 | { 65 | m_pitch -= rad; 66 | 67 | while (m_pitch < 0) m_pitch += 2 * (float)Math.PI; 68 | while (m_pitch > (float)Math.PI) m_pitch -= 2 * (float)Math.PI; 69 | } 70 | 71 | public void MoveUp(float stepMultipler) 72 | { 73 | m_cameraPosition.Z += UpDownSpeed * stepMultipler; 74 | } 75 | 76 | public override string ToString() 77 | { 78 | float deg = MathHelper.ToDegrees(m_yaw); 79 | while (deg < 0) deg += 360; 80 | return $"Camera: {m_cameraPosition} Yaw: {deg} (H:{(int)(deg / 3)})"; 81 | } 82 | 83 | public Vector3 GetCameraPosition() 84 | { 85 | return m_cameraPosition; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /AionMonoLib/FontUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Microsoft.Xna.Framework.Content; 6 | using Microsoft.Xna.Framework.Graphics; 7 | 8 | namespace monono2.AionMonoLib 9 | { 10 | public static class FontUtil 11 | { 12 | private class CalibriLoader : ContentManager 13 | { 14 | public CalibriLoader(IServiceProvider serviceProvider) : base(serviceProvider) 15 | { 16 | } 17 | 18 | // intercept OpenStream and force it to read a specific embedded resource. 19 | protected override Stream OpenStream(string assetName) 20 | { 21 | return Assembly.GetExecutingAssembly().GetManifestResourceStream( 22 | Assembly.GetExecutingAssembly().GetManifestResourceNames() 23 | .Single(s => s.EndsWith(".calibri10.xnb"))); 24 | } 25 | 26 | public SpriteFont LoadFont() 27 | { 28 | return base.ReadAsset("calibri", null); 29 | } 30 | } 31 | 32 | public static SpriteFont LoadCalibriFont(IServiceProvider serviceProvider) 33 | { 34 | return new CalibriLoader(serviceProvider).LoadFont(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /AionMonoLib/GameInput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Input; 5 | 6 | namespace monono2.AionMonoLib 7 | { 8 | public class KeyStateWatcher 9 | { 10 | Keys m_key; 11 | bool s_down = false; 12 | public KeyStateWatcher(Keys key) 13 | { 14 | m_key = key; 15 | } 16 | 17 | public bool IsDown(ref KeyboardState kb) 18 | { 19 | if (!s_down && kb.IsKeyDown(m_key)) 20 | { 21 | s_down = true; 22 | return true; 23 | } 24 | else if (s_down && kb.IsKeyUp(m_key)) 25 | s_down = false; 26 | return false; 27 | } 28 | } 29 | 30 | public static class GameInput 31 | { 32 | static bool s_mouseDown = false; 33 | static int s_mouseStartX = 0, s_mouseStartY = 0; 34 | static KeyStateWatcher s_ksw1 = new KeyStateWatcher(Keys.D1); 35 | static KeyStateWatcher s_ksw2 = new KeyStateWatcher(Keys.D2); 36 | static KeyStateWatcher s_ksw3 = new KeyStateWatcher(Keys.D3); 37 | static KeyStateWatcher s_ksw4 = new KeyStateWatcher(Keys.D4); 38 | static KeyStateWatcher s_ksw5 = new KeyStateWatcher(Keys.D5); 39 | static KeyStateWatcher s_ksw6 = new KeyStateWatcher(Keys.D6); 40 | static KeyStateWatcher s_ksw7 = new KeyStateWatcher(Keys.D7); 41 | 42 | static KeyStateWatcher s_kswt = new KeyStateWatcher(Keys.T); 43 | static KeyStateWatcher s_kswy = new KeyStateWatcher(Keys.Y); 44 | static KeyStateWatcher s_kswm = new KeyStateWatcher(Keys.M); 45 | private static bool s_wasPreviouslyActive; 46 | 47 | public static void HandleInput(AionLevelViewerImpl game, bool isActive, GameTime gameTime, Camera camera, ref Matrix view) 48 | { 49 | if (!isActive) 50 | { 51 | s_wasPreviouslyActive = false; 52 | s_mouseDown = false; 53 | return; 54 | } 55 | 56 | var kb = Keyboard.GetState(); 57 | 58 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || kb.IsKeyDown(Keys.Escape)) 59 | game.InvokeOnExitRequest(); 60 | 61 | float speedMult = 0.04f; 62 | if (kb.IsKeyDown(Keys.LeftShift) || kb.IsKeyDown(Keys.RightShift)) 63 | speedMult = 0.7f; 64 | 65 | if (kb.IsKeyDown(Keys.W)) 66 | { 67 | camera.Forward(speedMult); 68 | camera.ApplyToView(ref view); 69 | } 70 | if (kb.IsKeyDown(Keys.S)) 71 | { 72 | camera.Forward(-speedMult); 73 | camera.ApplyToView(ref view); 74 | } 75 | if (kb.IsKeyDown(Keys.A)) 76 | { 77 | camera.Right(speedMult); 78 | camera.ApplyToView(ref view); 79 | } 80 | if (kb.IsKeyDown(Keys.D)) 81 | { 82 | camera.Right(-speedMult); 83 | camera.ApplyToView(ref view); 84 | } 85 | if (kb.IsKeyDown(Keys.Q)) 86 | { 87 | camera.Turn(-MathHelper.ToRadians(2.5f)); 88 | camera.ApplyToView(ref view); 89 | } 90 | if (kb.IsKeyDown(Keys.E)) 91 | { 92 | camera.Turn(MathHelper.ToRadians(2.5f)); 93 | camera.ApplyToView(ref view); 94 | } 95 | if (kb.IsKeyDown(Keys.R)) 96 | { 97 | camera.MoveUp(speedMult); 98 | camera.ApplyToView(ref view); 99 | } 100 | if (kb.IsKeyDown(Keys.F)) 101 | { 102 | camera.MoveUp(-speedMult); 103 | camera.ApplyToView(ref view); 104 | } 105 | 106 | // navmesh 107 | if (s_kswt.IsDown(ref kb)) 108 | { 109 | game.SetAstarTargetPoint(); 110 | } 111 | if (s_kswy.IsDown(ref kb)) 112 | { 113 | game.SetAstarStartPoint(); 114 | } 115 | if (s_kswm.IsDown(ref kb)) 116 | { 117 | game.DrawNavMeshUnderPosition(); 118 | } 119 | 120 | // toggles 121 | if (s_ksw1.IsDown(ref kb)) 122 | game.ShowTerrain ^= true; 123 | if (s_ksw2.IsDown(ref kb)) 124 | game.ShowMesh ^= true; 125 | if (s_ksw3.IsDown(ref kb)) 126 | game.ShowCollision ^= true; 127 | if (s_ksw4.IsDown(ref kb)) 128 | game.ShowOriginLines ^= true; 129 | if (s_ksw5.IsDown(ref kb)) 130 | game.ShowFloorLines ^= true; 131 | if (s_ksw6.IsDown(ref kb)) 132 | game.ShowNames ^= true; 133 | if (s_ksw7.IsDown(ref kb)) 134 | game.DoorMode = (game.DoorMode + 1) % 3; // hide doors, show state 1, show state 2 135 | 136 | if (s_wasPreviouslyActive) 137 | { 138 | var ms = Mouse.GetState(); 139 | if (ms.LeftButton == ButtonState.Pressed) 140 | { 141 | if (!s_mouseDown) 142 | { 143 | s_mouseDown = true; 144 | s_mouseStartX = ms.X; 145 | s_mouseStartY = ms.Y; 146 | 147 | game.InvokeOnChangeMouseVisibilityRequest(false); 148 | } 149 | else 150 | { 151 | int dx = s_mouseStartX - ms.X; 152 | int dy = s_mouseStartY - ms.Y; 153 | 154 | if (dx != 0 || dy != 0) 155 | { 156 | if (dx != 0) 157 | camera.Turn(MathHelper.ToRadians(-dx) / 4f); 158 | if (dy != 0) 159 | camera.Pitch(MathHelper.ToRadians(dy) / 4f); 160 | 161 | camera.ApplyToView(ref view); 162 | 163 | Mouse.SetPosition(s_mouseStartX, s_mouseStartY); 164 | } 165 | } 166 | } 167 | else if (s_mouseDown) 168 | { 169 | s_mouseDown = false; 170 | game.InvokeOnChangeMouseVisibilityRequest(true); 171 | } 172 | } 173 | s_wasPreviouslyActive = true; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /AionMonoLib/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("AionMonoLib")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("AionMonoLib")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("87049253-8e27-4a37-afcc-7dbcd718cadc")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /AionMonoLib/VertexBufferUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.Xna.Framework; 5 | using Microsoft.Xna.Framework.Graphics; 6 | 7 | namespace monono2.AionMonoLib 8 | { 9 | // http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series1/Terrain_lighting.php 10 | public struct VertexPositionColorNormal : IVertexType 11 | { 12 | public Vector3 Position; 13 | public Color Color; 14 | public Vector3 Normal; 15 | 16 | public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration( 17 | new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0), 18 | new VertexElement(sizeof(float) * 3, VertexElementFormat.Color, VertexElementUsage.Color, 0), 19 | new VertexElement(sizeof(float) * 3 + 4, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0)); 20 | 21 | public VertexPositionColorNormal(Vector3 pos, Color color, Vector3 normal) 22 | { 23 | Position = pos; 24 | Color = color; 25 | Normal = normal; 26 | } 27 | 28 | VertexDeclaration IVertexType.VertexDeclaration 29 | { 30 | get { return VertexDeclaration; } 31 | } 32 | } 33 | 34 | public static class VertexBufferUtil 35 | { 36 | // Splits vertices into multiple buffers to stay within the size limit. 37 | public static List CreateLargeVertexBuffer(GraphicsDevice GraphicsDevice, IEnumerable v) where T : struct 38 | { 39 | // VertexBuffer limit is 128MB: 40 | // https://docs.microsoft.com/en-us/windows/desktop/direct3d11/overviews-direct3d-11-resources-limits 41 | // Going over the limit causes the creation of the next VertexBuffer to fail with: 42 | // HRESULT: [0x887A0005], Module: [SharpDX.DXGI], ApiCode: [DXGI_ERROR_DEVICE_REMOVED/DeviceRemoved], Message: Unknown 43 | 44 | if (v == null || v.Count() == 0) 45 | return null; 46 | int maxBufferElements = (128 * 1024 * 1024) / System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); 47 | int maxTriangleVertices = maxBufferElements / 3 * 3; 48 | 49 | var result = new List(); 50 | 51 | int vCount = v.Count(); 52 | for (int i = 0; i < vCount; i += maxTriangleVertices) 53 | { 54 | int curSize = Math.Min(vCount - i, maxTriangleVertices); 55 | 56 | var array = v.Skip(i).Take(curSize).ToArray(); 57 | 58 | var vb = new VertexBuffer(GraphicsDevice, typeof(T), curSize, BufferUsage.WriteOnly); 59 | vb.SetData(array); 60 | 61 | result.Add(vb); 62 | } 63 | return result; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /AionMonoLib/VertexLoading/AstarVertexBufferGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using monono2.Common.Navigation; 6 | 7 | namespace monono2.AionMonoLib 8 | { 9 | public class AstarVertexBufferGenerator 10 | { 11 | private GraphicsDevice GraphicsDevice; 12 | private CompiledNavMeshSet m_compiledNavMeshSet; 13 | private Vector3 m_testNavTarget = new Vector3(1900.072f, 2591.614f, 264.4005f); 14 | private Vector3 m_testNavStart = new Vector3(1874.048f, 2618.841f, 289.2002f); 15 | 16 | public AstarVertexBufferGenerator(GraphicsDevice GraphicsDevice, CompiledNavMeshSet compiledNavMeshSet) 17 | { 18 | this.GraphicsDevice = GraphicsDevice; 19 | m_compiledNavMeshSet = compiledNavMeshSet; 20 | } 21 | 22 | public void SetTargetPoint(Vector3 target, RenderData renderData, ref string resultMessage) 23 | { 24 | if (m_compiledNavMeshSet == null) 25 | return; 26 | if (m_testNavTarget == target) 27 | return; 28 | m_testNavTarget = target; 29 | var sg = m_compiledNavMeshSet.FindSubgraphUnderPoint(target.X, target.Y, target.Z + 4, 10); 30 | 31 | // m_testString = $" TEST[pt: {c} {(sg==null?"MESH NOT FOUND":$"{sg.BlockWidth} {sg.BlockHeight} {sg.Z1} {sg.Z2}")}]"; 32 | renderData.astarLineVertexBuffer = CreateVertexBuffer(out resultMessage); 33 | } 34 | 35 | public void SetStartPoint(Vector3 target, RenderData renderData, ref string resultMessage) 36 | { 37 | if (m_compiledNavMeshSet == null) 38 | return; 39 | if (m_testNavStart == target) 40 | return; 41 | m_testNavStart = target; 42 | renderData.astarLineVertexBuffer = CreateVertexBuffer(out resultMessage); 43 | } 44 | 45 | private VertexBuffer CreateVertexBuffer(out string resultMessage) 46 | { 47 | resultMessage = ""; 48 | if (m_testNavTarget == Vector3.Zero || m_testNavStart == Vector3.Zero) 49 | return null; 50 | 51 | int maxFall = 30; 52 | var startMesh = m_compiledNavMeshSet.FindSubgraphUnderPoint(m_testNavStart.X, m_testNavStart.Y, m_testNavStart.Z, maxFall); 53 | var goalMesh = m_compiledNavMeshSet.FindSubgraphUnderPoint(m_testNavTarget.X, m_testNavTarget.Y, m_testNavTarget.Z, maxFall); 54 | 55 | if (startMesh == null || goalMesh == null) 56 | { 57 | resultMessage = $"start or goal is null: start:{startMesh} goal:{goalMesh}"; 58 | return null; 59 | } 60 | if (startMesh != goalMesh) 61 | { 62 | resultMessage = $"start and goal are different meshes"; 63 | return null; 64 | } 65 | 66 | var lines = TestAStar(startMesh, m_testNavStart, m_testNavTarget, maxFall); 67 | if (lines == null || lines.Count == 0) 68 | return null; 69 | 70 | var buffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), lines.Count, BufferUsage.WriteOnly); 71 | buffer.SetData(lines.ToArray()); 72 | return buffer; 73 | } 74 | 75 | private List TestAStar(CompiledNavMesh compiledMesh, Vector3 startVec, Vector3 endVec, int maxFall) 76 | { 77 | var startNode = compiledMesh.FindFloorUnderPoint(startVec.X, startVec.Y, startVec.Z + 2, maxFall); 78 | var goalNode = compiledMesh.FindFloorUnderPoint(endVec.X, endVec.Y, endVec.Z + 2, maxFall); 79 | if (startNode.blockIndex == goalNode.blockIndex) 80 | return null; 81 | 82 | var exactEnd = endVec; // TODO - caller should provide this 83 | var start = compiledMesh.WorldPointFromNode(startNode); 84 | var goal = compiledMesh.WorldPointFromNode(goalNode); 85 | 86 | var floorLineVertices = new List(); 87 | floorLineVertices.Add(new VertexPositionColor(start + new Vector3(0, 0, 5), Color.Lime)); 88 | floorLineVertices.Add(new VertexPositionColor(start, Color.Lime)); 89 | 90 | floorLineVertices.Add(new VertexPositionColor(goal + new Vector3(0, 0, 5), Color.Red)); 91 | floorLineVertices.Add(new VertexPositionColor(goal, Color.Red)); 92 | 93 | var path = compiledMesh.AStar(startNode, goalNode); 94 | var currentNode = goalNode; 95 | while (true) 96 | { 97 | if (!path.ContainsKey(currentNode)) 98 | throw new InvalidOperationException(); 99 | var next = path[currentNode]; // walks backwards from goal, so really 'prev' 100 | if (next.Equals(default(CompiledNavMesh.NodeId))) 101 | break; 102 | 103 | var ep0 = compiledMesh.WorldPointFromNode(currentNode); 104 | var ep1 = compiledMesh.WorldPointFromNode(next); 105 | 106 | floorLineVertices.Add(new VertexPositionColor(ep0 + new Vector3(0, 0, 0.1f), Color.White)); 107 | floorLineVertices.Add(new VertexPositionColor(ep1 + new Vector3(0, 0, 0.1f), Color.White)); 108 | 109 | currentNode = next; 110 | } 111 | 112 | // overlay the reduced path on top of original AStar results... 113 | 114 | // findPath runs AStar again but with path point reduction 115 | var pathPointList = compiledMesh.findPath(startNode, goalNode, exactEnd); 116 | 117 | for (int i = 1; i < pathPointList.Count; i++) 118 | { 119 | floorLineVertices.Add(new VertexPositionColor(pathPointList[i] + new Vector3(0, 0, 0.2f), Color.Blue)); 120 | floorLineVertices.Add(new VertexPositionColor(pathPointList[i - 1] + new Vector3(0, 0, 0.2f), Color.Blue)); 121 | } 122 | return floorLineVertices; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /AionMonoLib/calibri10.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzsort/monono2/25eabe693a02ad999ec7574155e1448611c0a04a/AionMonoLib/calibri10.xnb -------------------------------------------------------------------------------- /AionMonoLib/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Common/AionXmlStreamReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using monono2.Common.BinaryXml; 4 | 5 | namespace monono2.Common 6 | { 7 | // Stream wrapper that automatically decodes binary xml. 8 | public class AionXmlStreamReader : Stream 9 | { 10 | private Stream m_stream; 11 | 12 | public AionXmlStreamReader(Stream stream, bool includeBom) 13 | { 14 | try 15 | { 16 | bool isBinaryXml = stream.ReadByte() == 128; 17 | stream.Position = 0; 18 | 19 | if (isBinaryXml) 20 | { 21 | // decode and replace m_stream 22 | var xmlStream = BinaryXmlDecoder.Decode(new BinaryReader(stream), includeBom); 23 | stream.Close(); 24 | 25 | m_stream = xmlStream; 26 | } 27 | else 28 | { 29 | m_stream = stream; 30 | } 31 | } 32 | catch 33 | { 34 | stream.Close(); 35 | throw; 36 | } 37 | } 38 | 39 | public override bool CanRead => m_stream.CanRead; 40 | 41 | public override bool CanSeek => m_stream.CanSeek; 42 | 43 | public override bool CanWrite => m_stream.CanWrite; 44 | 45 | public override long Length => m_stream.Length; 46 | 47 | public override long Position { get => m_stream.Position; set => m_stream.Position = value; } 48 | 49 | public override void Flush() 50 | { 51 | m_stream.Flush(); 52 | } 53 | 54 | public override int Read(byte[] buffer, int offset, int count) 55 | { 56 | return m_stream.Read(buffer, offset, count); 57 | } 58 | 59 | public override long Seek(long offset, SeekOrigin origin) 60 | { 61 | return m_stream.Seek(offset, origin); 62 | } 63 | 64 | public override void SetLength(long value) 65 | { 66 | m_stream.SetLength(value); 67 | } 68 | 69 | public override void Write(byte[] buffer, int offset, int count) 70 | { 71 | m_stream.Write(buffer, offset, count); 72 | } 73 | 74 | protected override void Dispose(bool disposing) 75 | { 76 | if (m_stream != null) 77 | { 78 | m_stream.Close(); 79 | m_stream = null; 80 | } 81 | base.Dispose(disposing); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Common/BBTree.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using Microsoft.Xna.Framework; 6 | 7 | namespace monono2.Common 8 | { 9 | // bounding box tree. 10 | public class BBTree 11 | { 12 | private BBNode m_root = new BBNode(); 13 | 14 | private class BBNode 15 | { 16 | public BoundingBox m_bbox; 17 | public List m_children; 18 | public T m_value; 19 | 20 | public BBNode() 21 | { 22 | m_children = new List(); 23 | } 24 | 25 | public BBNode(BoundingBox bbox, T value) 26 | { 27 | m_bbox = bbox; 28 | m_value = value; 29 | } 30 | 31 | // EDAN30 Photorealistic Computer Graphics - Magnus Andersson 32 | // http://fileadmin.cs.lth.se/cs/Education/EDAN30/lectures/S2-bvh.pdf 33 | public void Split() 34 | { 35 | const int TARGET_LEAF_SIZE = 4; 36 | 37 | if (m_children == null || m_children.Count < TARGET_LEAF_SIZE) 38 | return; 39 | 40 | // get centers and sort on the largest axis 41 | var centers = m_children.Select(o => new Tuple(o, Util.GetBoundingBoxCenter(o.m_bbox))); 42 | 43 | var dimx = m_bbox.Max.X - m_bbox.Min.X; 44 | var dimy = m_bbox.Max.Y - m_bbox.Min.Y; 45 | var dimz = m_bbox.Max.Z - m_bbox.Min.Z; 46 | if (dimx >= dimy && dimx >= dimz) 47 | centers = centers.OrderBy(o => o.Item2.X); 48 | else if (dimy >= dimz) 49 | centers = centers.OrderBy(o => o.Item2.Y); 50 | else 51 | centers = centers.OrderBy(o => o.Item2.Z); 52 | 53 | var left = new BBNode(); 54 | var right = new BBNode(); 55 | int i = 0; 56 | int mid = m_children.Count / 2; 57 | foreach (var n in centers) 58 | { 59 | if (i++ < mid) 60 | left.m_children.Add(n.Item1); 61 | else 62 | right.m_children.Add(n.Item1); 63 | } 64 | 65 | left.UpdateBBox(); 66 | right.UpdateBBox(); 67 | m_children.Clear(); 68 | m_children.Add(left); 69 | m_children.Add(right); 70 | 71 | left.Split(); 72 | right.Split(); 73 | } 74 | 75 | private void UpdateBBox() 76 | { 77 | if (m_children.Count == 0) return; 78 | m_bbox = m_children[0].m_bbox; 79 | for (int i = 1; i < m_children.Count; i++) 80 | m_bbox = BoundingBox.CreateMerged(m_bbox, m_children[i].m_bbox); 81 | } 82 | 83 | // return true to keep processing, false to stop. 84 | public bool DoActionOnIntersectingMeshes(BoundingBox test, Func func) 85 | { 86 | foreach (var c in m_children) 87 | { 88 | if (c.m_bbox.Intersects(test)) 89 | { 90 | if (c.m_children != null) 91 | { 92 | if (!EqualityComparer.Default.Equals(m_value, default(T))) 93 | throw new InvalidOperationException("should not have both value and children"); 94 | 95 | if (!c.DoActionOnIntersectingMeshes(test, func)) 96 | return false; 97 | } 98 | else 99 | { 100 | if (!func(c.m_value)) 101 | return false; 102 | } 103 | } 104 | } 105 | return true; 106 | } 107 | 108 | public void DebugPrint(int depth) 109 | { 110 | Log.WriteLine("".PadRight(depth * 2) + m_bbox); 111 | if (!EqualityComparer.Default.Equals(m_value, default(T))) 112 | Log.WriteLine("".PadRight((depth + 1) * 2) + "Value: " + m_value); 113 | 114 | if (m_children != null) 115 | foreach (var c in m_children) 116 | c.DebugPrint(depth + 1); 117 | } 118 | 119 | public void Validate() 120 | { 121 | foreach (var node in m_children) 122 | { 123 | if (node.m_bbox.Min.X > node.m_bbox.Max.X || 124 | node.m_bbox.Min.Y > node.m_bbox.Max.Y || 125 | node.m_bbox.Min.Z > node.m_bbox.Max.Z) 126 | throw new InvalidOperationException(); 127 | 128 | if (m_bbox.Contains(node.m_bbox) != ContainmentType.Contains) 129 | throw new InvalidOperationException("child bbox not contained!"); 130 | 131 | if (node.m_children != null) 132 | node.Validate(); 133 | } 134 | } 135 | } 136 | 137 | public void Insert(BoundingBox bbox, T value) 138 | { 139 | if (m_root.m_children.Count == 0) 140 | m_root.m_bbox = bbox; 141 | else 142 | m_root.m_bbox = BoundingBox.CreateMerged(bbox, m_root.m_bbox); 143 | m_root.m_children.Add(new BBNode(bbox, value)); 144 | } 145 | 146 | public void DoActionOnIntersectingMeshes(BoundingBox test, Func func) 147 | { 148 | m_root.DoActionOnIntersectingMeshes(test, func); 149 | } 150 | 151 | public void DebugPrint() 152 | { 153 | m_root.DebugPrint(0); 154 | } 155 | 156 | public void Validate() 157 | { 158 | m_root.Validate(); 159 | } 160 | 161 | public void BuildTree() 162 | { 163 | m_root.Split(); 164 | } 165 | 166 | public BoundingBox GetBoundingBox() 167 | { 168 | return m_root.m_bbox; 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /Common/ByteHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace monono2.Common 5 | { 6 | public static class ByteHelpers 7 | { 8 | public static string ReadUTF16Z(byte[] data, int offset) 9 | { 10 | var sb = new StringBuilder(); 11 | for (int i = offset; data[i] != 0 || data[i + 1] != 0; i += 2) 12 | { 13 | sb.Append((char)(data[i] | data[i + 1] << 8)); 14 | } 15 | 16 | return sb.ToString(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Common/Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7180EEF5-DEE0-452E-970A-A43EC1ECB8EF} 8 | Library 9 | Properties 10 | monono2.Common 11 | monono2.Common 12 | v4.6.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | x64 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | DEBUG;TRACE 30 | prompt 31 | 4 32 | AnyCPU 33 | 34 | 35 | true 36 | bin\x64\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x64 40 | prompt 41 | MinimumRecommendedRules.ruleset 42 | 43 | 44 | bin\x64\Release\ 45 | DEBUG;TRACE 46 | true 47 | pdbonly 48 | x64 49 | prompt 50 | MinimumRecommendedRules.ruleset 51 | 52 | 53 | 54 | ..\packages\DotNetZip.1.11.0\lib\net20\DotNetZip.dll 55 | 56 | 57 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Forms.DX.dll 58 | 59 | 60 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\MonoGame.Framework.dll 61 | 62 | 63 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.dll 64 | 65 | 66 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.Direct3D11.dll 67 | 68 | 69 | ..\packages\MonoGame.Forms.DX.2.1.0.2\lib\net451\SharpDX.DXGI.dll 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /Common/DirManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using monono2.Common.FileFormats.Pak; 6 | 7 | namespace monono2.Common 8 | { 9 | public sealed class DirManager : IDisposable 10 | { 11 | private string m_rootPath; 12 | private SortedDictionary m_fileListing = new SortedDictionary(); 13 | public DirManager(string rootPath) 14 | { 15 | SetRootPath(rootPath); 16 | LoadAll(); 17 | } 18 | 19 | // load only subdirs under root. paths are still relative to rootPath. 20 | // subdirs must be physical/local dirs, not dirs inside pak. 21 | public DirManager(string rootPath, IEnumerable subdirs) 22 | { 23 | SetRootPath(rootPath); 24 | foreach (var dir in subdirs) 25 | LoadSubDir(dir); 26 | } 27 | 28 | private void SetRootPath(string rootPath) 29 | { 30 | m_rootPath = rootPath.ToLower(); 31 | if (!Directory.Exists(m_rootPath)) 32 | throw new DirectoryNotFoundException(); 33 | } 34 | 35 | public void DebugPrintFileListing(string matchPrefix = null, 36 | string matchAny = null, string matchSuffix = null) 37 | { 38 | foreach (var kvp in m_fileListing) 39 | { 40 | if (!string.IsNullOrEmpty(matchPrefix) && !kvp.Key.StartsWith(matchPrefix, StringComparison.InvariantCultureIgnoreCase)) 41 | continue; 42 | if (!string.IsNullOrEmpty(matchAny) && kvp.Key.IndexOf(matchAny, StringComparison.InvariantCultureIgnoreCase) == -1) 43 | continue; 44 | if (!string.IsNullOrEmpty(matchSuffix) && !kvp.Key.EndsWith(matchSuffix, StringComparison.InvariantCultureIgnoreCase)) 45 | continue; 46 | Log.Write(kvp.Value == null ? "LOCAL " : " PAK "); 47 | Log.WriteLine(kvp.Key); 48 | } 49 | } 50 | 51 | public bool Exists(string path) 52 | { 53 | return m_fileListing.ContainsKey(NormalizePath(path)); 54 | } 55 | 56 | public Stream OpenFile(string relativePath) 57 | { 58 | var path = NormalizePath(relativePath); 59 | var pr = m_fileListing[path]; 60 | if (pr == null) 61 | { 62 | // local file 63 | return File.OpenRead(GetFullLocalPath(relativePath)); 64 | } 65 | 66 | return new MemoryStream(pr.GetFile(MakePathRelativeToPak(path, pr))); 67 | } 68 | 69 | private string MakePathRelativeToPak(string relativePath, PakReader pr) 70 | { 71 | string relativeToPak = Path.GetDirectoryName(pr.OriginalPakPath).Substring(m_rootPath.Length); 72 | return relativePath.Substring(relativeToPak.Length).TrimStart(new[] { '\\', '/' }); 73 | } 74 | 75 | // Load all subdirectories. 76 | // Do not use with LoadSubDir. 77 | private void LoadAll() 78 | { 79 | GenerateFileListing(""); 80 | } 81 | 82 | // Load a specific subdirectory under the root. 83 | // Do not load the same directory twice! 84 | private void LoadSubDir(string subdir) 85 | { 86 | GenerateFileListing(subdir); 87 | } 88 | 89 | // Rules: 90 | // - Filenames are lowercase and use backslash. 91 | // - Filenames include directories and are relative to the root path. 92 | // - .pak files are excluded from the listing since they get expanded. 93 | // - Files contained in .pak files take precedence over local files. 94 | // - The filemap maps relative paths to a PakReader. If PakReader is null, path is a local file. 95 | private SortedDictionary GenerateFileListing(string subpath) 96 | { 97 | var paksToLoad = new List(); 98 | 99 | // load pak names and local files 100 | foreach (var path in Directory.EnumerateFiles(Path.Combine(m_rootPath, subpath), "*", SearchOption.AllDirectories)) 101 | { 102 | if (Path.GetExtension(path).Equals(".pak", StringComparison.OrdinalIgnoreCase)) 103 | { 104 | paksToLoad.Add(path); 105 | continue; 106 | } 107 | 108 | m_fileListing.Add(GetRelativePath(path), null); 109 | } 110 | 111 | // load filenames from pak, and map each to a PakReader. 112 | foreach (var pak in paksToLoad) 113 | { 114 | var pr = new PakReader(pak); 115 | foreach (var file in pr.Files.Keys) 116 | { 117 | string relativePath = GetRelativePath(Path.Combine(Path.GetDirectoryName(pak), file)); 118 | PakReader existing; 119 | if (m_fileListing.TryGetValue(relativePath, out existing)) 120 | { 121 | if (existing == null) 122 | { 123 | m_fileListing[relativePath] = pr; 124 | } 125 | // TODO aion gamedata has colliding names... (especially in levels/common) 126 | // how should that be handled? could check if models are the same. just ignoring for now... 127 | //else 128 | //Log.WriteLine("duplicate filename encountered: " + relativePath); 129 | //throw new InvalidOperationException("duplicate filename encountered: " + relativePath); 130 | } 131 | else 132 | { 133 | m_fileListing.Add(relativePath, pr); 134 | } 135 | } 136 | } 137 | 138 | return m_fileListing; 139 | } 140 | 141 | private string NormalizePath(string path) 142 | { 143 | return path.ToLower().Replace('/', '\\'); 144 | } 145 | 146 | private string GetRelativePath(string path) 147 | { 148 | path = NormalizePath(path); 149 | if (!path.StartsWith(m_rootPath)) 150 | throw new InvalidOperationException("root path doesn't match filename!"); 151 | return path.Substring(m_rootPath.Length).TrimStart(new[] { '\\' }); 152 | } 153 | 154 | private string GetFullLocalPath(string relativePath) 155 | { 156 | return Path.Combine(m_rootPath, relativePath); 157 | } 158 | 159 | // DirManager must be kept open while reading files. 160 | public void Close() 161 | { 162 | if (m_fileListing != null) 163 | { 164 | foreach (var pr in m_fileListing.Values.Distinct().Where(o => o != null)) 165 | pr.Close(); 166 | m_fileListing.Clear(); 167 | m_fileListing = null; 168 | } 169 | } 170 | 171 | public void Dispose() 172 | { 173 | Close(); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /Common/DoorLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Xml.Linq; 5 | using Microsoft.Xna.Framework; 6 | 7 | namespace monono2.Common 8 | { 9 | public class DoorInfo 10 | { 11 | public int EntityId; 12 | public string Name; 13 | public Vector3 Pos; 14 | public Vector3 Angles; // rotation 15 | public string object_AnimatedModel; // cga filename 16 | public bool bClickable; 17 | public bool bCloseable; 18 | public bool bOneWay; 19 | public bool bOpened; 20 | 21 | public Matrix GetMatrix() 22 | { 23 | return Matrix.CreateFromYawPitchRoll( 24 | MathHelper.ToRadians(Angles.X), 25 | MathHelper.ToRadians(Angles.Y), 26 | MathHelper.ToRadians(Angles.Z)) 27 | * Matrix.CreateTranslation(Pos); 28 | } 29 | } 30 | 31 | public static class DoorLoader 32 | { 33 | // if levelFolder is empty, levelDir points to a single level directory. 34 | // otherwise, levelDir points to the levels\ directory, and levelFolder chooses a specific level directory. 35 | public static List LoadDoorInfosForLevel(DirManager levelDir, string levelFolder = "") 36 | { 37 | var result = new List(); 38 | using (var stream = new AionXmlStreamReader(levelDir.OpenFile(Path.Combine(levelFolder, "mission_mission0.xml")), false)) 39 | { 40 | var xdoc = XDocument.Load(stream); 41 | foreach (var e in xdoc.Root.Element("Objects").Elements("Entity")) 42 | { 43 | if (e.Attribute("EntityClass").Value != "Door") 44 | { 45 | continue; 46 | } 47 | 48 | var doorInfo = new DoorInfo(); 49 | doorInfo.EntityId = int.Parse(e.Attribute("EntityId").Value); 50 | doorInfo.Name = e.Attribute("Name").Value; 51 | 52 | // TODO "dir" is a value in degrees which matches "angles"... 53 | // do these always represent the same rotation? check broken door models... add check... 54 | // TODO are x and y in angles always zero? 55 | doorInfo.Angles = e.Attribute("Angles") != null ? Util.ParseVector(e.Attribute("Angles").Value) : Vector3.Zero; 56 | doorInfo.Pos = Util.ParseVector(e.Attribute("Pos").Value); 57 | 58 | var properties = e.Element("Properties"); 59 | doorInfo.object_AnimatedModel = properties.Attribute("object_AnimatedModel").Value; 60 | 61 | var server = properties.Element("Server"); 62 | doorInfo.bClickable = server.Attribute("bClickable").Value == "1"; 63 | doorInfo.bCloseable = server.Attribute("bCloseable").Value == "1"; 64 | doorInfo.bOneWay = server.Attribute("bOneWay").Value == "1"; 65 | doorInfo.bOpened = server.Attribute("bOpened").Value == "1"; 66 | 67 | result.Add(doorInfo); 68 | } 69 | } 70 | return result; 71 | } 72 | 73 | public static void ValidateDoorModels(DirManager meshesDir, string cgaPath) 74 | { 75 | // check assumptions: 76 | // TODO - file should exist 77 | // TODO - should have .cga extension 78 | // TODO - check for existence of .anm or .cal or .caf files? 79 | // TODO - cga should be valid 80 | // TODO - check if any scale or rotation controllers have non default values. 81 | // TODO - check position controllers, assume default values, or count = 2 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Common/FileFormats/BinaryXml/BinaryXmlDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Xml; 5 | 6 | namespace monono2.Common.BinaryXml 7 | { 8 | /* 9 | * Code was converted from Aion Exporter 10 | * 11 | * XML generation: http://www.genedavis.com/library/xml/java_dom_xml_creation.jsp 12 | */ 13 | 14 | public class BinaryXmlDecoder 15 | { 16 | private static void WriteNode(XmlWriter xw, BinaryXmlNode node) 17 | { 18 | xw.WriteStartElement(node.Name); 19 | foreach (var kvp in node.Attributes) 20 | xw.WriteAttributeString(kvp.Key, kvp.Value); 21 | 22 | foreach (BinaryXmlNode node1 in node.Children) 23 | WriteNode(xw, node1); 24 | 25 | if (node.Value != null) 26 | xw.WriteValue(node.Value); 27 | xw.WriteEndElement(); 28 | } 29 | 30 | public static Stream Decode(BinaryReader input, bool includeBom) 31 | { 32 | var binaryXmlFile = new BinaryXmlFile(); 33 | binaryXmlFile.Read(input); 34 | 35 | // Creating an empty XML Document 36 | //DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); 37 | //DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); 38 | //Document doc = docBuilder.newDocument(); 39 | 40 | var settings = new XmlWriterSettings(); 41 | settings.Encoding = new UnicodeEncoding(false, includeBom); 42 | settings.NewLineHandling = NewLineHandling.Entitize; 43 | settings.Indent = true; 44 | 45 | // use a MemoryStream instead of string to preserve encoding. 46 | var ms = new MemoryStream(); 47 | using (var xw = XmlWriter.Create(ms, settings)) 48 | { 49 | xw.WriteStartDocument(); 50 | WriteNode(xw, binaryXmlFile.Root); 51 | xw.WriteEndDocument(); 52 | xw.Close(); 53 | } 54 | 55 | // Output the XML 56 | ms.Position = 0; 57 | return ms; 58 | 59 | //// set up a transformer 60 | //TransformerFactory transfac = TransformerFactory.newInstance(); 61 | //Transformer trans = transfac.newTransformer(); 62 | //trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 63 | //trans.setOutputProperty(OutputKeys.INDENT, "yes"); 64 | //trans.setOutputProperty(OutputKeys.ENCODING, "Unicode"); 65 | 66 | //// write XML tree to the stream 67 | //StreamResult result = new StreamResult(output); 68 | //DOMSource source = new DOMSource(doc); 69 | //trans.transform(source, result); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Common/FileFormats/BinaryXml/BinaryXmlFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace monono2.Common.BinaryXml 6 | { 7 | public class BinaryXmlFile 8 | { 9 | public BinaryXmlNode Root; 10 | 11 | public void Read(BinaryReader input) 12 | { 13 | if (input.ReadByte() != 128) 14 | throw new Exception("not a binary XML file"); 15 | BinaryXmlStringTable table = new BinaryXmlStringTable(); 16 | table.Read(input); 17 | Root = new BinaryXmlNode(); 18 | Root.Read(input, table); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Common/FileFormats/BinaryXml/BinaryXmlFileHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace monono2.Common.BinaryXml 5 | { 6 | public class BinaryXmlFileHelpers 7 | { 8 | public static int ReadPackedS32(BinaryReader stream) 9 | { 10 | int num1 = stream.ReadByte(); 11 | int num2 = 0; 12 | int num3 = 0; 13 | for (; num1 >= 128; num1 = stream.ReadByte()) 14 | { 15 | num2 |= (num1 & 0x7F) << num3; 16 | num3 += 7; 17 | } 18 | return num2 | (num1 << num3); 19 | } 20 | 21 | public static String ReadTable(byte[] data, int offset) 22 | { 23 | if (offset == 0) 24 | return ""; 25 | 26 | return ByteHelpers.ReadUTF16Z(data, 2 * offset); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Common/FileFormats/BinaryXml/BinaryXmlNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace monono2.Common.BinaryXml 6 | { 7 | public class BinaryXmlNode 8 | { 9 | public string Name; 10 | public string Value; 11 | public Dictionary Attributes; 12 | public List Children; 13 | 14 | public void Read(BinaryReader input, BinaryXmlStringTable table) 15 | { 16 | Name = table.getData(BinaryXmlFileHelpers.ReadPackedS32(input)); 17 | // Log.Write("<" + Name); 18 | Attributes = new Dictionary(); 19 | Children = new List(); 20 | Value = null; 21 | int num1 = input.ReadByte(); 22 | if ((num1 & 1) == 1) 23 | { 24 | var offset = BinaryXmlFileHelpers.ReadPackedS32(input); 25 | Value = table.getData(offset); 26 | } 27 | if ((num1 & 2) == 2) 28 | { 29 | int attributeCount = BinaryXmlFileHelpers.ReadPackedS32(input); 30 | for (int index = 0; index < attributeCount; ++index) 31 | { 32 | int keyTableOffset = BinaryXmlFileHelpers.ReadPackedS32(input); 33 | int valueTableOffset = BinaryXmlFileHelpers.ReadPackedS32(input); 34 | 35 | var k = table.getData(keyTableOffset); 36 | var v = table.getData(valueTableOffset); 37 | // Log.WriteLine($" \"{k}\"=\"{v}\""); 38 | Attributes[k] = v; 39 | } 40 | } 41 | 42 | //Log.WriteLine(">"); 43 | //if (Value != null) 44 | // Log.WriteLine(Value); 45 | 46 | if ((num1 & 4) == 4) // has child nodes 47 | { 48 | int num3 = BinaryXmlFileHelpers.ReadPackedS32(input); 49 | for (int index = 0; index < num3; ++index) 50 | { 51 | BinaryXmlNode binaryXmlNode = new BinaryXmlNode(); 52 | binaryXmlNode.Read(input, table); 53 | Children.Add(binaryXmlNode); 54 | } 55 | } 56 | //Log.WriteLine(""); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Common/FileFormats/BinaryXml/BinaryXmlStringTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace monono2.Common.BinaryXml 5 | { 6 | public class BinaryXmlStringTable 7 | { 8 | private byte[] m_data; 9 | 10 | public String getData(int index) 11 | { 12 | if (index == 0) 13 | return ""; 14 | 15 | return ByteHelpers.ReadUTF16Z(m_data, index * 2); 16 | } 17 | 18 | public void Read(BinaryReader input) 19 | { 20 | int count = BinaryXmlFileHelpers.ReadPackedS32(input); 21 | m_data = input.ReadBytes(count); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Common/FileFormats/BrushLstLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Text; 6 | using Microsoft.Xna.Framework; 7 | 8 | namespace monono2.Common 9 | { 10 | public class BrushInfo 11 | { 12 | public int brushInfoIndex; 13 | public string filename; 14 | public BoundingBox bbox; 15 | } 16 | 17 | public class BrushEntry 18 | { 19 | public int entryIdx; 20 | public int meshIdx; // maps to brushInfoIndex 21 | public Vector3 position; 22 | public Matrix rotationMatrix; 23 | public byte eventType; 24 | } 25 | 26 | public class BrushLstLoader 27 | { 28 | public List brushInfoList = new List(); 29 | public List brushEntries = new List(); 30 | public bool[] m_eventUsage = new bool[5]; 31 | 32 | public BrushLstLoader(string path) 33 | { 34 | using (var meshInputStream = new BinaryReader(File.Open(path, FileMode.Open))) 35 | { 36 | Load(meshInputStream); 37 | } 38 | } 39 | 40 | public BrushLstLoader(Stream s) 41 | { 42 | Load(new BinaryReader(s)); 43 | } 44 | 45 | private void Load(BinaryReader meshInputStream) 46 | { 47 | byte[] signature = new byte[3]; 48 | meshInputStream.Read(signature, 0, 3); 49 | if (signature[0] != 0x43 || signature[1] != 0x52 || signature[2] != 0x59) // CRY 50 | throw new IOException("Wrong signature"); 51 | 52 | int dw1 = meshInputStream.ReadInt32(); 53 | 54 | int meshDataBlockSz = meshInputStream.ReadInt32(); 55 | 56 | if (meshDataBlockSz < 16 || meshDataBlockSz > 19) 57 | { 58 | throw new InvalidOperationException("unexpected block size"); 59 | } 60 | 61 | int titlesCount = meshInputStream.ReadInt32(); 62 | for (int i = 0; i < titlesCount; i++) 63 | { 64 | int nameLen = meshInputStream.ReadInt32(); 65 | byte[] nameBytes = new byte[nameLen - 4]; 66 | meshInputStream.Read(nameBytes, 0, nameLen - 4); 67 | // TODO Use these names somehow 68 | } 69 | 70 | // meshes info 71 | int meshInfoCount = meshInputStream.ReadInt32(); 72 | byte[] fileNameBytes = new byte[128]; 73 | for (int i = 0; i < meshInfoCount; i++) 74 | { 75 | var info = new BrushInfo(); 76 | info.brushInfoIndex = i; 77 | 78 | meshInputStream.ReadInt32(); // skip 79 | 80 | // int dw1 = meshInputStream.readInt(); 81 | 82 | meshInputStream.Read(fileNameBytes, 0, 128); 83 | info.filename = Encoding.UTF8.GetString(fileNameBytes).Trim().Trim('\0').ToLower().Replace('\\', '/'); 84 | 85 | meshInputStream.ReadInt32(); // skip - usually 1, sometimes 3 86 | 87 | float x1 = meshInputStream.ReadSingle(); 88 | float y1 = meshInputStream.ReadSingle(); 89 | float z1 = meshInputStream.ReadSingle(); 90 | float x2 = meshInputStream.ReadSingle(); 91 | float y2 = meshInputStream.ReadSingle(); 92 | float z2 = meshInputStream.ReadSingle(); 93 | info.bbox = new BoundingBox(new Vector3(x1, y1, z1), new Vector3(x2, y2, z2)); 94 | brushInfoList.Add(info); 95 | } 96 | 97 | // meshes data 98 | int meshDataCount = meshInputStream.ReadInt32(); 99 | for (int i = 0; i < meshDataCount; i++) 100 | { 101 | var meshData = new BrushEntry(); 102 | meshData.entryIdx = i; 103 | 104 | meshInputStream.ReadInt32();//skip 105 | meshInputStream.ReadInt32();//skip 106 | 107 | meshData.meshIdx = meshInputStream.ReadInt32(); 108 | if (meshData.meshIdx < 0 || meshData.meshIdx >= brushInfoList.Count) 109 | throw new IndexOutOfRangeException(); 110 | 111 | meshInputStream.ReadInt32();//skip 112 | meshInputStream.ReadInt32();//skip 113 | meshInputStream.ReadInt32();//skip 114 | 115 | 116 | // read 3x4 matrix 117 | float[] matrix = new float[3*4]; 118 | for (int j = 0; j < 3*4; j++) 119 | matrix[j] = meshInputStream.ReadSingle(); 120 | 121 | // position 122 | var posx = matrix[0 * 4 + 3]; 123 | var posy = matrix[1 * 4 + 3]; 124 | var posz = matrix[2 * 4 + 3]; 125 | meshData.position = new Vector3(posx, posy, posz); 126 | 127 | // orientation matrix 128 | var m = new Matrix(); 129 | m.M11 = matrix[0 * 4 + 0]; 130 | m.M12 = matrix[0 * 4 + 1]; 131 | m.M13 = matrix[0 * 4 + 2]; 132 | //m.M14 = matrix[0 * 4 + 3]; 133 | m.M21 = matrix[1 * 4 + 0]; 134 | m.M22 = matrix[1 * 4 + 1]; 135 | m.M23 = matrix[1 * 4 + 2]; 136 | //m.M24 = matrix[1 * 4 + 3]; 137 | m.M31 = matrix[2 * 4 + 0]; 138 | m.M32 = matrix[2 * 4 + 1]; 139 | m.M33 = matrix[2 * 4 + 2]; 140 | //m.M34 = matrix[2 * 4 + 3]; 141 | m.M44 = 1; 142 | 143 | meshData.rotationMatrix = m; 144 | 145 | meshInputStream.ReadByte(); // 100, or some sewing objects are 200 ? 146 | meshInputStream.ReadByte(); // maybe an angle? 147 | meshInputStream.ReadByte(); // unknown 148 | meshInputStream.ReadByte(); // unknown 149 | int b = meshInputStream.ReadInt32(); // unknown - maybe shorts 150 | meshInputStream.ReadInt32(); // unknown - maybe shorts 151 | meshInputStream.ReadInt32(); // 0 - unknown 152 | 153 | 154 | // Server Event Decorations 155 | // 00 = no decoration/also means normal usage of event service 156 | // 01 = christmas 157 | // 02 = halloween 158 | // 03 = braxcafe 159 | // 04 = valentine 160 | // 08 = oversea maid event 161 | int eventType = meshInputStream.ReadInt32(); 162 | if (eventType < 0 || eventType > 4) 163 | { 164 | Log.WriteLine($"Ignoring unknown event: {eventType} - {brushInfoList[meshData.meshIdx].filename}"); 165 | //throw new InvalidOperationException("invalid event type: " + eventType); 166 | } 167 | else 168 | { 169 | meshData.eventType = (byte)eventType; 170 | m_eventUsage[eventType] = true; 171 | } 172 | 173 | // //if (brushInfoList[meshData.meshIdx].filename.IndexOf("halloween", StringComparison.OrdinalIgnoreCase) >= 0) 174 | // if (eventType == 2) 175 | // Debug.WriteLine(eventType + " " + brushInfoList[meshData.meshIdx].filename); 176 | 177 | meshInputStream.ReadInt32(); // always 3? 178 | 179 | meshInputStream.ReadBytes(4 * (meshDataBlockSz - 16)); 180 | 181 | //if (b != 0) 182 | // Debug.WriteLine($"meshIdx:{meshData.meshIdx} value:{b} {brushInfoList[meshData.meshIdx].filename}"); 183 | 184 | //DEBUG if (eventType == 1) 185 | brushEntries.Add(meshData); 186 | } 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Common/FileFormats/Config/EncryptedConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace monono2.Common.FileFormats.Config 6 | { 7 | public static class EncryptedConfig 8 | { 9 | // decodes the system.cfg file. 10 | public static string DecryptConfigFile(string path) 11 | { 12 | var b = File.ReadAllBytes(path); 13 | for (int i = 0; i < b.Length; i++) 14 | { 15 | if (b[i] >= 0x80) 16 | b[i] ^= 0xFF; 17 | } 18 | return Encoding.ASCII.GetString(b); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Common/FileFormats/EncryptedHtml/EncryptedHtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace monono2.Common.EncryptedHtml 8 | { 9 | public static class EncryptedHtml 10 | { 11 | public static byte[] Decode(string originalFilename, Stream input) 12 | { 13 | var b = DecodeInternal(originalFilename, input); 14 | return b.Skip(1).ToArray(); 15 | } 16 | 17 | public static string DecodeToString(string originalFilename, Stream input) 18 | { 19 | var b = DecodeInternal(originalFilename, input); 20 | 21 | // TODO - currently only knows utf16le... 22 | if (b[1] != 0xFF || b[2] != 0xFE) 23 | throw new InvalidOperationException("unexpected BOM"); 24 | 25 | return Encoding.Unicode.GetString(b, 3, b.Length - 3); 26 | } 27 | 28 | private static byte[] DecodeInternal(string originalFilename, Stream input) 29 | { 30 | if (input.ReadByte() != 0x81) 31 | throw new InvalidOperationException("invalid header"); 32 | 33 | using (var ms = new MemoryStream()) 34 | { 35 | input.CopyTo(ms); 36 | var b = ms.ToArray(); 37 | 38 | b[0] = 0; 39 | 40 | int magic = 0; 41 | int magic2 = 0; 42 | 43 | var filenameWithoutExt = Path.GetFileNameWithoutExtension(originalFilename); 44 | for (int i = 0; i < filenameWithoutExt.Length; i++) 45 | { 46 | var tmp = (filenameWithoutExt[i] & 0xF) + i; 47 | magic += tmp; 48 | magic2 ^= tmp; 49 | } 50 | 51 | for (int i = 0; i < b.Length; i++) 52 | { 53 | magic += 0x1D; 54 | magic ^= magic2; 55 | magic2 += 3; 56 | b[i] ^= (byte)magic; 57 | } 58 | 59 | //if (b[0] != 0x81) 60 | // throw new InvalidOperationException("unexpected decode result"); 61 | 62 | return b; 63 | } 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Common/FileFormats/H32Loader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Microsoft.Xna.Framework; 6 | using monono2.Common.Navigation; 7 | 8 | namespace monono2.Common 9 | { 10 | public class H32Loader 11 | { 12 | public List vertices; 13 | public int width; 14 | public bool isEmpty = true; 15 | private HashSet cutoutIndexes = new HashSet(); 16 | 17 | public H32Loader(string path) 18 | { 19 | using (var fs = File.OpenRead(path)) 20 | { 21 | Load(fs); 22 | } 23 | } 24 | 25 | public H32Loader(Stream stream) 26 | { 27 | Load(stream); 28 | } 29 | 30 | private void Load(Stream stream) 31 | { 32 | using (var br = new BinaryReader(stream)) 33 | { 34 | width = (int)Math.Sqrt(br.BaseStream.Length / 3); 35 | vertices = new List(width * width); 36 | 37 | int x = 0; 38 | int y = 0; 39 | while (br.BaseStream.Position < br.BaseStream.Length) 40 | { 41 | int p1 = br.ReadUInt16(); 42 | byte mat = br.ReadByte(); 43 | if (mat == 0x3F) 44 | { 45 | cutoutIndexes.Add((int) (br.BaseStream.Position / 3 - 1)); 46 | } 47 | 48 | // Detect terrains which have all zero heights, such as the abyss. 49 | // AL allows a single nonzero value to set a constant height, but I don't believe any maps use this. 50 | if (isEmpty && p1 != 0) 51 | isEmpty = false; 52 | 53 | float z = p1 / 32f; 54 | 55 | vertices.Add(new Vector3(y * 2, x *2, z)); 56 | 57 | if (++x == width) 58 | { 59 | x = 0; 60 | y++; 61 | } 62 | } 63 | } 64 | } 65 | 66 | public Vector3 VertexLookup(int x, int y) 67 | { 68 | return vertices[y * width + x]; 69 | } 70 | 71 | public bool IsCutout(int x, int y) 72 | { 73 | return cutoutIndexes.Contains(y * width + x); 74 | } 75 | 76 | // load terrain vertices for collision testing 77 | public void LoadIntoGeoSpace(GeoSpace geoSpace) 78 | { 79 | if (isEmpty) 80 | return; 81 | 82 | // Rules: 83 | // - subdivide X & Y to create smaller AABBs for geospace. 84 | // - remove steep slopes. 85 | 86 | const int subdivide = 8; 87 | 88 | var meshTriangleVertices = new List(); 89 | 90 | for (int sectorY = 0; sectorY < width - 1; sectorY += subdivide) 91 | { 92 | for (int sectorX = 0; sectorX < width - 1; sectorX += subdivide) 93 | { 94 | meshTriangleVertices.Clear(); 95 | 96 | // can reduce to 2 triangles only if all vertices are the same height and none are removed. 97 | // TODO - use a real poly reduction algorithm instead of fixed size. 98 | bool optimizationAllowed = true; 99 | 100 | int endX = Math.Min(width - 1, sectorX + subdivide); 101 | int endY = Math.Min(width - 1, sectorY + subdivide); 102 | for (int y = sectorY; y < endY; y++) 103 | { 104 | for (int x = sectorX; x < endX; x++) 105 | { 106 | var p1 = VertexLookup(x, y); 107 | var p2 = VertexLookup(x, y + 1); 108 | var p3 = VertexLookup(x + 1, y); 109 | var p4 = VertexLookup(x + 1, y + 1); 110 | 111 | // TODO - replace with a correct slope test. 112 | // TODO - test if any maps have unit size other than 2. 113 | if (Math.Abs(p1.Z - p2.Z) >= 2 || Math.Abs(p1.Z - p3.Z) >= 2) 114 | { 115 | optimizationAllowed = false; 116 | continue; 117 | } 118 | 119 | meshTriangleVertices.Add(p1); 120 | meshTriangleVertices.Add(p3); 121 | meshTriangleVertices.Add(p2); 122 | 123 | meshTriangleVertices.Add(p2); 124 | meshTriangleVertices.Add(p4); 125 | meshTriangleVertices.Add(p3); 126 | } 127 | } 128 | 129 | if (meshTriangleVertices.Count == 0) 130 | continue; 131 | 132 | if (optimizationAllowed) 133 | { 134 | // if all points have the same Z, replace with 2 triangles. 135 | if (meshTriangleVertices.All(v => v.Z == meshTriangleVertices[0].Z)) 136 | { 137 | // don't optimize if there is a mix of 3F and non-3F mats, as this would break cutouts. 138 | // a mix would be rare, so for simplicity, just don't optimize when 3F is found. 139 | bool anyMat3F = false; 140 | for (int y = sectorY; y < endY; y++) 141 | { 142 | for (int x = sectorX; x < endX; x++) 143 | { 144 | if (IsCutout(x, y)) { 145 | anyMat3F = true; 146 | goto exitMatCheck; 147 | } 148 | } 149 | } 150 | exitMatCheck: 151 | 152 | 153 | if (!anyMat3F) 154 | { 155 | var p1 = VertexLookup(sectorX, sectorY); 156 | var p2 = VertexLookup(sectorX, endY); 157 | var p3 = VertexLookup(endX, sectorY); 158 | var p4 = VertexLookup(endX, endY); 159 | 160 | if (p1.Z != meshTriangleVertices[0].Z || 161 | p2.Z != meshTriangleVertices[0].Z || 162 | p3.Z != meshTriangleVertices[0].Z || 163 | p4.Z != meshTriangleVertices[0].Z) 164 | throw new InvalidOperationException("Z mismatch"); 165 | 166 | meshTriangleVertices.Clear(); 167 | 168 | meshTriangleVertices.Add(p1); 169 | meshTriangleVertices.Add(p3); 170 | meshTriangleVertices.Add(p2); 171 | 172 | meshTriangleVertices.Add(p2); 173 | meshTriangleVertices.Add(p4); 174 | meshTriangleVertices.Add(p3); 175 | } 176 | } 177 | } 178 | 179 | geoSpace.AddCollidableMeshToTree(meshTriangleVertices); 180 | } 181 | } 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Common/FileFormats/LevelDataXmlLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Xml.Linq; 7 | using Microsoft.Xna.Framework; 8 | 9 | namespace monono2.Common 10 | { 11 | public class LevelDataXmlLoader 12 | { 13 | public List VegetationCgfFilenames { get; private set; } 14 | public float WaterLevel { get; private set; } 15 | public Point MapWidthAndHeight { get; private set; } 16 | 17 | public LevelDataXmlLoader(string filename) 18 | { 19 | using (var fs = File.OpenRead(filename)) 20 | { 21 | Load(fs); 22 | } 23 | } 24 | 25 | public LevelDataXmlLoader(Stream levelDataXml) 26 | { 27 | Load(levelDataXml); 28 | } 29 | 30 | private void Load(Stream levelDataXml) 31 | { 32 | var xdoc = XDocument.Load(levelDataXml); 33 | LoadVegetationCgfFilenames(xdoc); 34 | LoadWaterLevel(xdoc); 35 | LoadMapWidthAndHeight(xdoc); 36 | } 37 | 38 | private void LoadVegetationCgfFilenames(XDocument xdoc) 39 | { 40 | VegetationCgfFilenames = new List(); 41 | foreach (var e in xdoc.Root.Element("Vegetation").Elements()) 42 | { 43 | VegetationCgfFilenames.Add(e.Attribute("FileName").Value); 44 | } 45 | } 46 | 47 | private void LoadWaterLevel(XDocument xdoc) 48 | { 49 | WaterLevel = float.Parse(xdoc.Root.Element("LevelInfo").Attribute("WaterLevel").Value, CultureInfo.InvariantCulture); 50 | } 51 | 52 | private void LoadMapWidthAndHeight(XDocument xdoc) 53 | { 54 | int w = int.Parse(xdoc.Root.Element("LevelInfo").Attribute("HeightmapXSize").Value); 55 | int h = int.Parse(xdoc.Root.Element("LevelInfo").Attribute("HeightmapYSize").Value); 56 | MapWidthAndHeight = new Point(w, h); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Common/FileFormats/ObjectsLstLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Microsoft.Xna.Framework; 5 | 6 | namespace monono2.Common 7 | { 8 | public class ObjectsLstItem 9 | { 10 | public Vector3 Position; 11 | public int ObjectId; 12 | public float Scale; 13 | public int Heading; 14 | } 15 | 16 | public class ObjectsLstLoader 17 | { 18 | public static List Load(string filename, int mapWidth, int mapHeight) 19 | { 20 | using (var stream = File.OpenRead(filename)) 21 | { 22 | return Load(stream, mapWidth, mapHeight); 23 | } 24 | } 25 | 26 | public static List Load(Stream stream, int mapWidth, int mapHeight) 27 | { 28 | if (mapWidth <= 0 || mapHeight <= 0) 29 | throw new ArgumentOutOfRangeException("width and height should be > 0"); 30 | if (mapWidth != mapHeight) 31 | throw new InvalidOperationException("maps should be square"); 32 | 33 | using (var br = new BinaryReader(stream)) 34 | { 35 | if (br.ReadInt32() != 0x10) throw new InvalidOperationException("objects.lst: expected 0x10 header"); 36 | 37 | // TODO - this currently uses the unscaled size. the input to this function should probably 38 | // be pre-scaled with the unit size, and then this calc will become (65536 / mapWidth). 39 | float magic = 32768.0f / mapWidth; 40 | var result = new List(); 41 | while (br.BaseStream.Position < br.BaseStream.Length) 42 | { 43 | int xPos = br.ReadUInt16(); 44 | int yPos = br.ReadUInt16(); 45 | int zPos = br.ReadUInt16(); 46 | int objectId = br.ReadByte(); 47 | int unk123 = br.ReadByte(); // investigate... values 0 and 255. maybe 255 means no collision. 48 | float scale = br.ReadSingle(); 49 | int heading = br.ReadInt32(); 50 | 51 | var item = new ObjectsLstItem(); 52 | item.Position = new Vector3(xPos / magic, yPos / magic, zPos / magic); 53 | item.ObjectId = objectId; 54 | item.Scale = scale; 55 | item.Heading = heading * 360 / 255; 56 | 57 | result.Add(item); 58 | } 59 | return result; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Common/FileFormats/Pak/PakCentralDirEnd.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace monono2.Common.FileFormats.Pak 8 | { 9 | public class PakCentralDirEnd 10 | { 11 | public const int HeaderSize = 22; 12 | 13 | public ushort signature1; 14 | public ushort signature2; 15 | public ushort diskNum; 16 | public ushort firstDisk; 17 | public ushort thisDiskCentralDirCount; 18 | public ushort totalCentralDirCount; 19 | public uint centralDirSize; 20 | public uint centralDirOffset; 21 | public ushort commentLength; 22 | 23 | public static PakCentralDirEnd Read(BinaryReader br) 24 | { 25 | var result = new PakCentralDirEnd(); 26 | result.signature1 = br.ReadUInt16(); 27 | result.signature2 = br.ReadUInt16(); 28 | result.diskNum = br.ReadUInt16(); 29 | result.firstDisk = br.ReadUInt16(); 30 | result.thisDiskCentralDirCount = br.ReadUInt16(); 31 | result.totalCentralDirCount = br.ReadUInt16(); 32 | result.centralDirSize = br.ReadUInt32(); 33 | result.centralDirOffset = br.ReadUInt32(); 34 | result.commentLength = br.ReadUInt16(); 35 | 36 | if (result.signature1 != PakConstants.PAK_SIGNATURE1 || 37 | result.signature2 != PakConstants.PAK_SIGNATURE2_END) 38 | { 39 | if (result.signature1 != PakConstants.ZIP_SIGNATURE1 || 40 | result.signature2 != PakConstants.ZIP_SIGNATURE2_END) 41 | { 42 | throw new InvalidOperationException("bad EOCD signature"); 43 | } 44 | 45 | // zipformat = true 46 | } 47 | 48 | if (result.diskNum != 0) 49 | throw new InvalidOperationException("expected disk 0. multi disk not supported"); 50 | 51 | if (result.thisDiskCentralDirCount == 0) 52 | throw new InvalidOperationException("unexpected empty dir count"); 53 | 54 | if (result.thisDiskCentralDirCount != result.totalCentralDirCount) 55 | throw new InvalidOperationException("expected matching counts"); 56 | 57 | return result; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Common/FileFormats/Pak/PakCentralDirFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace monono2.Common.FileFormats.Pak 8 | { 9 | public class PakCentralDirFile 10 | { 11 | public bool isAionFormat; // true for aion header, false for zip format 12 | 13 | public ushort signature1; 14 | public ushort signature2; 15 | public ushort createVersion; 16 | public ushort extractVersion; 17 | public ushort flags; 18 | public ushort compressionMethod; 19 | public ushort time; 20 | public ushort date; 21 | public uint crc; 22 | public uint compressedSize; 23 | public uint uncompressedSize; 24 | public ushort filenameLength; 25 | public ushort extraFieldLength; 26 | public ushort fileCommentLength; 27 | public ushort diskNumStart; 28 | public ushort internalFileAttr; 29 | public uint externalFileAttr; 30 | public uint localHeaderOffset; 31 | 32 | public string filename; 33 | 34 | //public byte[] extraField; 35 | //public byte[] comment; 36 | 37 | public static PakCentralDirFile Read(BinaryReader br) 38 | { 39 | var result = new PakCentralDirFile(); 40 | result.signature1 = br.ReadUInt16(); 41 | result.signature2 = br.ReadUInt16(); 42 | result.createVersion = br.ReadUInt16(); 43 | result.extractVersion = br.ReadUInt16(); 44 | result.flags = br.ReadUInt16(); 45 | result.compressionMethod = br.ReadUInt16(); 46 | result.time = br.ReadUInt16(); 47 | result.date = br.ReadUInt16(); 48 | result.crc = br.ReadUInt32(); 49 | result.compressedSize = br.ReadUInt32(); 50 | result.uncompressedSize = br.ReadUInt32(); 51 | result.filenameLength = br.ReadUInt16(); 52 | result.extraFieldLength = br.ReadUInt16(); 53 | result.fileCommentLength = br.ReadUInt16(); 54 | result.diskNumStart = br.ReadUInt16(); 55 | result.internalFileAttr = br.ReadUInt16(); 56 | result.externalFileAttr = br.ReadUInt32(); 57 | result.localHeaderOffset = br.ReadUInt32(); 58 | 59 | result.filename = PakUtil.ReadFilename(br, result.filenameLength); 60 | 61 | if (result.signature1 == PakConstants.PAK_SIGNATURE1 && 62 | result.signature2 == PakConstants.PAK_SIGNATURE2_DIR) 63 | { 64 | result.isAionFormat = true; 65 | } 66 | else 67 | { 68 | if (result.signature1 != PakConstants.ZIP_SIGNATURE1 || 69 | result.signature2 != PakConstants.ZIP_SIGNATURE2_DIR) 70 | throw new InvalidOperationException("bad central dir signature"); 71 | 72 | // zipformat = true 73 | } 74 | 75 | if (result.extraFieldLength != 0) 76 | { 77 | var b = br.ReadBytes(result.extraFieldLength); 78 | // throw new InvalidOperationException("extra field not supported"); 79 | } 80 | 81 | if (result.fileCommentLength != 0) 82 | throw new InvalidOperationException("file comment not supported"); 83 | 84 | if (result.diskNumStart != 0) 85 | throw new InvalidOperationException("disk num not supported"); 86 | 87 | return result; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Common/FileFormats/Pak/PakFileEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace monono2.Common.FileFormats.Pak 9 | { 10 | public class PakFileEntry 11 | { 12 | public ushort signature1; 13 | public ushort signature2; 14 | public ushort extractVersion; 15 | public ushort flags; 16 | public ushort compressionMethod; 17 | public ushort time; 18 | public ushort date; 19 | public uint crc; 20 | public uint compressedSize; 21 | public uint uncompressedSize; 22 | public ushort filenameLength; 23 | public ushort extraFieldLength; 24 | 25 | public string filename; 26 | 27 | // after Read(), stream points to the start of the compressed data. 28 | public static PakFileEntry Read(BinaryReader br) 29 | { 30 | var result = new PakFileEntry(); 31 | result.signature1 = br.ReadUInt16(); 32 | result.signature2 = br.ReadUInt16(); 33 | result.extractVersion = br.ReadUInt16(); 34 | result.flags = br.ReadUInt16(); 35 | result.compressionMethod = br.ReadUInt16(); 36 | result.time = br.ReadUInt16(); 37 | result.date = br.ReadUInt16(); 38 | result.crc = br.ReadUInt32(); 39 | result.compressedSize = br.ReadUInt32(); 40 | result.uncompressedSize = br.ReadUInt32(); 41 | result.filenameLength = br.ReadUInt16(); 42 | result.extraFieldLength = br.ReadUInt16(); 43 | 44 | result.filename = PakUtil.ReadFilename(br, result.filenameLength); 45 | 46 | if (result.signature1 != PakConstants.PAK_SIGNATURE1 || 47 | result.signature2 != PakConstants.PAK_SIGNATURE2_FILE) 48 | { 49 | if (result.signature1 != PakConstants.ZIP_SIGNATURE1 || 50 | result.signature2 != PakConstants.ZIP_SIGNATURE2_FILE) 51 | throw new InvalidOperationException("bad file signature"); 52 | 53 | // zipformat = true 54 | } 55 | 56 | if (result.extraFieldLength != 0) 57 | throw new InvalidOperationException("extra field not supported"); 58 | 59 | return result; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Common/FileFormats/Pak/PakReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Ionic.Zlib; 6 | 7 | namespace monono2.Common.FileFormats.Pak 8 | { 9 | public sealed class PakReader : IDisposable 10 | { 11 | private PakReaderSlim m_pr; 12 | public Dictionary Files { get; private set; } 13 | 14 | public PakReader(string filename) 15 | { 16 | m_pr = new PakReaderSlim(filename); 17 | LoadFileListing(); 18 | } 19 | 20 | public string OriginalPakPath => m_pr.OriginalPakPath; 21 | 22 | private void LoadFileListing() 23 | { 24 | var result = new Dictionary(); 25 | 26 | foreach (var cd in m_pr.ReadCentralDir()) 27 | { 28 | result.Add(cd.filename, cd); 29 | } 30 | 31 | Files = result; 32 | } 33 | 34 | public byte[] GetFile(string filename) 35 | { 36 | return m_pr.ReadFileBytes(Files[PakUtil.NormalizeFilename(filename)]); 37 | } 38 | 39 | public void Close() 40 | { 41 | if (Files != null) 42 | { 43 | Files.Clear(); 44 | Files = null; 45 | } 46 | 47 | if (m_pr != null) 48 | { 49 | m_pr.Close(); 50 | } 51 | } 52 | 53 | public void Dispose() 54 | { 55 | Close(); 56 | } 57 | } 58 | 59 | public sealed class PakReaderSlim : IDisposable 60 | { 61 | private FileStream m_fs; 62 | private BinaryReader m_br; 63 | 64 | private PakCentralDirEnd m_eocd; 65 | 66 | public PakReaderSlim(string filename) 67 | { 68 | m_fs = File.OpenRead(filename); 69 | m_br = new BinaryReader(m_fs); 70 | 71 | ReadCentralDirEnd(); 72 | } 73 | 74 | public string OriginalPakPath { get { return m_fs.Name; } } 75 | 76 | public void Close() 77 | { 78 | if (m_br != null) 79 | { 80 | m_br.Close(); 81 | m_br = null; 82 | } 83 | 84 | if (m_fs != null) 85 | { 86 | m_fs.Close(); 87 | m_fs = null; 88 | } 89 | } 90 | 91 | 92 | private class EncryptedAionPakReader : Stream 93 | { 94 | Stream m_underlying; 95 | PakCentralDirFile m_dirfile; 96 | long m_startPosition; 97 | long m_currentPosition; 98 | 99 | public EncryptedAionPakReader(Stream stream, PakCentralDirFile dirfile) 100 | { 101 | m_underlying = stream; 102 | m_startPosition = stream.Position; 103 | m_dirfile = dirfile; 104 | 105 | if (!dirfile.isAionFormat) 106 | throw new InvalidOperationException("zip stream is not encrypted!"); 107 | } 108 | 109 | public override bool CanRead => true; 110 | public override bool CanSeek => false; 111 | public override bool CanWrite => false; 112 | 113 | public override long Length => m_dirfile.uncompressedSize; 114 | 115 | public override long Position { get => m_currentPosition; set => throw new NotImplementedException(); } 116 | 117 | public override void Flush() 118 | { 119 | throw new NotImplementedException(); 120 | } 121 | 122 | public override int Read(byte[] buffer, int offset, int count) 123 | { 124 | int bytesRead = 0; 125 | if (m_currentPosition < 32) 126 | { 127 | int tbloff = (int)m_dirfile.compressedSize & 0x3FF; 128 | while (m_currentPosition < 32 && bytesRead < count) 129 | { 130 | int c = m_underlying.ReadByte(); 131 | if (c == -1) 132 | break; 133 | 134 | // decode byte 135 | byte b = (byte)c; 136 | b ^= PakConstants.table2[tbloff + m_currentPosition]; 137 | 138 | buffer[offset + bytesRead] = b; 139 | 140 | m_currentPosition++; 141 | bytesRead++; 142 | } 143 | return bytesRead; 144 | } 145 | 146 | bytesRead = m_underlying.Read(buffer, offset, count); 147 | m_currentPosition += bytesRead; 148 | return bytesRead; 149 | } 150 | 151 | public override long Seek(long offset, SeekOrigin origin) 152 | { 153 | throw new NotImplementedException(); 154 | } 155 | 156 | public override void SetLength(long value) 157 | { 158 | throw new NotImplementedException(); 159 | } 160 | 161 | public override void Write(byte[] buffer, int offset, int count) 162 | { 163 | throw new NotImplementedException(); 164 | } 165 | } 166 | 167 | private void DecodeAionBytes(PakCentralDirFile dirfile, byte[] bytesToModify) 168 | { 169 | int tbloff = (int)dirfile.compressedSize & 0x3FF; 170 | for (int i = 0; i < bytesToModify.Length && i < 32; i++) 171 | bytesToModify[i] ^= PakConstants.table2[tbloff + i]; 172 | } 173 | 174 | public byte[] ReadFileBytes(PakCentralDirFile dirfile) 175 | { 176 | m_fs.Seek(dirfile.localHeaderOffset, SeekOrigin.Begin); 177 | var fileheader = PakFileEntry.Read(m_br); 178 | 179 | if (!dirfile.filename.Equals(fileheader.filename, StringComparison.OrdinalIgnoreCase) || 180 | dirfile.compressedSize != fileheader.compressedSize || 181 | dirfile.uncompressedSize != fileheader.uncompressedSize || 182 | dirfile.compressionMethod != fileheader.compressionMethod) 183 | { 184 | throw new InvalidOperationException("header mismatch"); 185 | } 186 | 187 | byte[] result; 188 | if (dirfile.compressionMethod == 0) 189 | { 190 | result = m_br.ReadBytes((int)dirfile.compressedSize); 191 | if (dirfile.isAionFormat) 192 | { 193 | DecodeAionBytes(dirfile, result); 194 | } 195 | } 196 | else if (dirfile.compressionMethod == 8) 197 | { 198 | Stream zip = (dirfile.isAionFormat ? new EncryptedAionPakReader(m_fs, dirfile) : (Stream)m_fs); 199 | using (var tmp = new DeflateStream(zip, CompressionMode.Decompress, true)) 200 | { 201 | var br = new BinaryReader(tmp); 202 | result = br.ReadBytes((int)dirfile.uncompressedSize); 203 | } 204 | } 205 | else 206 | throw new InvalidOperationException("unsupported compression method"); 207 | 208 | return result; 209 | } 210 | 211 | public List ReadCentralDir() 212 | { 213 | var result = new List(m_eocd.thisDiskCentralDirCount); 214 | 215 | m_fs.Seek(m_eocd.centralDirOffset, SeekOrigin.Begin); 216 | 217 | for (int i = 0; i < m_eocd.thisDiskCentralDirCount; i++) 218 | { 219 | var dirfile = PakCentralDirFile.Read(m_br); 220 | result.Add(dirfile); 221 | } 222 | 223 | return result; 224 | } 225 | 226 | private void ReadCentralDirEnd() 227 | { 228 | m_fs.Seek(-PakCentralDirEnd.HeaderSize, SeekOrigin.End); 229 | m_eocd = PakCentralDirEnd.Read(m_br); 230 | } 231 | 232 | public void Dispose() 233 | { 234 | Close(); 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /Common/FileFormats/Pak/PakUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace monono2.Common.FileFormats.Pak 8 | { 9 | public static class PakUtil 10 | { 11 | public static string NormalizeFilename(string originalFilename) 12 | { 13 | return originalFilename.ToLower().Replace('\\', '/'); 14 | } 15 | 16 | public static string ReadFilename(BinaryReader br, int length) 17 | { 18 | return NormalizeFilename(Encoding.UTF8.GetString(br.ReadBytes(length))); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Common/FileFormats/WorldIdXmlLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Xml.Linq; 6 | 7 | namespace monono2.Common 8 | { 9 | public class WorldIdEntry 10 | { 11 | public string Id; 12 | public string FolderName; 13 | } 14 | 15 | public class WorldIdXmlLoader 16 | { 17 | public Dictionary FolderNamesById; 18 | private Dictionary IdsByFolderName; 19 | 20 | public WorldIdXmlLoader(Stream stream) 21 | { 22 | Load(XDocument.Load(stream)); 23 | } 24 | 25 | public WorldIdXmlLoader(string path) 26 | { 27 | // read client maps 28 | Load(XDocument.Load(path)); 29 | } 30 | 31 | private void Load(XDocument xdoc) 32 | { 33 | // read client maps 34 | var list = xdoc.Root.Elements("data"); 35 | 36 | var hash = new HashSet(); 37 | FolderNamesById = new Dictionary(); 38 | foreach (var node in list) 39 | { 40 | // validate 41 | if (string.IsNullOrWhiteSpace(node.Value)) 42 | throw new InvalidOperationException("worldid.xml contains data entry with empty level name"); 43 | if (hash.Contains(node.Value)) 44 | throw new InvalidOperationException("worldid.xml contains duplicate level: " + node.Value); 45 | hash.Add(node.Value); 46 | // end validate 47 | 48 | FolderNamesById[node.Attribute("id").Value] = node.Value; 49 | } 50 | 51 | IdsByFolderName = new Dictionary(); 52 | foreach (var kvp in FolderNamesById) 53 | { 54 | IdsByFolderName.Add(kvp.Value.ToLowerInvariant(), kvp.Key); 55 | } 56 | } 57 | 58 | public string GetLevelId(string folderName) 59 | { 60 | return IdsByFolderName[folderName]; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Common/LevelLoadHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Microsoft.Xna.Framework; 5 | 6 | namespace monono2.Common 7 | { 8 | public static class LevelLoadHelper 9 | { 10 | public static BrushLstLoader CreateBrushLstLoader(DirManager levelDir, string relPath) 11 | { 12 | BrushLstLoader brushlst = null; 13 | if (levelDir.Exists(relPath)) 14 | { 15 | using (var brushLstStream = levelDir.OpenFile(relPath)) 16 | brushlst = new BrushLstLoader(brushLstStream); 17 | } 18 | return brushlst; 19 | } 20 | 21 | public static Dictionary CreateBrushLstCgfLoaderMap( 22 | DirManager meshesDir, BrushLstLoader brushlst) 23 | { 24 | var cgfMap = new Dictionary(); 25 | foreach (var brushInfo in brushlst.brushInfoList) 26 | { 27 | if (!meshesDir.Exists(brushInfo.filename)) 28 | { 29 | Log.WriteLine("**Model not found: " + brushInfo.filename); 30 | continue; 31 | } 32 | 33 | using (var cgfStream = meshesDir.OpenFile(brushInfo.filename)) 34 | { 35 | var c = new CgfLoader(cgfStream); 36 | cgfMap.Add(brushInfo.brushInfoIndex, c); 37 | } 38 | } 39 | return cgfMap; 40 | } 41 | 42 | public class ObjectsLstContext 43 | { 44 | public Dictionary cgfMap; 45 | public List objects; 46 | } 47 | 48 | public static ObjectsLstContext LoadObjectsLst(DirManager meshesDir, 49 | DirManager levelDir, string levelRoot = "") 50 | { 51 | if (!levelDir.Exists(Path.Combine(levelRoot, "objects.lst"))) 52 | return null; 53 | 54 | LevelDataXmlLoader levelDataXml; 55 | using (var levelDataStream = levelDir.OpenFile(Path.Combine(levelRoot, "leveldata.xml"))) 56 | levelDataXml = new LevelDataXmlLoader(levelDataStream); 57 | 58 | var result = new ObjectsLstContext(); 59 | result.cgfMap = new Dictionary(); 60 | for (int i = 0; i < levelDataXml.VegetationCgfFilenames.Count; i++) 61 | { 62 | if (!meshesDir.Exists(levelDataXml.VegetationCgfFilenames[i])) 63 | continue; 64 | 65 | using (var cgfStream = meshesDir.OpenFile(levelDataXml.VegetationCgfFilenames[i])) 66 | { 67 | result.cgfMap[i] = new CgfLoader(cgfStream); 68 | } 69 | } 70 | 71 | using (var objectLstStream = levelDir.OpenFile(Path.Combine(levelRoot, "objects.lst"))) 72 | result.objects = ObjectsLstLoader.Load(objectLstStream, 73 | levelDataXml.MapWidthAndHeight.X, levelDataXml.MapWidthAndHeight.Y); 74 | 75 | return result; 76 | } 77 | 78 | // get the world transform for a vegetation object 79 | public static Matrix GetObjectMatrix(ObjectsLstItem o) 80 | { 81 | return 82 | Matrix.CreateScale(o.Scale) * 83 | Matrix.CreateRotationZ(MathHelper.ToRadians(-o.Heading)) * 84 | Matrix.CreateTranslation(o.Position); 85 | } 86 | 87 | // get the world transform for a brush 88 | public static Matrix GetBrushMatrix(BrushEntry brush) 89 | { 90 | Matrix m = brush.rotationMatrix; // copy 91 | Util.FlipMatrixDiagonal3x3(ref m); 92 | m *= Matrix.CreateTranslation(brush.position); 93 | return m; 94 | } 95 | 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Common/Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace monono2.Common 5 | { 6 | public static class Log 7 | { 8 | private static bool s_isInit; 9 | private static bool s_isConsole; 10 | public static void Init(bool isConsole) 11 | { 12 | if (s_isInit) 13 | throw new InvalidOperationException("Log already initialized"); 14 | s_isInit = true; 15 | s_isConsole = isConsole; 16 | } 17 | 18 | private static void CheckInit() 19 | { 20 | if (!s_isInit) 21 | throw new InvalidOperationException("Log not initialized"); 22 | } 23 | 24 | public static void Write(string s) 25 | { 26 | CheckInit(); 27 | if (s_isConsole) 28 | Console.Write(s); 29 | Debug.Write(s); 30 | } 31 | 32 | public static void WriteLine(string s) 33 | { 34 | CheckInit(); 35 | if (s_isConsole) 36 | Console.WriteLine(s); 37 | Debug.WriteLine(s); 38 | } 39 | 40 | public static void WriteLine(object o) 41 | { 42 | WriteLine(o.ToString()); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Common/Navigation/GeoSpace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | 6 | namespace monono2.Common.Navigation 7 | { 8 | // manages whole-world collision testing 9 | public class GeoSpace 10 | { 11 | private BBTree m_collisionMeshTree = new BBTree(); 12 | 13 | // add a subset of an array as a mesh. 14 | public void AddCollidableMeshToTree(List collisionVertices, 15 | int collisionStart, int count) 16 | { 17 | int collisionEnd = collisionStart + count; //collisionVertices.Count; 18 | if (collisionStart != collisionEnd) 19 | { 20 | // break large meshes into triangles to improve AABB tree. 21 | // TODO - also split meshes with large areas? 22 | if (collisionEnd - collisionStart > 8000) 23 | { 24 | // add individual triangles from mesh 25 | for (int i = collisionStart; i < collisionEnd;) 26 | { 27 | var points = new Vector3[3]; 28 | points[0] = collisionVertices[i++].Position; 29 | points[1] = collisionVertices[i++].Position; 30 | points[2] = collisionVertices[i++].Position; 31 | m_collisionMeshTree.Insert(BoundingBox.CreateFromPoints(points), points); 32 | } 33 | return; 34 | } 35 | { 36 | // Add collidable mesh to bb tree 37 | var points = new Vector3[collisionEnd - collisionStart]; 38 | for (int i = collisionStart, dst = 0; i < collisionEnd; i++, dst++) 39 | points[dst] = collisionVertices[i].Position; 40 | m_collisionMeshTree.Insert(BoundingBox.CreateFromPoints(points), points); 41 | } 42 | } 43 | } 44 | 45 | public void AddCollidableMeshToTree(List triangles) 46 | { 47 | if (triangles.Count > 0) 48 | { 49 | m_collisionMeshTree.Insert(BoundingBox.CreateFromPoints(triangles), triangles.ToArray()); 50 | } 51 | } 52 | 53 | public bool HasCollision(RayX ray) 54 | { 55 | bool hasCollision = false; 56 | m_collisionMeshTree.DoActionOnIntersectingMeshes(ray.GetBoundingBox(), 57 | (Vector3[] points) => 58 | { 59 | for (int i = 0; i < points.Length; i += 3) 60 | { 61 | float t = ray.IntersectsTriangle(points[i], points[i + 1], points[i + 2]); 62 | if (t <= ray.Limit) 63 | { 64 | hasCollision = true; 65 | return false; // stop 66 | } 67 | } 68 | return true; 69 | }); 70 | return hasCollision; 71 | } 72 | 73 | public void DoActionOnIntersectingMeshes(BoundingBox test, Func func) 74 | { 75 | m_collisionMeshTree.DoActionOnIntersectingMeshes(test, func); 76 | } 77 | 78 | public void BuildTree() 79 | { 80 | m_collisionMeshTree.BuildTree(); 81 | } 82 | 83 | public void Validate() 84 | { 85 | m_collisionMeshTree.Validate(); 86 | 87 | //m_collisionMeshTree.DebugPrint(); 88 | } 89 | 90 | public BoundingBox GetBoundingBox() 91 | { 92 | return m_collisionMeshTree.GetBoundingBox(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Common/Navigation/NavMeshCompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | 6 | namespace monono2.Common.Navigation 7 | { 8 | public class NavMeshCompiler 9 | { 10 | private int m_blockWidth; 11 | private int m_blockHeight; 12 | private List[] m_floorData; 13 | private float m_step; 14 | private float m_maxZStep; 15 | 16 | public NavMeshCompiler(int blockWidth, int blockHeight, 17 | List[] floorData, 18 | float step, float maxZStep) 19 | { 20 | m_blockWidth = blockWidth; 21 | m_blockHeight = blockHeight; 22 | m_floorData = floorData; 23 | m_step = step; 24 | m_maxZStep = maxZStep; 25 | } 26 | 27 | /* 28 | height grid: 29 | each entry: 30 | 0xFF000000 - number of heights (if <= 1, inline. else index to list). 31 | 0x00FFFFFF - index to heights, or 3 byte single height. max distinct = 16 million (4096^2). 32 | 33 | height format (3 bytes): 34 | 0xFF - 8 directions, from top cw, top TR R BR bot BL left TL. 35 | 0xFFFF - encoded height = (height_value - z1) * 0xFFFF / (z2-z1). 36 | 37 | if 8dir = 0xFF, space is impassable. impassable should always be inline (no index) with floor count 0. 38 | 39 | each node edge is implied by neighbor having a close height that is not blocked in that direction. 40 | */ 41 | public CompiledNavMesh Build(HashSet subgraph, 42 | int startBX, int startBY, int newBlockWidth, int newBlockHeight, 43 | float x1, float y1, // world XY of the lower bound of the mesh bounding box. 44 | int z1, int z2) // min and max z values, range is used for encoding height 45 | { 46 | if (startBX < 0 || startBY < 0 || newBlockWidth <= 0 || newBlockHeight <= 0 || 47 | startBX + newBlockWidth > m_blockWidth || startBY + newBlockHeight > m_blockHeight) 48 | throw new ArgumentOutOfRangeException(); 49 | if (z1 < 0 || z2 <= z1) 50 | throw new ArgumentOutOfRangeException(); 51 | 52 | int endBX = startBX + newBlockWidth; 53 | int endBY = startBY + newBlockHeight; 54 | 55 | uint[] grid = new uint[newBlockWidth * newBlockHeight]; 56 | var multiheights = new List(newBlockWidth * newBlockHeight); // arbitrary capacity 57 | 58 | var bytes = new List(); // temp storage per cell 59 | 60 | int iSub = 0; // index to new grid for subgraph 61 | 62 | for (int y = startBY; y < endBY; y++) 63 | { 64 | int iAll = y * m_blockWidth + startBX; // index to floor cell 65 | 66 | for (int x = startBX; x < endBX; x++, iAll++, iSub++) 67 | { 68 | bytes.Clear(); 69 | if (m_floorData[iAll] != null) 70 | { 71 | foreach (var floor in m_floorData[iAll]) 72 | { 73 | ushort encHeight = NavMeshUtil.EncodeHeight(z1, z2, floor.Z100i / 100.0f); 74 | var flags = floor.DirectionFlags; 75 | if (flags == 0xFF) 76 | continue; 77 | 78 | // exclude vertices that are not part of the subgraph. 79 | if (!subgraph.Contains(new EdgeVertex { BX = (ushort)x, BY = (ushort)y, Z100i = floor.Z100i })) 80 | continue; 81 | 82 | bytes.Add(flags); 83 | bytes.Add((byte)(encHeight)); 84 | bytes.Add((byte)(encHeight >> 8)); 85 | } 86 | } 87 | 88 | if (bytes.Count == 3) 89 | { 90 | grid[iSub] = (1 << 24) | // count 1 91 | ((uint)bytes[0] << 16) | // flags 92 | ((uint)bytes[2] << 8) | // encoded height 93 | bytes[1]; 94 | } 95 | else if (bytes.Count > 3) 96 | { 97 | if (bytes.Count > 255 * 3) 98 | throw new InvalidOperationException("too many heights"); 99 | 100 | int index = StoreBytesInMultiFloorSet(multiheights, bytes); 101 | grid[iSub] = (uint)((bytes.Count / 3) << 24) | (uint)index; 102 | } 103 | else 104 | { 105 | grid[iSub] = 0x00FF0000; 106 | } 107 | } 108 | } 109 | 110 | if (multiheights.Count > 0xFFFFFF) 111 | throw new InvalidOperationException("height limit exceeded"); 112 | 113 | Debug.WriteLine($"Compiled NavMesh {newBlockWidth}x{newBlockHeight}x{z2-z1} bytes: grid:{grid.Length*4}, Zs:{multiheights.Count}"); 114 | 115 | var compiledNavMesh = new CompiledNavMesh(newBlockWidth, newBlockHeight, m_step, m_maxZStep, x1, y1, z1, z2, grid, multiheights.ToArray()); 116 | compiledNavMesh.Validate(); 117 | return compiledNavMesh; 118 | } 119 | 120 | private int StoreBytesInMultiFloorSet(List multiheights, List bytes) 121 | { 122 | // TODO - count number of dupes found -- if it is low - remove this code. 123 | // dedupe will probably not help much now that subgraphs are split. 124 | 125 | goto SKIP_DEDUPE; 126 | 127 | // dedupe repeated byte sequence... 128 | var end = multiheights.Count - bytes.Count; 129 | for (int j = 0; j < end; j++) 130 | { 131 | if (multiheights[j] == bytes[0]) 132 | { 133 | for (int i = 1; i < bytes.Count; i++) 134 | { 135 | if (multiheights[j + i] != bytes[i]) 136 | { 137 | goto next; 138 | } 139 | } 140 | return j; 141 | } 142 | next:; 143 | } 144 | SKIP_DEDUPE: 145 | int result = multiheights.Count; 146 | multiheights.AddRange(bytes); 147 | return result; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Common/Navigation/NavMeshUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Xna.Framework; 3 | 4 | namespace monono2.Common.Navigation 5 | { 6 | public static class NavMeshUtil 7 | { 8 | public const int DIRECTION_TOP = 1 << 0; 9 | public const int DIRECTION_TR = 1 << 1; 10 | public const int DIRECTION_RIGHT = 1 << 2; 11 | public const int DIRECTION_BR = 1 << 3; 12 | public const int DIRECTION_BOTTOM = 1 << 4; 13 | public const int DIRECTION_BL = 1 << 5; 14 | public const int DIRECTION_LEFT = 1 << 6; 15 | public const int DIRECTION_TL = 1 << 7; 16 | 17 | public static Point OffsetFromDirectionFlag(int flag) 18 | { 19 | switch (flag) 20 | { 21 | case DIRECTION_TOP: 22 | return new Point(0, 1); 23 | case DIRECTION_TR: 24 | return new Point(1, 1); 25 | case DIRECTION_RIGHT: 26 | return new Point(1, 0); 27 | case DIRECTION_BR: 28 | return new Point(1, -1); 29 | case DIRECTION_BOTTOM: 30 | return new Point(0, -1); 31 | case DIRECTION_BL: 32 | return new Point(-1, -1); 33 | case DIRECTION_LEFT: 34 | return new Point(-1, 0); 35 | case DIRECTION_TL: 36 | return new Point(-1, 1); 37 | default: 38 | throw new InvalidOperationException("invalid flag"); 39 | } 40 | } 41 | 42 | public static int GetInverseDirection(int flag) 43 | { 44 | return ((flag & 0xF) << 4) | ((flag & 0xF0) >> 4); 45 | } 46 | 47 | public static int getAdjacentDirectionsMask(int a) 48 | { 49 | return 0xFF & ((a << 1) | (a >> 1) | (a << 7) | (a >> 7)); 50 | } 51 | 52 | public static bool isDirectionAdjacent(int a, int b) 53 | { 54 | return (getAdjacentDirectionsMask(a) & b) != 0; 55 | } 56 | 57 | // x2,y2 should be 1 unit away. 58 | public static int DetermineDirection(int x1, int y1, int x2, int y2) 59 | { 60 | int dx = x2 - x1; 61 | int dy = y2 - y1; 62 | 63 | if (dx == -1) 64 | { 65 | if (dy == -1) 66 | return DIRECTION_BL; 67 | else if (dy == 0) 68 | return DIRECTION_LEFT; 69 | else if (dy == 1) 70 | return DIRECTION_TL; 71 | } 72 | else if (dx == 0) 73 | { 74 | if (dy == -1) 75 | return DIRECTION_BOTTOM; 76 | else if (dy == 1) 77 | return DIRECTION_TOP; 78 | } 79 | else if (dx == 1) 80 | { 81 | if (dy == -1) 82 | return DIRECTION_BR; 83 | else if (dy == 0) 84 | return DIRECTION_RIGHT; 85 | else if (dy == 1) 86 | return DIRECTION_TR; 87 | } 88 | 89 | throw new InvalidOperationException(); 90 | } 91 | 92 | // slightly different version of determineDirection using floats. 93 | // gets the 8-way direction from 2 arbitrary vectors (ignoring z). 94 | public static int determineDirectionFromWorld(Vector3 start, Vector3 end) 95 | { 96 | float dx = end.X - start.X; 97 | float dy = end.Y - start.Y; 98 | 99 | int compass = (((int)Math.Round(Math.Atan2(dy, dx) / (2 * Math.PI / 8))) + 8) % 8; 100 | 101 | switch (compass) 102 | { 103 | case 0: 104 | return DIRECTION_RIGHT; 105 | case 1: 106 | return DIRECTION_TR; 107 | case 2: 108 | return DIRECTION_TOP; 109 | case 3: 110 | return DIRECTION_TL; 111 | case 4: 112 | return DIRECTION_LEFT; 113 | case 5: 114 | return DIRECTION_BL; 115 | case 6: 116 | return DIRECTION_BOTTOM; 117 | case 7: 118 | return DIRECTION_BR; 119 | default: 120 | throw new InvalidOperationException("unexpected compass result"); 121 | } 122 | } 123 | 124 | public static ushort EncodeHeight(int minz, int maxz, float z) 125 | { 126 | return (ushort)((z - minz) * ushort.MaxValue / (maxz - minz)); 127 | } 128 | 129 | public static float DecodeHeight(int minz, int maxz, ushort encz) 130 | { 131 | return minz + encz * (maxz - minz) / (float)ushort.MaxValue; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Common/PriorityQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace monono2.Common 6 | { 7 | public class PriorityQueue 8 | { 9 | private SortedDictionary> m_items = new SortedDictionary>(); 10 | 11 | public void Push(T value, int priority) 12 | { 13 | if (!m_items.TryGetValue(priority, out Queue q)) 14 | { 15 | q = new Queue(); 16 | m_items.Add(priority, q); 17 | } 18 | q.Enqueue(value); 19 | } 20 | 21 | public T Pop() 22 | { 23 | var kvp = m_items.First(); 24 | var result = kvp.Value.Dequeue(); 25 | if (kvp.Value.Count == 0) 26 | m_items.Remove(kvp.Key); 27 | return result; 28 | } 29 | 30 | public bool Empty() 31 | { 32 | return m_items.Count == 0; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Common")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Common")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7180eef5-dee0-452e-970a-a43ec1ecb8ef")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Common/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # monono2 2 | Aion geo viewer and geo builder. 3 | Based on GeoDataBuilderJ. 4 | 5 | ## AionClientViewer.exe 6 | Viewer for the Aion client directory and .pak contents, encrypted files, images, 3D models and levels. 7 | 8 | 3D viewer keyboard controls:
9 | 1 - Toggle terrain.
10 | 2 - Toggle non-collidable meshes.
11 | 3 - Toggle collidable meshes.
12 | 4 - Toggle origin/axis lines.
13 | 5 - Toggle floor/navmesh. (requires code modification to use).
14 | 6 - Toggle names.
15 | 7 - Toggle doors: state 1/state 2/hidden.
16 | WADS - move.
17 | RF - move up/down.
18 | Shift - speed up.
19 | 20 | ## ALGeoBuilder 21 | Geodata is generated in a format similar to the typical format used by Aion Lightning servers 22 | but should be expected to require code modification to use. 23 | 24 | Basic usage to generate .geo files: `algeobuilder.exe -geo -o c:\output c:\aionclient` 25 | 26 | - Loads terrain, brushes and vegetation. 27 | - Generates a custom door format. 28 | - Generates a custom navmesh format. 29 | 30 | ## monono2.exe 31 | Native Monogame (non-winforms) level viewer.
32 | - Usage: monono2.exe (client dir) (level name) 33 | -------------------------------------------------------------------------------- /monono2.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2037 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AionGeoViewer", "AionGeoViewer\AionGeoViewer.csproj", "{C8415F5C-A7FE-4658-A3E8-732B2ACE4F3B}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{7180EEF5-DEE0-452E-970A-A43EC1ECB8EF}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ALGeoBuilder", "ALGeoBuilder\ALGeoBuilder.csproj", "{5AFE04CF-A395-4738-8EB5-97F41E8863F0}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AionClientViewer", "AionClientViewer\AionClientViewer.csproj", "{E01A9D4A-2C75-4E2E-BF5D-7C68BC88CFAB}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AionMonoLib", "AionMonoLib\AionMonoLib.csproj", "{87049253-8E27-4A37-AFCC-7DBCD718CADC}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|x64 = Debug|x64 19 | Release|x64 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {C8415F5C-A7FE-4658-A3E8-732B2ACE4F3B}.Debug|Any CPU.ActiveCfg = Debug|x64 23 | {C8415F5C-A7FE-4658-A3E8-732B2ACE4F3B}.Debug|Any CPU.Build.0 = Debug|x64 24 | {C8415F5C-A7FE-4658-A3E8-732B2ACE4F3B}.Debug|x64.ActiveCfg = Debug|x64 25 | {C8415F5C-A7FE-4658-A3E8-732B2ACE4F3B}.Debug|x64.Build.0 = Debug|x64 26 | {C8415F5C-A7FE-4658-A3E8-732B2ACE4F3B}.Release|x64.ActiveCfg = Release|x64 27 | {C8415F5C-A7FE-4658-A3E8-732B2ACE4F3B}.Release|x64.Build.0 = Release|x64 28 | {7180EEF5-DEE0-452E-970A-A43EC1ECB8EF}.Debug|x64.ActiveCfg = Debug|x64 29 | {7180EEF5-DEE0-452E-970A-A43EC1ECB8EF}.Debug|x64.Build.0 = Debug|x64 30 | {7180EEF5-DEE0-452E-970A-A43EC1ECB8EF}.Release|x64.ActiveCfg = Release|x64 31 | {7180EEF5-DEE0-452E-970A-A43EC1ECB8EF}.Release|x64.Build.0 = Release|x64 32 | {5AFE04CF-A395-4738-8EB5-97F41E8863F0}.Debug|x64.ActiveCfg = Debug|x64 33 | {5AFE04CF-A395-4738-8EB5-97F41E8863F0}.Debug|x64.Build.0 = Debug|x64 34 | {5AFE04CF-A395-4738-8EB5-97F41E8863F0}.Release|x64.ActiveCfg = Release|x64 35 | {5AFE04CF-A395-4738-8EB5-97F41E8863F0}.Release|x64.Build.0 = Release|x64 36 | {2CC55488-5A1A-424C-B4E1-19E742326EA5}.Debug|x64.ActiveCfg = Debug|x64 37 | {2CC55488-5A1A-424C-B4E1-19E742326EA5}.Debug|x64.Build.0 = Debug|x64 38 | {2CC55488-5A1A-424C-B4E1-19E742326EA5}.Release|x64.ActiveCfg = Release|x64 39 | {2CC55488-5A1A-424C-B4E1-19E742326EA5}.Release|x64.Build.0 = Release|x64 40 | {E01A9D4A-2C75-4E2E-BF5D-7C68BC88CFAB}.Debug|x64.ActiveCfg = Debug|x64 41 | {E01A9D4A-2C75-4E2E-BF5D-7C68BC88CFAB}.Debug|x64.Build.0 = Debug|x64 42 | {E01A9D4A-2C75-4E2E-BF5D-7C68BC88CFAB}.Release|x64.ActiveCfg = Release|x64 43 | {E01A9D4A-2C75-4E2E-BF5D-7C68BC88CFAB}.Release|x64.Build.0 = Release|x64 44 | {87049253-8E27-4A37-AFCC-7DBCD718CADC}.Debug|x64.ActiveCfg = Debug|x64 45 | {87049253-8E27-4A37-AFCC-7DBCD718CADC}.Debug|x64.Build.0 = Debug|x64 46 | {87049253-8E27-4A37-AFCC-7DBCD718CADC}.Release|x64.ActiveCfg = Release|x64 47 | {87049253-8E27-4A37-AFCC-7DBCD718CADC}.Release|x64.Build.0 = Release|x64 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {72F1F4E3-2697-4D36-9961-D5F9DD746ABA} 54 | EndGlobalSection 55 | EndGlobal 56 | --------------------------------------------------------------------------------