├── .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("" + Name + ">");
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 |
--------------------------------------------------------------------------------