More work towards classic integration.
* Centralised theater usage to the Reset functions, so it no longer needs to be given to all drawing calls. * Added interface for TilesetManager * Added MixfileManager and Mixfile classes * Removed TextureManager from globals; it is now a purely internal thing in TilesetManager.
This commit is contained in:
parent
996de77eb7
commit
8378c69c05
@ -330,6 +330,7 @@
|
||||
<Compile Include="Interface\ITeamColor.cs" />
|
||||
<Compile Include="Interface\ITechno.cs" />
|
||||
<Compile Include="Interface\ITechnoType.cs" />
|
||||
<Compile Include="Interface\ITilesetManager.cs" />
|
||||
<Compile Include="Interface\ITool.cs" />
|
||||
<Compile Include="Interface\IWidget.cs" />
|
||||
<Compile Include="Interface\ToolType.cs" />
|
||||
@ -598,12 +599,13 @@
|
||||
<Compile Include="Tools\WaypointsTool.cs" />
|
||||
<Compile Include="Utility\BooleanStringStyle.cs" />
|
||||
<Compile Include="Utility\ClassicSpriteLoader.cs" />
|
||||
<Compile Include="Utility\CRC.cs" />
|
||||
<Compile Include="Utility\ExtensionMethods.cs" />
|
||||
<Compile Include="Utility\GameTextManager.cs" />
|
||||
<Compile Include="Utility\GameTextManagerClassic.cs" />
|
||||
<Compile Include="Utility\GeneralUtils.cs" />
|
||||
<Compile Include="Utility\GenericBooleanTypeConverter.cs" />
|
||||
<Compile Include="Utility\Hashing\CRC.cs" />
|
||||
<Compile Include="Utility\Hashing\Rol.cs" />
|
||||
<Compile Include="Utility\ImageUtils.cs" />
|
||||
<Compile Include="Utility\INI.cs" />
|
||||
<Compile Include="Utility\INITools.cs" />
|
||||
@ -612,6 +614,8 @@
|
||||
<Compile Include="Utility\Megafile.cs" />
|
||||
<Compile Include="Utility\MegafileBuilder.cs" />
|
||||
<Compile Include="Utility\MegafileManager.cs" />
|
||||
<Compile Include="Utility\Mixfile.cs" />
|
||||
<Compile Include="Utility\MixfileManager.cs" />
|
||||
<Compile Include="Utility\MRU.cs" />
|
||||
<Compile Include="Utility\PropertyTracker.cs" />
|
||||
<Compile Include="Event\MapRefreshEventArgs.cs" />
|
||||
|
@ -33,6 +33,8 @@
|
||||
this.btnBrowse = new System.Windows.Forms.Button();
|
||||
this.btnContinue = new System.Windows.Forms.Button();
|
||||
this.btnQuit = new System.Windows.Forms.Button();
|
||||
this.btnClassic = new System.Windows.Forms.Button();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
@ -46,14 +48,17 @@
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBox1.Location = new System.Drawing.Point(12, 39);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(301, 20);
|
||||
this.textBox1.Size = new System.Drawing.Size(310, 20);
|
||||
this.textBox1.TabIndex = 1;
|
||||
//
|
||||
// btnBrowse
|
||||
//
|
||||
this.btnBrowse.Location = new System.Drawing.Point(319, 37);
|
||||
this.btnBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnBrowse.Location = new System.Drawing.Point(328, 37);
|
||||
this.btnBrowse.Name = "btnBrowse";
|
||||
this.btnBrowse.Size = new System.Drawing.Size(84, 23);
|
||||
this.btnBrowse.TabIndex = 2;
|
||||
@ -63,40 +68,71 @@
|
||||
//
|
||||
// btnContinue
|
||||
//
|
||||
this.btnContinue.Location = new System.Drawing.Point(319, 97);
|
||||
this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnContinue.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnContinue.Location = new System.Drawing.Point(328, 106);
|
||||
this.btnContinue.Name = "btnContinue";
|
||||
this.btnContinue.Size = new System.Drawing.Size(84, 23);
|
||||
this.btnContinue.TabIndex = 3;
|
||||
this.btnContinue.TabIndex = 5;
|
||||
this.btnContinue.Text = "OK";
|
||||
this.btnContinue.UseVisualStyleBackColor = true;
|
||||
this.btnContinue.Click += new System.EventHandler(this.btnContinue_Click);
|
||||
//
|
||||
// btnQuit
|
||||
//
|
||||
this.btnQuit.Location = new System.Drawing.Point(229, 97);
|
||||
this.btnQuit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnQuit.DialogResult = System.Windows.Forms.DialogResult.No;
|
||||
this.btnQuit.Location = new System.Drawing.Point(12, 106);
|
||||
this.btnQuit.Name = "btnQuit";
|
||||
this.btnQuit.Size = new System.Drawing.Size(84, 23);
|
||||
this.btnQuit.TabIndex = 4;
|
||||
this.btnQuit.Text = "Quit Editor";
|
||||
this.btnQuit.UseVisualStyleBackColor = true;
|
||||
this.btnQuit.Visible = false;
|
||||
this.btnQuit.Click += new System.EventHandler(this.btnQuit_Click);
|
||||
//
|
||||
// btnClassic
|
||||
//
|
||||
this.btnClassic.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnClassic.Location = new System.Drawing.Point(161, 106);
|
||||
this.btnClassic.Name = "btnClassic";
|
||||
this.btnClassic.Size = new System.Drawing.Size(161, 23);
|
||||
this.btnClassic.TabIndex = 3;
|
||||
this.btnClassic.Text = "Continue with classic graphics";
|
||||
this.btnClassic.UseVisualStyleBackColor = true;
|
||||
this.btnClassic.Click += new System.EventHandler(this.btnClassic_Click);
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.label2.Location = new System.Drawing.Point(13, 70);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(395, 33);
|
||||
this.label2.TabIndex = 0;
|
||||
this.label2.Text = "To skip this dialog and always start with the classic graphics, edit CnCTDRAMapEd" +
|
||||
"itor.exe.config and set the <ClassicGraphics> setting to True.";
|
||||
//
|
||||
// GameInstallationPathForm
|
||||
//
|
||||
this.AcceptButton = this.btnContinue;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(419, 132);
|
||||
this.CancelButton = this.btnClassic;
|
||||
this.ClientSize = new System.Drawing.Size(424, 141);
|
||||
this.ControlBox = false;
|
||||
this.Controls.Add(this.btnClassic);
|
||||
this.Controls.Add(this.btnQuit);
|
||||
this.Controls.Add(this.btnContinue);
|
||||
this.Controls.Add(this.btnBrowse);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "GameInstallationPathForm";
|
||||
this.Text = "Select game installation path";
|
||||
this.Text = "Select C&C Remastered installation path";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@ -109,5 +145,7 @@
|
||||
private System.Windows.Forms.Button btnBrowse;
|
||||
private System.Windows.Forms.Button btnContinue;
|
||||
private System.Windows.Forms.Button btnQuit;
|
||||
private System.Windows.Forms.Button btnClassic;
|
||||
private System.Windows.Forms.Label label2;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Windows.Forms;
|
||||
@ -55,7 +56,7 @@ namespace MobiusEditor
|
||||
return;
|
||||
}
|
||||
textBox1.Text = dir;
|
||||
DialogResult = DialogResult.OK;
|
||||
DialogResult = DialogResult.Yes;
|
||||
Close();
|
||||
}
|
||||
|
||||
@ -64,5 +65,11 @@ namespace MobiusEditor
|
||||
DialogResult = DialogResult.No;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnClassic_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ namespace MobiusEditor
|
||||
{
|
||||
static Globals()
|
||||
{
|
||||
SetTileSize(false);
|
||||
double minScale = 1.0 / Math.Min(OriginalTileWidth, OriginalTileHeight);
|
||||
// Startup options
|
||||
UseClassicGraphics = Properties.Settings.Default.UseClassicGraphics;
|
||||
@ -68,9 +69,15 @@ namespace MobiusEditor
|
||||
public const string MegafilePath = @"DATA";
|
||||
public const string GameTextFilenameFormat = @"DATA\TEXT\MASTERTEXTFILE_{0}.LOC";
|
||||
|
||||
public const int OriginalTileWidth = 128;
|
||||
public const int OriginalTileHeight = 128;
|
||||
public static readonly Size OriginalTileSize = new Size(OriginalTileWidth, OriginalTileHeight);
|
||||
public static void SetTileSize(bool classic)
|
||||
{
|
||||
OriginalTileWidth = classic ? 24 : 128;
|
||||
OriginalTileHeight = classic ? 24 : 128;
|
||||
}
|
||||
|
||||
public static int OriginalTileWidth { get; private set; }
|
||||
public static int OriginalTileHeight { get; private set; }
|
||||
public static Size OriginalTileSize => new Size(OriginalTileWidth, OriginalTileHeight);
|
||||
|
||||
public const int PixelWidth = 24;
|
||||
public const int PixelHeight = 24;
|
||||
@ -134,8 +141,7 @@ namespace MobiusEditor
|
||||
public const long MaxMapSize = 0x20000;
|
||||
|
||||
public static IArchiveManager TheArchiveManager;
|
||||
public static TextureManager TheTextureManager;
|
||||
public static TilesetManager TheTilesetManager;
|
||||
public static ITilesetManager TheTilesetManager;
|
||||
public static ITeamColorManager TheTeamColorManager;
|
||||
public static IGameTextManager TheGameTextManager;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MobiusEditor.Model;
|
||||
|
||||
namespace MobiusEditor.Interface
|
||||
{
|
||||
@ -9,10 +10,9 @@ namespace MobiusEditor.Interface
|
||||
{
|
||||
String LoadRoot { get; }
|
||||
|
||||
bool LoadArchive(GameType gameType, string archivePath);
|
||||
bool FileExists(string path);
|
||||
Stream OpenFile(string path);
|
||||
void Reset(GameType gameType);
|
||||
void Reset(GameType gameType, TheaterType theater);
|
||||
string[] ExpandModPaths { get; set; }
|
||||
}
|
||||
}
|
||||
|
15
CnCTDRAMapEditor/Interface/ITilesetManager.cs
Normal file
15
CnCTDRAMapEditor/Interface/ITilesetManager.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using MobiusEditor.Model;
|
||||
using MobiusEditor.Utility;
|
||||
|
||||
namespace MobiusEditor.Interface
|
||||
{
|
||||
public interface ITilesetManager
|
||||
{
|
||||
void Reset(TheaterType theater);
|
||||
bool GetTeamColorTileData(string name, int shape, ITeamColor teamColor, out Tile tile, bool generateFallback, bool onlyIfDefined);
|
||||
bool GetTeamColorTileData(string name, int shape, ITeamColor teamColor, out Tile tile);
|
||||
bool GetTileData(string name, int shape, out Tile tile, bool generateFallback, bool onlyIfDefined);
|
||||
bool GetTileData(string name, int shape, out Tile tile);
|
||||
int GetTileDataLength(string name);
|
||||
}
|
||||
}
|
@ -1335,10 +1335,10 @@ namespace MobiusEditor
|
||||
TheaterType theaterType = ttc.ConvertFrom(new MapContext(plugin.Map, false), theater);
|
||||
// Resetting to a specific game type will take care of classic mode.
|
||||
Globals.TheArchiveManager.ExpandModPaths = modPaths;
|
||||
Globals.TheArchiveManager.Reset(gameType);
|
||||
Globals.TheArchiveManager.Reset(gameType, theaterType);
|
||||
Globals.TheGameTextManager.Reset(gameType);
|
||||
Globals.TheTextureManager.Reset(gameType, theaterType);
|
||||
Globals.TheTilesetManager.Reset();
|
||||
//Globals.TheTextureManager.Reset(gameType, theaterType);
|
||||
Globals.TheTilesetManager.Reset(theaterType);
|
||||
Globals.TheTeamColorManager.Reset(gameType, theaterType);
|
||||
// Load game-specific data
|
||||
if (gameType == GameType.TiberianDawn)
|
||||
@ -1665,8 +1665,7 @@ namespace MobiusEditor
|
||||
pl.Dispose();
|
||||
}
|
||||
// Unload graphics
|
||||
Globals.TheTilesetManager.Reset();
|
||||
Globals.TheTextureManager.Reset(GameType.None, null);
|
||||
Globals.TheTilesetManager.Reset(null);
|
||||
// Clean up loaded file status
|
||||
filename = null;
|
||||
loadedFileType = FileType.None;
|
||||
@ -2398,7 +2397,7 @@ namespace MobiusEditor
|
||||
Tile templateTile = null;
|
||||
if (template != null)
|
||||
{
|
||||
Globals.TheTilesetManager.GetTileData(plugin.Map.Theater.Tilesets, template.Name, template.GetIconIndex(template.GetFirstValidIcon()), out templateTile, false, true);
|
||||
Globals.TheTilesetManager.GetTileData(template.Name, template.GetIconIndex(template.GetFirstValidIcon()), out templateTile);
|
||||
}
|
||||
// For the following, check if the thumbnail was initialised.
|
||||
SmudgeType smudge = plugin.Map.SmudgeTypes.Where(sm => !sm.IsAutoBib && sm.Icons == 1 && sm.Size.Width == 1 && sm.Size.Height == 1 && sm.Thumbnail != null
|
||||
@ -2424,13 +2423,13 @@ namespace MobiusEditor
|
||||
&& (!Globals.FilterTheaterObjects || ov.Theaters == null || ov.Theaters.Contains(plugin.Map.Theater))).OrderBy(ov => ov.ID).FirstOrDefault();
|
||||
OverlayType wall = plugin.Map.OverlayTypes.Where(ov => (ov.Flag & OverlayTypeFlag.Wall) == OverlayTypeFlag.Wall
|
||||
&& (!Globals.FilterTheaterObjects || ov.Theaters == null || ov.Theaters.Contains(plugin.Map.Theater))).OrderBy(ov => ov.ID).FirstOrDefault();
|
||||
bool gotBeacon = Globals.TheTilesetManager.GetTileData(plugin.Map.Theater.Tilesets, "beacon", 0, out Tile waypoint, false, true);
|
||||
bool gotBeacon = Globals.TheTilesetManager.GetTileData("beacon", 0, out Tile waypoint);
|
||||
if (!gotBeacon)
|
||||
{
|
||||
// Beacon only exists in rematered graphics. Get fallback.
|
||||
Globals.TheTilesetManager.GetTileData(plugin.Map.Theater.Tilesets, "armor", 6, out waypoint, false, true);
|
||||
Globals.TheTilesetManager.GetTileData("armor", 6, out waypoint);
|
||||
}
|
||||
Globals.TheTilesetManager.GetTileData(plugin.Map.Theater.Tilesets, "mine", 3, out Tile cellTrigger, false, true);
|
||||
Globals.TheTilesetManager.GetTileData("mine", 3, out Tile cellTrigger);
|
||||
LoadNewIcon(mapToolStripButton, templateTile?.Image, plugin, 0);
|
||||
LoadNewIcon(smudgeToolStripButton, smudge?.Thumbnail, plugin, 1);
|
||||
//LoadNewIcon(overlayToolStripButton, overlayTile?.Image, plugin, 2);
|
||||
@ -2446,9 +2445,13 @@ namespace MobiusEditor
|
||||
// The Texture manager returns a clone of its own cached image. The Tileset manager caches those clones,
|
||||
// and is responsible for their cleanup, but if we use it directly it needs to be disposed.
|
||||
// Icon: chrono cursor from TEXTURES_SRGB.MEG
|
||||
using (Bitmap select = Globals.TheTextureManager.GetTexture(@"DATA\ART\TEXTURES\SRGB\ICON_SELECT_GREEN_04.DDS", null, false).Item1)
|
||||
if (Globals.TheTilesetManager is TilesetManager tsm)
|
||||
{
|
||||
LoadNewIcon(selectToolStripButton, select, plugin, 11, false);
|
||||
// Loaded without tileset manager, in modern only. Will need to fix this for classic later.
|
||||
using (Bitmap select = tsm.TextureManager.GetTexture(@"DATA\ART\TEXTURES\SRGB\ICON_SELECT_GREEN_04.DDS", null, false).Item1)
|
||||
{
|
||||
LoadNewIcon(selectToolStripButton, select, plugin, 11, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void LoadNewIcon(ViewToolStripButton button, Bitmap image, IGamePlugin plugin, int index)
|
||||
|
@ -258,7 +258,7 @@ namespace MobiusEditor.Model
|
||||
return (this.Name ?? String.Empty).ToUpperInvariant();
|
||||
}
|
||||
|
||||
public void Init(GameType gameType, TheaterType theater, HouseType house, DirectionType direction)
|
||||
public void Init(GameType gameType, HouseType house, DirectionType direction)
|
||||
{
|
||||
Bitmap oldImage = this.Thumbnail;
|
||||
Building mockBuilding = new Building()
|
||||
@ -268,7 +268,7 @@ namespace MobiusEditor.Model
|
||||
Strength = 256,
|
||||
Direction = direction
|
||||
};
|
||||
(Rectangle, Action<Graphics>, bool) render = MapRenderer.RenderBuilding(gameType, theater, Point.Empty, Globals.PreviewTileSize, Globals.PreviewTileScale, mockBuilding);
|
||||
(Rectangle, Action<Graphics>, bool) render = MapRenderer.RenderBuilding(gameType, Point.Empty, Globals.PreviewTileSize, Globals.PreviewTileScale, mockBuilding);
|
||||
if (!render.Item1.IsEmpty)
|
||||
{
|
||||
Bitmap th = new Bitmap(render.Item1.Width, render.Item1.Height);
|
||||
|
@ -94,10 +94,10 @@ namespace MobiusEditor.Model
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(GameType gameType, TheaterType theater, HouseType house, DirectionType direction)
|
||||
public void Init(HouseType house, DirectionType direction)
|
||||
{
|
||||
var oldImage = Thumbnail;
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, Name, 4, out Tile tile))
|
||||
if (Globals.TheTilesetManager.GetTileData(Name, 4, out Tile tile))
|
||||
{
|
||||
_RenderSize = tile.Image.Size;
|
||||
}
|
||||
@ -113,7 +113,7 @@ namespace MobiusEditor.Model
|
||||
using (var g = Graphics.FromImage(infantryThumbnail))
|
||||
{
|
||||
MapRenderer.SetRenderSettings(g, Globals.PreviewSmoothScale);
|
||||
MapRenderer.RenderInfantry(theater, Point.Empty, Globals.PreviewTileSize, mockInfantry, InfantryStoppingType.Center).Item2(g);
|
||||
MapRenderer.RenderInfantry(Point.Empty, Globals.PreviewTileSize, mockInfantry, InfantryStoppingType.Center).Item2(g);
|
||||
}
|
||||
Thumbnail = infantryThumbnail;
|
||||
if (oldImage != null)
|
||||
|
@ -527,31 +527,31 @@ namespace MobiusEditor.Model
|
||||
}
|
||||
foreach (SmudgeType smudgeType in this.SmudgeTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
|
||||
{
|
||||
smudgeType.Init(this.Theater);
|
||||
smudgeType.Init();
|
||||
}
|
||||
foreach (OverlayType overlayType in this.OverlayTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
|
||||
{
|
||||
overlayType.Init(gameType, this.Theater);
|
||||
overlayType.Init(gameType);
|
||||
}
|
||||
foreach (TerrainType terrainType in this.TerrainTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
|
||||
{
|
||||
terrainType.Init(this.Theater);
|
||||
terrainType.Init();
|
||||
}
|
||||
// Ignore expansion status for these; they can still be enabled later.
|
||||
DirectionType infDir = this.UnitDirectionTypes.Where(d => d.Facing == FacingType.South).First();
|
||||
foreach (InfantryType infantryType in this.AllInfantryTypes)
|
||||
{
|
||||
infantryType.Init(gameType, this.Theater, this.HouseTypesIncludingNone.Where(h => h.Equals(infantryType.OwnerHouse)).FirstOrDefault(), infDir);
|
||||
infantryType.Init(this.HouseTypesIncludingNone.Where(h => h.Equals(infantryType.OwnerHouse)).FirstOrDefault(), infDir);
|
||||
}
|
||||
DirectionType unitDir = this.UnitDirectionTypes.Where(d => d.Facing == FacingType.SouthWest).First();
|
||||
foreach (UnitType unitType in this.AllUnitTypes)
|
||||
{
|
||||
unitType.Init(gameType, this.Theater, this.HouseTypesIncludingNone.Where(h => h.Equals(unitType.OwnerHouse)).FirstOrDefault(), unitDir);
|
||||
unitType.Init(gameType, this.HouseTypesIncludingNone.Where(h => h.Equals(unitType.OwnerHouse)).FirstOrDefault(), unitDir);
|
||||
}
|
||||
DirectionType bldDir = this.UnitDirectionTypes.Where(d => d.Facing == FacingType.North).First();
|
||||
foreach (BuildingType buildingType in this.BuildingTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
|
||||
{
|
||||
buildingType.Init(gameType, this.Theater, this.HouseTypesIncludingNone.Where(h => h.Equals(buildingType.OwnerHouse)).FirstOrDefault(), bldDir);
|
||||
buildingType.Init(gameType, this.HouseTypesIncludingNone.Where(h => h.Equals(buildingType.OwnerHouse)).FirstOrDefault(), bldDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ namespace MobiusEditor.Model
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(GameType gameType, TheaterType theater)
|
||||
public void Init(GameType gameType)
|
||||
{
|
||||
var oldImage = Thumbnail;
|
||||
var tileSize = Globals.PreviewTileSize;
|
||||
@ -182,7 +182,7 @@ namespace MobiusEditor.Model
|
||||
Icon = tilenr,
|
||||
};
|
||||
MapRenderer.SetRenderSettings(g, Globals.PreviewSmoothScale);
|
||||
MapRenderer.RenderOverlay(gameType, theater, Point.Empty, Globals.PreviewTileSize, Globals.PreviewTileScale, mockOverlay).Item2(g);
|
||||
MapRenderer.RenderOverlay(gameType, Point.Empty, Globals.PreviewTileSize, Globals.PreviewTileScale, mockOverlay).Item2(g);
|
||||
}
|
||||
Thumbnail = th;
|
||||
if (oldImage != null)
|
||||
|
@ -140,7 +140,7 @@ namespace MobiusEditor.Model
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(TheaterType theater)
|
||||
public void Init()
|
||||
{
|
||||
var oldImage = Thumbnail;
|
||||
var tileSize = Globals.PreviewTileSize;
|
||||
@ -155,7 +155,7 @@ namespace MobiusEditor.Model
|
||||
{
|
||||
for (int x = 0; x < Size.Width; x++)
|
||||
{
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, Name, icon++, out Tile tile))
|
||||
if (Globals.TheTilesetManager.GetTileData(Name, icon++, out Tile tile))
|
||||
{
|
||||
found = true;
|
||||
Rectangle overlayBounds = MapRenderer.RenderBounds(tile.Image.Size, new Size(1, 1), Globals.PreviewTileScale);
|
||||
|
@ -330,7 +330,7 @@ namespace MobiusEditor.Model
|
||||
// This allows mods to add 'random' tiles to existing 1x1 tiles. Check excludes 'Clear' terrain and items already defined as random.
|
||||
if (IconWidth == 1 & IconHeight == 1 && (Flag & TemplateTypeFlag.Clear) == TemplateTypeFlag.None && (Flag & TemplateTypeFlag.RandomCell) == TemplateTypeFlag.None)
|
||||
{
|
||||
if (Globals.TheTilesetManager.GetTileDataLength(theater.Tilesets, Name) > 1)
|
||||
if (Globals.TheTilesetManager.GetTileDataLength(Name) > 1)
|
||||
{
|
||||
Flag |= TemplateTypeFlag.RandomCell;
|
||||
}
|
||||
@ -351,7 +351,7 @@ namespace MobiusEditor.Model
|
||||
}
|
||||
else
|
||||
{
|
||||
numIcons = Globals.TheTilesetManager.GetTileDataLength(theater.Tilesets, Name);
|
||||
numIcons = Globals.TheTilesetManager.GetTileDataLength(Name);
|
||||
}
|
||||
numIcons = Math.Max(1, numIcons);
|
||||
NumIcons = numIcons;
|
||||
@ -404,7 +404,7 @@ namespace MobiusEditor.Model
|
||||
tryDummy = forceDummy;
|
||||
}
|
||||
// Fetch dummy if definitely in bounds, first cell of a random one, or dummy is forced.
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, nameToFetch, iconToFetch, out Tile tile, tryDummy, forceDummy || !isRandom || (x == 0 && y == 0)))
|
||||
if (Globals.TheTilesetManager.GetTileData(nameToFetch, iconToFetch, out Tile tile, tryDummy, forceDummy || !isRandom || (x == 0 && y == 0)))
|
||||
{
|
||||
if (tile.Image != null)
|
||||
{
|
||||
|
@ -190,11 +190,11 @@ namespace MobiusEditor.Model
|
||||
return (Name ?? String.Empty).ToUpperInvariant();
|
||||
}
|
||||
|
||||
public void Init(TheaterType theater)
|
||||
public void Init()
|
||||
{
|
||||
var oldImage = Thumbnail;
|
||||
string tileName = GraphicsSource;
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, tileName, DisplayIcon, out Tile tile))
|
||||
if (Globals.TheTilesetManager.GetTileData(tileName, DisplayIcon, out Tile tile))
|
||||
{
|
||||
var tileSize = Globals.PreviewTileSize;
|
||||
var renderSize = new Size(tileSize.Width * Size.Width, tileSize.Height * Size.Height);
|
||||
|
@ -146,10 +146,10 @@ namespace MobiusEditor.Model
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(GameType gameType, TheaterType theater, HouseType house, DirectionType direction)
|
||||
public void Init(GameType gameType, HouseType house, DirectionType direction)
|
||||
{
|
||||
var oldImage = Thumbnail;
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, Name, 0, out Tile tile))
|
||||
if (Globals.TheTilesetManager.GetTileData(Name, 0, out Tile tile))
|
||||
{
|
||||
_RenderSize = tile.Image.Size;
|
||||
}
|
||||
@ -170,7 +170,7 @@ namespace MobiusEditor.Model
|
||||
using (var g = Graphics.FromImage(bigThumbnail))
|
||||
{
|
||||
MapRenderer.SetRenderSettings(g, Globals.PreviewSmoothScale);
|
||||
MapRenderer.RenderUnit(gameType, theater, new Point(1, 1), Globals.PreviewTileSize, mockUnit).Item2(g);
|
||||
MapRenderer.RenderUnit(gameType, new Point(1, 1), Globals.PreviewTileSize, mockUnit).Item2(g);
|
||||
}
|
||||
using (var g2 = Graphics.FromImage(unitThumbnail))
|
||||
{
|
||||
|
@ -77,7 +77,14 @@ namespace MobiusEditor
|
||||
else
|
||||
{
|
||||
#if CLASSICIMPLEMENTED
|
||||
LoadEditorClassic();
|
||||
if (Globals.UseClassicGraphics)
|
||||
{
|
||||
LoadEditorClassic();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
@ -122,6 +129,7 @@ namespace MobiusEditor
|
||||
|
||||
private static void LoadEditorClassic()
|
||||
{
|
||||
Globals.SetTileSize(true);
|
||||
#if CLASSICIMPLEMENTED
|
||||
// The system should scan all mix archives for known filenames of other mix archives so it can do recursive searches.
|
||||
// Mix files should be given in order or depth, so first give ones that are in the folder, then ones that may occur inside others.
|
||||
@ -130,33 +138,46 @@ namespace MobiusEditor
|
||||
gameFolders.Add(GameType.TiberianDawn, "Classic\\TD\\");
|
||||
gameFolders.Add(GameType.RedAlert, "Classic\\RA\\");
|
||||
gameFolders.Add(GameType.SoleSurvivor, "Classic\\TD\\");
|
||||
//Globals.TheArchiveManager = new MixfileManager(ApplicationPath, gameFolders);
|
||||
MixfileManager mfm = new MixfileManager(ApplicationPath, gameFolders);
|
||||
Globals.TheArchiveManager = mfm;
|
||||
|
||||
var mixfilesLoaded = true;
|
||||
// This will map the mix files to the respective games, and look for them in the respective folders.
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.TiberianDawn, "cclocal.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.TiberianDawn, "conquer.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.TiberianDawn, "desert.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.TiberianDawn, "temperat.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.TiberianDawn, "winter.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.SoleSurvivor, "cclocal.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.SoleSurvivor, "conquer.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.SoleSurvivor, "desert.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.SoleSurvivor, "temperat.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.SoleSurvivor, "winter.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "expand2.mix");
|
||||
// All graphics from expand are also in expand2
|
||||
//mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "expand.mix");
|
||||
Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "redalert.mix");
|
||||
Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "main.mix");
|
||||
// Only needed for conquer.eng, and expand* override those anyway.
|
||||
//mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "local.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "conquer.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "lores.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "lores1.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "temperat.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "snow.mix");
|
||||
mixfilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.RedAlert, "interior.mix");
|
||||
// Tiberian Dawn
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.TiberianDawn, "cclocal.mix", false, false);
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.TiberianDawn, "conquer.mix", false, false);
|
||||
// Tiberian Dawn Theaters
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.TiberianDawn, "desert.mix", false, false, true);
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.TiberianDawn, "temperat.mix", false, false, true);
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.TiberianDawn, "winter.mix", false, false, true);
|
||||
// Sole Survivor
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.SoleSurvivor, "cclocal.mix", false, false);
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.SoleSurvivor, "conquer.mix", false, false);
|
||||
// Sole Survivor Theaters
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.SoleSurvivor, "desert.mix", false, false, true);
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.SoleSurvivor, "temperat.mix", false, false, true);
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.SoleSurvivor, "winter.mix", false, false, true);
|
||||
// Red Alert
|
||||
// Aftermath expand file. Required.
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.RedAlert, "expand2.mix", false, false);
|
||||
// Counterstrike expand file. All graphics from expand are also in expand2.mix,
|
||||
// but it could be used in modding to override different files. Not considered vital.
|
||||
mfm.LoadArchive(GameType.RedAlert, "expand.mix", false, false);
|
||||
// Container archives, so not considered vital.
|
||||
mfm.LoadArchive(GameType.RedAlert, "redalert.mix", true, false);
|
||||
mfm.LoadArchive(GameType.RedAlert, "main.mix", true, false);
|
||||
// Needed for theater palettes and the remap settings in palette.cps
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.RedAlert, "local.mix", false, true);
|
||||
// Main graphics archive
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.RedAlert, "conquer.mix", false, true);
|
||||
// Infantry
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.RedAlert, "lores.mix", false, true);
|
||||
// Expansion infantry
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.RedAlert, "lores1.mix", false, true);
|
||||
// Theaters
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.RedAlert, "temperat.mix", false, true, true);
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.RedAlert, "snow.mix", false, true, true);
|
||||
mixfilesLoaded &= mfm.LoadArchive(GameType.RedAlert, "interior.mix", false, true, true);
|
||||
#if !DEVELOPER
|
||||
if (!mixfilesLoaded)
|
||||
{
|
||||
@ -164,20 +185,31 @@ namespace MobiusEditor
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Initialize texture, tileset, team color, and game text managers
|
||||
// TODO
|
||||
// TilesetManager: is the system graphics are requested from, possibly with house remap.
|
||||
//Globals.TheTilesetManager = new ClassicTilesetManager(mfm);
|
||||
|
||||
// All the same. Would introduce region-based language differences, but the French and German files are... also called "conquer.eng".
|
||||
Dictionary<GameType, String> gameStringsFiles = new Dictionary<GameType, string>();
|
||||
gameStringsFiles.Add(GameType.TiberianDawn, "conquer.eng");
|
||||
gameStringsFiles.Add(GameType.RedAlert, "conquer.eng");
|
||||
gameStringsFiles.Add(GameType.SoleSurvivor, "conquer.eng");
|
||||
Globals.TheTeamColorManager = new TeamRemapManager(mfm);
|
||||
Globals.TheGameTextManager = new GameTextManagerClassic(mfm, gameStringsFiles);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void LoadEditorRemastered(String runPath)
|
||||
{
|
||||
// Initialize megafiles
|
||||
Globals.TheArchiveManager = new MegafileManager(Path.Combine(runPath, Globals.MegafilePath), runPath);
|
||||
|
||||
MegafileManager mfm = new MegafileManager(Path.Combine(runPath, Globals.MegafilePath), runPath);
|
||||
var megafilesLoaded = true;
|
||||
megafilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.None, "CONFIG.MEG");
|
||||
megafilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.None, "TEXTURES_COMMON_SRGB.MEG");
|
||||
megafilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.None, "TEXTURES_RA_SRGB.MEG");
|
||||
megafilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.None, "TEXTURES_SRGB.MEG");
|
||||
megafilesLoaded &= Globals.TheArchiveManager.LoadArchive(GameType.None, "TEXTURES_TD_SRGB.MEG");
|
||||
megafilesLoaded &= mfm.LoadArchive("CONFIG.MEG");
|
||||
megafilesLoaded &= mfm.LoadArchive("TEXTURES_COMMON_SRGB.MEG");
|
||||
megafilesLoaded &= mfm.LoadArchive("TEXTURES_RA_SRGB.MEG");
|
||||
megafilesLoaded &= mfm.LoadArchive("TEXTURES_SRGB.MEG");
|
||||
megafilesLoaded &= mfm.LoadArchive("TEXTURES_TD_SRGB.MEG");
|
||||
#if !DEVELOPER
|
||||
if (!megafilesLoaded)
|
||||
{
|
||||
@ -185,9 +217,10 @@ namespace MobiusEditor
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Globals.TheArchiveManager = mfm;
|
||||
// Initialize texture, tileset, team color, and game text managers
|
||||
Globals.TheTextureManager = new TextureManager(Globals.TheArchiveManager);
|
||||
Globals.TheTilesetManager = new TilesetManager(Globals.TheArchiveManager, Globals.TheTextureManager, Globals.TilesetsXMLPath, Globals.TexturesPath);
|
||||
TextureManager txm = new TextureManager(Globals.TheArchiveManager);
|
||||
Globals.TheTilesetManager = new TilesetManager(Globals.TheArchiveManager, txm, Globals.TilesetsXMLPath, Globals.TexturesPath);
|
||||
Globals.TheTeamColorManager = new TeamColorManager(Globals.TheArchiveManager);
|
||||
// Not adapted to mods for now...
|
||||
var cultureName = CultureInfo.CurrentUICulture.Name;
|
||||
@ -212,12 +245,12 @@ namespace MobiusEditor
|
||||
}
|
||||
// If it does not exist, try to use the directory from the settings.
|
||||
bool validSavedDirectory = false;
|
||||
if (!String.IsNullOrWhiteSpace(Properties.Settings.Default.GameDirectoryPath) &&
|
||||
Directory.Exists(Properties.Settings.Default.GameDirectoryPath))
|
||||
string savedPath = (Properties.Settings.Default.GameDirectoryPath ?? String.Empty).Trim();
|
||||
if (savedPath.Length > 0 && Directory.Exists(savedPath))
|
||||
{
|
||||
if (FileTest(Properties.Settings.Default.GameDirectoryPath))
|
||||
if (FileTest(savedPath))
|
||||
{
|
||||
runPath = Properties.Settings.Default.GameDirectoryPath;
|
||||
runPath = savedPath;
|
||||
validSavedDirectory = true;
|
||||
}
|
||||
}
|
||||
@ -240,11 +273,19 @@ namespace MobiusEditor
|
||||
if (!validSavedDirectory)
|
||||
{
|
||||
var gameInstallationPathForm = new GameInstallationPathForm();
|
||||
if (gameInstallationPathForm.ShowDialog() == DialogResult.No)
|
||||
return null;
|
||||
runPath = Path.GetDirectoryName(gameInstallationPathForm.SelectedPath);
|
||||
Properties.Settings.Default.GameDirectoryPath = runPath;
|
||||
Properties.Settings.Default.Save();
|
||||
switch (gameInstallationPathForm.ShowDialog())
|
||||
{
|
||||
case DialogResult.OK:
|
||||
runPath = Path.GetDirectoryName(gameInstallationPathForm.SelectedPath);
|
||||
Properties.Settings.Default.GameDirectoryPath = runPath;
|
||||
Properties.Settings.Default.Save();
|
||||
break;
|
||||
case DialogResult.No: // No longer used; cancelling will always fall back to classic graphics.
|
||||
return null;
|
||||
case DialogResult.Cancel:
|
||||
Globals.UseClassicGraphics = true;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return runPath;
|
||||
}
|
||||
|
@ -495,7 +495,7 @@ namespace MobiusEditor.RedAlert
|
||||
throw new ApplicationException("Cannot find the necessary file inside the " + Path.GetFileName(path) + " archive.");
|
||||
}
|
||||
INI ini = new INI();
|
||||
using (BinaryReader reader = new BinaryReader(megafile.Open(mprFile)))
|
||||
using (BinaryReader reader = new BinaryReader(megafile.OpenFile(mprFile)))
|
||||
{
|
||||
iniBytes = reader.ReadAllBytes();
|
||||
ParseIniContent(ini, iniBytes);
|
||||
|
@ -195,7 +195,7 @@ namespace MobiusEditor.Render
|
||||
icon = 0;
|
||||
}
|
||||
// If it's actually placed on the map, show it, even if it has no graphics.
|
||||
if (Globals.TheTilesetManager.GetTileData(map.Theater.Tilesets, name, icon, out Tile tile, true, false))
|
||||
if (Globals.TheTilesetManager.GetTileData(name, icon, out Tile tile, true, false))
|
||||
{
|
||||
var renderBounds = new Rectangle(topLeft.X * tileSize.Width, topLeft.Y * tileSize.Height, tileSize.Width, tileSize.Height);
|
||||
if(tile.Image != null)
|
||||
@ -256,7 +256,7 @@ namespace MobiusEditor.Render
|
||||
bool paintAsOverlay = overlay.Type.IsOverlay && (layers & MapLayerFlag.Overlay) != MapLayerFlag.None;
|
||||
if (paintAsWall || paintAsResource || paintAsOverlay)
|
||||
{
|
||||
RenderOverlay(gameType, map.Theater, location, tileSize, tileScale, overlay).Item2(graphics);
|
||||
RenderOverlay(gameType, location, tileSize, tileScale, overlay).Item2(graphics);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -280,7 +280,7 @@ namespace MobiusEditor.Render
|
||||
{
|
||||
continue;
|
||||
}
|
||||
overlappingRenderList.Add(RenderBuilding(gameType, map.Theater, topLeft, tileSize, tileScale, building));
|
||||
overlappingRenderList.Add(RenderBuilding(gameType, topLeft, tileSize, tileScale, building));
|
||||
}
|
||||
}
|
||||
if ((layers & MapLayerFlag.Infantry) != MapLayerFlag.None)
|
||||
@ -298,7 +298,7 @@ namespace MobiusEditor.Render
|
||||
{
|
||||
continue;
|
||||
}
|
||||
overlappingRenderList.Add(RenderInfantry(map.Theater, topLeft, tileSize, infantry, (InfantryStoppingType)i));
|
||||
overlappingRenderList.Add(RenderInfantry(topLeft, tileSize, infantry, (InfantryStoppingType)i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -310,7 +310,7 @@ namespace MobiusEditor.Render
|
||||
{
|
||||
continue;
|
||||
}
|
||||
overlappingRenderList.Add(RenderUnit(gameType, map.Theater, topLeft, tileSize, unit));
|
||||
overlappingRenderList.Add(RenderUnit(gameType, topLeft, tileSize, unit));
|
||||
}
|
||||
}
|
||||
// Paint flat items (like the repair bay)
|
||||
@ -333,7 +333,7 @@ namespace MobiusEditor.Render
|
||||
{
|
||||
continue;
|
||||
}
|
||||
RenderOverlay(gameType, map.Theater, topLeft, tileSize, tileScale, overlay).Item2(graphics);
|
||||
RenderOverlay(gameType, topLeft, tileSize, tileScale, overlay).Item2(graphics);
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,7 +348,7 @@ namespace MobiusEditor.Render
|
||||
{
|
||||
continue;
|
||||
}
|
||||
RenderWaypoint(gameType, map.BasicSection.SoloMission, map.Theater, tileSize, flagColors, waypoint).Item2(graphics);
|
||||
RenderWaypoint(gameType, map.BasicSection.SoloMission, tileSize, flagColors, waypoint).Item2(graphics);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -375,7 +375,7 @@ namespace MobiusEditor.Render
|
||||
);
|
||||
imageAttributes.SetColorMatrix(colorMatrix);
|
||||
}
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, smudge.Type.Name, smudge.Icon, out Tile tile))
|
||||
if (Globals.TheTilesetManager.GetTileData(smudge.Type.Name, smudge.Icon, out Tile tile))
|
||||
{
|
||||
Rectangle smudgeBounds = RenderBounds(tile.Image.Size, new Size(1, 1), tileScale);
|
||||
smudgeBounds.X += topLeft.X * tileSize.Width;
|
||||
@ -393,14 +393,14 @@ namespace MobiusEditor.Render
|
||||
}
|
||||
}
|
||||
|
||||
public static (Rectangle, Action<Graphics>) RenderOverlay(GameType gameType, TheaterType theater, Point topLeft, Size tileSize, double tileScale, Overlay overlay)
|
||||
public static (Rectangle, Action<Graphics>) RenderOverlay(GameType gameType, Point topLeft, Size tileSize, double tileScale, Overlay overlay)
|
||||
{
|
||||
OverlayType ovtype = overlay.Type;
|
||||
string name = ovtype.GraphicsSource;
|
||||
int icon = ovtype.IsConcrete || ovtype.IsResource || ovtype.IsWall || ovtype.ForceTileNr == -1 ? overlay.Icon : ovtype.ForceTileNr;
|
||||
bool isTeleport = gameType == GameType.SoleSurvivor && ovtype == SoleSurvivor.OverlayTypes.Teleport && Globals.AdjustSoleTeleports;
|
||||
// For Decoration types, generate dummy if not found.
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, name, icon, out Tile tile, (ovtype.Flag & OverlayTypeFlag.Pavement) != 0, false))
|
||||
if (Globals.TheTilesetManager.GetTileData(name, icon, out Tile tile, (ovtype.Flag & OverlayTypeFlag.Pavement) != 0, false))
|
||||
{
|
||||
int actualTopLeftX = topLeft.X * tileSize.Width;
|
||||
int actualTopLeftY = topLeft.Y * tileSize.Height;
|
||||
@ -456,7 +456,7 @@ namespace MobiusEditor.Render
|
||||
public static (Rectangle, Action<Graphics>, bool) RenderTerrain(GameType gameType, TheaterType theater, Point topLeft, Size tileSize, double tileScale, Terrain terrain)
|
||||
{
|
||||
string tileName = terrain.Type.GraphicsSource;
|
||||
if (!Globals.TheTilesetManager.GetTileData(theater.Tilesets, tileName, terrain.Type.DisplayIcon, out Tile tile))
|
||||
if (!Globals.TheTilesetManager.GetTileData(tileName, terrain.Type.DisplayIcon, out Tile tile))
|
||||
{
|
||||
Debug.Print(string.Format("Terrain {0} ({1}) not found", tileName, terrain.Type.DisplayIcon));
|
||||
return (Rectangle.Empty, (g) => { }, false);
|
||||
@ -488,7 +488,7 @@ namespace MobiusEditor.Render
|
||||
return (terrainBounds, render, false);
|
||||
}
|
||||
|
||||
public static (Rectangle, Action<Graphics>, bool) RenderBuilding(GameType gameType, TheaterType theater, Point topLeft, Size tileSize, double tileScale, Building building)
|
||||
public static (Rectangle, Action<Graphics>, bool) RenderBuilding(GameType gameType, Point topLeft, Size tileSize, double tileScale, Building building)
|
||||
{
|
||||
var tint = building.Tint;
|
||||
var icon = building.Type.FrameOFfset;
|
||||
@ -502,7 +502,7 @@ namespace MobiusEditor.Render
|
||||
// Only fetch if damaged. BuildingType.IsSingleFrame is an override for the RA mines. Everything else works with one simple logic.
|
||||
if (isDamaged && !building.Type.IsSingleFrame)
|
||||
{
|
||||
maxIcon = Globals.TheTilesetManager.GetTileDataLength(theater.Tilesets, building.Type.GraphicsSource);
|
||||
maxIcon = Globals.TheTilesetManager.GetTileDataLength(building.Type.GraphicsSource);
|
||||
hasCollapseFrame = (gameType == GameType.TiberianDawn || gameType == GameType.SoleSurvivor) && maxIcon > 1 && maxIcon % 2 == 1;
|
||||
damageIconOffs = maxIcon / 2;
|
||||
collapseIcon = maxIcon - 1;
|
||||
@ -527,7 +527,7 @@ namespace MobiusEditor.Render
|
||||
}
|
||||
}
|
||||
ITeamColor tc = building.Type.CanRemap ? Globals.TheTeamColorManager[building.House.BuildingTeamColor] : null;
|
||||
if (Globals.TheTilesetManager.GetTeamColorTileData(theater.Tilesets, building.Type.GraphicsSource, icon, tc, out Tile tile))
|
||||
if (Globals.TheTilesetManager.GetTeamColorTileData(building.Type.GraphicsSource, icon, tc, out Tile tile))
|
||||
{
|
||||
var location = new Point(topLeft.X * tileSize.Width, topLeft.Y * tileSize.Height);
|
||||
var maxSize = new Size(building.Type.Size.Width * tileSize.Width, building.Type.Size.Height * tileSize.Height);
|
||||
@ -540,10 +540,10 @@ namespace MobiusEditor.Render
|
||||
int overlayIcon = 0;
|
||||
if (building.Strength <= healthyMin)
|
||||
{
|
||||
int maxOverlayIcon = Globals.TheTilesetManager.GetTileDataLength(theater.Tilesets, building.Type.FactoryOverlay);
|
||||
int maxOverlayIcon = Globals.TheTilesetManager.GetTileDataLength(building.Type.FactoryOverlay);
|
||||
overlayIcon = maxOverlayIcon / 2;
|
||||
}
|
||||
Globals.TheTilesetManager.GetTeamColorTileData(theater.Tilesets, building.Type.FactoryOverlay, overlayIcon, Globals.TheTeamColorManager[building.House.BuildingTeamColor], out factoryOverlayTile);
|
||||
Globals.TheTilesetManager.GetTeamColorTileData(building.Type.FactoryOverlay, overlayIcon, Globals.TheTeamColorManager[building.House.BuildingTeamColor], out factoryOverlayTile);
|
||||
}
|
||||
void render(Graphics g)
|
||||
{
|
||||
@ -594,11 +594,11 @@ namespace MobiusEditor.Render
|
||||
}
|
||||
}
|
||||
|
||||
public static (Rectangle, Action<Graphics>, bool) RenderInfantry(TheaterType theater, Point topLeft, Size tileSize, Infantry infantry, InfantryStoppingType infantryStoppingType)
|
||||
public static (Rectangle, Action<Graphics>, bool) RenderInfantry(Point topLeft, Size tileSize, Infantry infantry, InfantryStoppingType infantryStoppingType)
|
||||
{
|
||||
var icon = HumanShape[Facing32[infantry.Direction.ID]];
|
||||
string teamColor = infantry.House?.UnitTeamColor;
|
||||
if (Globals.TheTilesetManager.GetTeamColorTileData(theater.Tilesets, infantry.Type.Name, icon, Globals.TheTeamColorManager[teamColor], out Tile tile))
|
||||
if (Globals.TheTilesetManager.GetTeamColorTileData(infantry.Type.Name, icon, Globals.TheTeamColorManager[teamColor], out Tile tile))
|
||||
{
|
||||
// These values are experimental, from comparing map editor screenshots to game screenshots. -Nyer
|
||||
int infantryCorrectX = tileSize.Width / -12;
|
||||
@ -665,7 +665,7 @@ namespace MobiusEditor.Render
|
||||
}
|
||||
}
|
||||
|
||||
public static (Rectangle, Action<Graphics>, bool) RenderUnit(GameType gameType, TheaterType theater, Point topLeft, Size tileSize, Unit unit)
|
||||
public static (Rectangle, Action<Graphics>, bool) RenderUnit(GameType gameType, Point topLeft, Size tileSize, Unit unit)
|
||||
{
|
||||
int icon = -1;
|
||||
if (gameType == GameType.TiberianDawn || gameType == GameType.SoleSurvivor)
|
||||
@ -732,7 +732,7 @@ namespace MobiusEditor.Render
|
||||
teamColor = unit.House.UnitTeamColor;
|
||||
}
|
||||
}
|
||||
if (!Globals.TheTilesetManager.GetTeamColorTileData(theater.Tilesets, unit.Type.Name, icon, Globals.TheTeamColorManager[teamColor], out Tile tile))
|
||||
if (!Globals.TheTilesetManager.GetTeamColorTileData(unit.Type.Name, icon, Globals.TheTeamColorManager[teamColor], out Tile tile))
|
||||
{
|
||||
Debug.Print(string.Format("Unit {0} ({1}) not found", unit.Type.Name, icon));
|
||||
return (Rectangle.Empty, (g) => { }, false);
|
||||
@ -795,9 +795,9 @@ namespace MobiusEditor.Render
|
||||
turret2Icon = getRotorIcon(turret2Name, unit.Direction.ID, turret2Icon);
|
||||
}
|
||||
if (turretName != null)
|
||||
Globals.TheTilesetManager.GetTeamColorTileData(theater.Tilesets, turretName, turretIcon, Globals.TheTeamColorManager[teamColor], out turretTile);
|
||||
Globals.TheTilesetManager.GetTeamColorTileData(turretName, turretIcon, Globals.TheTeamColorManager[teamColor], out turretTile);
|
||||
if (turret2Name != null)
|
||||
Globals.TheTilesetManager.GetTeamColorTileData(theater.Tilesets, turret2Name, turret2Icon, Globals.TheTeamColorManager[teamColor], out turret2Tile);
|
||||
Globals.TheTilesetManager.GetTeamColorTileData(turret2Name, turret2Icon, Globals.TheTeamColorManager[teamColor], out turret2Tile);
|
||||
}
|
||||
var tint = unit.Tint;
|
||||
void render(Graphics g)
|
||||
@ -903,16 +903,16 @@ namespace MobiusEditor.Render
|
||||
return (renderBounds, render, false);
|
||||
}
|
||||
|
||||
public static (Rectangle, Action<Graphics>) RenderWaypoint(GameType gameType, bool soloMission, TheaterType theater, Size tileSize, ITeamColor[] flagColors, Waypoint waypoint)
|
||||
public static (Rectangle, Action<Graphics>) RenderWaypoint(GameType gameType, bool soloMission, Size tileSize, ITeamColor[] flagColors, Waypoint waypoint)
|
||||
{
|
||||
// Opacity is normally 0.5 for non-flag waypoint indicators, but is variable because the post-render
|
||||
// actions of the waypoints tool will paint a fully opaque version over the currently selected waypoint.
|
||||
//int mpId = Waypoint.GetMpIdFromFlag(waypoint.Flag);
|
||||
//float defaultOpacity = !soloMission && mpId >= 0 && mpId < flagColors.Length ? 1.0f : 0.5f;
|
||||
return Render(gameType, soloMission, theater, tileSize, flagColors, waypoint, 0.5f);
|
||||
return RenderWaypoint(gameType, soloMission, tileSize, flagColors, waypoint, 0.5f);
|
||||
}
|
||||
|
||||
public static (Rectangle, Action<Graphics>) Render(GameType gameType, bool soloMission, TheaterType theater, Size tileSize, ITeamColor[] flagColors, Waypoint waypoint, float transparencyModifier)
|
||||
public static (Rectangle, Action<Graphics>) RenderWaypoint(GameType gameType, bool soloMission, Size tileSize, ITeamColor[] flagColors, Waypoint waypoint, float transparencyModifier)
|
||||
{
|
||||
if (!waypoint.Point.HasValue)
|
||||
{
|
||||
@ -925,31 +925,36 @@ namespace MobiusEditor.Render
|
||||
float brightness = 1.0f;
|
||||
int mpId = Waypoint.GetMpIdFromFlag(waypoint.Flag);
|
||||
int icon = 0;
|
||||
bool defaultIcon = true;
|
||||
bool isDefaultIcon = true;
|
||||
bool gotIcon = false;
|
||||
Tile tile;
|
||||
if (!soloMission && mpId >= 0 && mpId < flagColors.Length)
|
||||
{
|
||||
defaultIcon = false;
|
||||
isDefaultIcon = false;
|
||||
tileGraphics = "flagfly";
|
||||
// Always paint flags as opaque.
|
||||
transparencyModifier = 1.0f;
|
||||
teamColor = flagColors[mpId];
|
||||
icon = 0;
|
||||
gotIcon = Globals.TheTilesetManager.GetTeamColorTileData(tileGraphics, icon, teamColor, out tile);
|
||||
}
|
||||
if (gameType == GameType.SoleSurvivor && (waypoint.Flag & WaypointFlag.CrateSpawn) == WaypointFlag.CrateSpawn)
|
||||
else if (gameType == GameType.SoleSurvivor && (waypoint.Flag & WaypointFlag.CrateSpawn) == WaypointFlag.CrateSpawn)
|
||||
{
|
||||
defaultIcon = false;
|
||||
isDefaultIcon = false;
|
||||
tileGraphics = "scrate";
|
||||
icon = 0;
|
||||
//tint = Color.FromArgb(waypoint.Tint.A, Color.Green);
|
||||
//brightness = 1.5f;
|
||||
gotIcon = Globals.TheTilesetManager.GetTileData(tileGraphics, icon, out tile);
|
||||
}
|
||||
bool gotIcon = Globals.TheTilesetManager.GetTeamColorTileData(theater.Tilesets, tileGraphics, icon, teamColor, out Tile tile, true, true);
|
||||
if (!gotIcon && defaultIcon)
|
||||
else
|
||||
{
|
||||
gotIcon = Globals.TheTilesetManager.GetTileData(tileGraphics, icon, out tile);
|
||||
}
|
||||
if (!gotIcon && isDefaultIcon)
|
||||
{
|
||||
// Beacon only exists in remastered graphics. Get fallback.
|
||||
tileGraphics = "armor";
|
||||
icon = 6;
|
||||
gotIcon = Globals.TheTilesetManager.GetTeamColorTileData(theater.Tilesets, tileGraphics, icon, teamColor, out tile, true, true);
|
||||
gotIcon = Globals.TheTilesetManager.GetTeamColorTileData(tileGraphics, icon, teamColor, out tile);
|
||||
}
|
||||
if (!gotIcon)
|
||||
{
|
||||
@ -1184,7 +1189,7 @@ namespace MobiusEditor.Render
|
||||
string name = ovtype.GraphicsSource;
|
||||
int icon = ovtype.IsConcrete || ovtype.IsResource || ovtype.IsWall || ovtype.ForceTileNr == -1 ? overlay.Icon : ovtype.ForceTileNr;
|
||||
// For Decoration types, generate dummy if not found.
|
||||
if (!Globals.TheTilesetManager.GetTileData(theater.Tilesets, name, icon, out Tile tile, (ovtype.Flag & OverlayTypeFlag.Pavement) != 0, false))
|
||||
if (!Globals.TheTilesetManager.GetTileData(name, icon, out Tile tile, (ovtype.Flag & OverlayTypeFlag.Pavement) != 0, false))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@ -1306,7 +1311,7 @@ namespace MobiusEditor.Render
|
||||
Type = SoleSurvivor.OverlayTypes.Road,
|
||||
Tint = Color.FromArgb(128, Color.White)
|
||||
};
|
||||
RenderOverlay(gameType, map.Theater, p, tileSize, tileScale, footballTerrain).Item2(graphics);
|
||||
RenderOverlay(gameType, p, tileSize, tileScale, footballTerrain).Item2(graphics);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1328,7 +1333,7 @@ namespace MobiusEditor.Render
|
||||
ITeamColor[] flagColors = map.FlagColors.ToArray();
|
||||
foreach (Waypoint wp in footballWayPoints)
|
||||
{
|
||||
RenderWaypoint(gameType, false, map.Theater, tileSize, flagColors, wp).Item2(graphics);
|
||||
RenderWaypoint(gameType, false, tileSize, flagColors, wp).Item2(graphics);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,8 +486,8 @@ namespace MobiusEditor.TiberianDawn
|
||||
{
|
||||
throw new ApplicationException("Cannot find the necessary files inside the " + Path.GetFileName(path) + " archive.");
|
||||
}
|
||||
using (BinaryReader iniReader = new BinaryReader(megafile.Open(iniFile)))
|
||||
using (BinaryReader binReader = new BinaryReader(megafile.Open(binFile)))
|
||||
using (BinaryReader iniReader = new BinaryReader(megafile.OpenFile(iniFile)))
|
||||
using (BinaryReader binReader = new BinaryReader(megafile.OpenFile(binFile)))
|
||||
{
|
||||
iniBytes = iniReader.ReadAllBytes();
|
||||
ParseIniContent(ini, iniBytes, forSole);
|
||||
|
@ -736,7 +736,7 @@ namespace MobiusEditor.Tools
|
||||
bibRender.Add(bibCellRender);
|
||||
}
|
||||
}
|
||||
var renderBuilding = MapRenderer.RenderBuilding(plugin.GameType, map.Theater, new Point(0, 0), Globals.PreviewTileSize, Globals.PreviewTileScale, mockBuilding);
|
||||
var renderBuilding = MapRenderer.RenderBuilding(plugin.GameType, new Point(0, 0), Globals.PreviewTileSize, Globals.PreviewTileScale, mockBuilding);
|
||||
Size previewSize = mockBuilding.OverlapBounds.Size;
|
||||
var buildingPreview = new Bitmap(previewSize.Width * Globals.PreviewTileWidth, previewSize.Height * Globals.PreviewTileHeight);
|
||||
buildingPreview.SetResolution(96, 96);
|
||||
|
@ -732,7 +732,7 @@ namespace MobiusEditor.Tools
|
||||
using (var g = Graphics.FromImage(infantryPreview))
|
||||
{
|
||||
MapRenderer.SetRenderSettings(g, Globals.PreviewSmoothScale);
|
||||
MapRenderer.RenderInfantry(map.Theater, Point.Empty, Globals.PreviewTileSize, mockInfantry, InfantryStoppingType.Center).Item2(g);
|
||||
MapRenderer.RenderInfantry(Point.Empty, Globals.PreviewTileSize, mockInfantry, InfantryStoppingType.Center).Item2(g);
|
||||
}
|
||||
infantryTypeMapPanel.MapImage = infantryPreview;
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ namespace MobiusEditor.Tools
|
||||
Type = SelectedOverlayType,
|
||||
Icon = 0
|
||||
};
|
||||
var render = MapRenderer.RenderOverlay(plugin.GameType, map.Theater, new Point(0,0), Globals.PreviewTileSize, Globals.PreviewTileScale, mockOverlay);
|
||||
var render = MapRenderer.RenderOverlay(plugin.GameType, new Point(0,0), Globals.PreviewTileSize, Globals.PreviewTileScale, mockOverlay);
|
||||
if (!render.Item1.IsEmpty)
|
||||
{
|
||||
using (var g = Graphics.FromImage(overlayPreview))
|
||||
|
@ -551,7 +551,7 @@ namespace MobiusEditor.Tools
|
||||
using (var g = Graphics.FromImage(unitPreview))
|
||||
{
|
||||
MapRenderer.SetRenderSettings(g, Globals.PreviewSmoothScale);
|
||||
MapRenderer.RenderUnit(plugin.GameType, map.Theater, new Point(1, 1), Globals.PreviewTileSize, mockUnit).Item2(g);
|
||||
MapRenderer.RenderUnit(plugin.GameType, new Point(1, 1), Globals.PreviewTileSize, mockUnit).Item2(g);
|
||||
}
|
||||
unitTypeMapPanel.MapImage = unitPreview;
|
||||
}
|
||||
|
@ -464,7 +464,7 @@ namespace MobiusEditor.Tools
|
||||
// If the selected waypoint is not a flag, re-render it as opaque.
|
||||
if (selected != null && (plugin.Map.BasicSection.SoloMission || (selected.Flag & WaypointFlag.PlayerStart) != WaypointFlag.PlayerStart))
|
||||
{
|
||||
MapRenderer.Render(plugin.GameType, true, map.Theater, Globals.MapTileSize, map.FlagColors.ToArray(), selected, 1.0f).Item2(graphics);
|
||||
MapRenderer.RenderWaypoint(plugin.GameType, true, Globals.MapTileSize, map.FlagColors.ToArray(), selected, 1.0f).Item2(graphics);
|
||||
}
|
||||
// Render those here to they are put over the opaque redraw of the current waypoint.
|
||||
MapRenderer.RenderAllTechnoTriggers(graphics, plugin.Map, Globals.MapTileSize, Globals.MapTileScale, Layers);
|
||||
|
@ -65,7 +65,7 @@ namespace MobiusEditor.Utility
|
||||
string testIniFile = megafile.Where(p => ext.IsMatch(Path.GetExtension(p).ToLower())).FirstOrDefault();
|
||||
if (testIniFile != null)
|
||||
{
|
||||
using (StreamReader iniReader = new StreamReader(megafile.Open(testIniFile), encDOS))
|
||||
using (StreamReader iniReader = new StreamReader(megafile.OpenFile(testIniFile), encDOS))
|
||||
{
|
||||
iniContents = iniReader.ReadToEnd();
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
|
||||
using System;
|
||||
|
||||
namespace MobiusEditor.Utility
|
||||
namespace MobiusEditor.Utility.Hashing
|
||||
{
|
||||
public class CRC
|
||||
{
|
142
CnCTDRAMapEditor/Utility/Hashing/Rol.cs
Normal file
142
CnCTDRAMapEditor/Utility/Hashing/Rol.cs
Normal file
@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MobiusEditor.Utility.Hashing
|
||||
{
|
||||
public class HashRol1 : Rol
|
||||
{
|
||||
public override UInt32 GetNameIdCorrectCase(String name)
|
||||
{
|
||||
return GetNameId(name, 1);
|
||||
}
|
||||
|
||||
public override UInt32 GetNameIdCorrectCase(Byte[] data)
|
||||
{
|
||||
return GetNameId(data, 1);
|
||||
}
|
||||
|
||||
public override String GetDisplayName()
|
||||
{
|
||||
return "ROL (TD/RA)";
|
||||
}
|
||||
|
||||
public override String GetSimpleName()
|
||||
{
|
||||
return "ROL";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class HashRol3 : Rol
|
||||
{
|
||||
public override UInt32 GetNameIdCorrectCase(String name)
|
||||
{
|
||||
return GetNameId(name, 3);
|
||||
}
|
||||
|
||||
public override UInt32 GetNameIdCorrectCase(Byte[] data)
|
||||
{
|
||||
return GetNameId(data, 3);
|
||||
}
|
||||
|
||||
public override String GetDisplayName()
|
||||
{
|
||||
return "ROL3 (setup TS/RA2/...)";
|
||||
}
|
||||
|
||||
public override String GetSimpleName()
|
||||
{
|
||||
return "ROL3";
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Rol
|
||||
{
|
||||
public UInt32 GetNameId(String name)
|
||||
{
|
||||
if (name == null)
|
||||
name = String.Empty;
|
||||
return GetNameIdCorrectCase(this.NeedsUpperCase ? name.ToUpperInvariant() : name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The hashing method. Assumes that the input is already formatted to the correct case according to NeedsUpperCase.
|
||||
/// </summary>
|
||||
/// <param name="name">String to hash.</param>
|
||||
/// <returns>The hashed value.</returns>
|
||||
public abstract UInt32 GetNameIdCorrectCase(String name);
|
||||
|
||||
/// <summary>
|
||||
/// The hashing method. Assumes that the input is already formatted to the correct case according to NeedsUpperCase.
|
||||
/// </summary>
|
||||
/// <param name="name">String to hash, as byte array.</param>
|
||||
/// <returns>The hashed value.</returns>
|
||||
public abstract UInt32 GetNameIdCorrectCase(Byte[] data);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the display name of the hashing method.
|
||||
/// </summary>
|
||||
/// <returns>The display name of the hashing method.</returns>
|
||||
public abstract String GetDisplayName();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the short name of the hashing method.
|
||||
/// </summary>
|
||||
/// <returns>The short name of the hashing method.</returns>
|
||||
public abstract String GetSimpleName();
|
||||
|
||||
/// <summary>
|
||||
/// Allows supporting methods that are not case insensitive.
|
||||
/// </summary>
|
||||
public virtual Boolean NeedsUpperCase
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
protected UInt32 GetNameId(String name, Int32 rot)
|
||||
{
|
||||
return GetNameId(Encoding.ASCII.GetBytes(name), rot);
|
||||
}
|
||||
|
||||
protected UInt32 GetNameId(Byte[] values, Int32 rot)
|
||||
{
|
||||
Int32 i = 0;
|
||||
UInt32 id = 0;
|
||||
Int32 len = values.Length; // length of the filename
|
||||
while (i < len)
|
||||
{
|
||||
// get next uint32 chunk
|
||||
UInt32 buffer = this.GetUInt32FromBuffer(values, len, ref i);
|
||||
if (i <= len)
|
||||
id = RotateLeft(id, rot) + buffer;
|
||||
else // Known quirk: final one only does ROL-1
|
||||
id = RotateLeft(id, 1) + buffer;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
protected UInt32 GetUInt32FromBuffer(Byte[] values, Int32 length, ref Int32 index)
|
||||
{
|
||||
UInt32 a = 0;
|
||||
for (Int32 i = 0; i < 4; ++i)
|
||||
{
|
||||
a >>= 8;
|
||||
if (index < length)
|
||||
a += ((UInt32)values[index] << 24);
|
||||
index++;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
public static UInt32 RotateLeft(UInt32 value, Int32 count)
|
||||
{
|
||||
return (value << count) | (value >> (32 - count));
|
||||
}
|
||||
|
||||
public static UInt32 RotateRight(UInt32 value, Int32 count)
|
||||
{
|
||||
return (value >> count) | (value << (32 - count));
|
||||
}
|
||||
}
|
||||
}
|
@ -97,18 +97,25 @@ namespace MobiusEditor.Utility
|
||||
}
|
||||
}
|
||||
|
||||
public Stream Open(string path)
|
||||
public Stream OpenFile(string path)
|
||||
{
|
||||
if (disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException("MegaFile");
|
||||
}
|
||||
if (!fileTable.TryGetValue(path, out SubFileData subFile))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return megafileMap.CreateViewStream(subFile.SubfileImageDataOffset, subFile.SubfileSize, MemoryMappedFileAccess.Read);
|
||||
}
|
||||
|
||||
public IEnumerator<string> GetEnumerator()
|
||||
{
|
||||
if (disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException("MegaFile");
|
||||
}
|
||||
foreach (var file in stringTable)
|
||||
{
|
||||
yield return file;
|
||||
|
@ -18,6 +18,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using MobiusEditor.Utility.Hashing;
|
||||
|
||||
namespace MobiusEditor.Utility
|
||||
{
|
||||
|
@ -12,11 +12,12 @@
|
||||
// distributed with this program. You should have received a copy of the
|
||||
// GNU General Public License along with permitted additional restrictions
|
||||
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
|
||||
using MobiusEditor.Interface;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MobiusEditor.Interface;
|
||||
using MobiusEditor.Model;
|
||||
|
||||
namespace MobiusEditor.Utility
|
||||
{
|
||||
@ -38,7 +39,7 @@ namespace MobiusEditor.Utility
|
||||
this.LoadRoot = Path.GetFullPath(loadRoot);
|
||||
}
|
||||
|
||||
public bool LoadArchive(GameType gameType, string archivePath)
|
||||
public bool LoadArchive(string archivePath)
|
||||
{
|
||||
if (!Path.IsPathRooted(archivePath))
|
||||
{
|
||||
@ -89,7 +90,7 @@ namespace MobiusEditor.Utility
|
||||
}
|
||||
foreach (var megafile in megafiles)
|
||||
{
|
||||
var stream = megafile.Open(path.ToUpper());
|
||||
var stream = megafile.OpenFile(path.ToUpper());
|
||||
if (stream != null)
|
||||
{
|
||||
return stream;
|
||||
@ -98,7 +99,7 @@ namespace MobiusEditor.Utility
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Reset(GameType gameType)
|
||||
public void Reset(GameType gameType, TheaterType theater)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
@ -110,7 +111,7 @@ namespace MobiusEditor.Utility
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
|
200
CnCTDRAMapEditor/Utility/Mixfile.cs
Normal file
200
CnCTDRAMapEditor/Utility/Mixfile.cs
Normal file
@ -0,0 +1,200 @@
|
||||
using MobiusEditor.Utility.Hashing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
|
||||
namespace MobiusEditor.Utility
|
||||
{
|
||||
public class Mixfile: IDisposable
|
||||
{
|
||||
private Dictionary<uint, (uint Offset, uint Length)> mixFileContents = new Dictionary<uint, (uint, uint)>();
|
||||
private HashRol1 hashRol = new HashRol1();
|
||||
|
||||
public string MixFileName { get; private set; }
|
||||
private MemoryMappedFile mixFileMap;
|
||||
private bool isEmbedded = false;
|
||||
private long fileStart;
|
||||
private long fileLength;
|
||||
|
||||
public Mixfile(string mixPath)
|
||||
{
|
||||
FileInfo mixFile = new FileInfo(mixPath);
|
||||
this.fileStart = 0;
|
||||
this.fileLength = mixFile.Length;
|
||||
this.MixFileName = mixPath;
|
||||
this.mixFileMap = MemoryMappedFile.CreateFromFile(
|
||||
new FileStream(mixPath, FileMode.Open, FileAccess.Read, FileShare.Read),
|
||||
null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, false);
|
||||
this.ReadMixHeader(this.mixFileMap, this.fileStart, this.fileLength);
|
||||
}
|
||||
|
||||
public Mixfile(Mixfile container, string name)
|
||||
{
|
||||
this.isEmbedded = true;
|
||||
this.MixFileName = container.MixFileName + " -> " + name;
|
||||
if (!container.GetFileInfo(name, out uint offset, out uint length))
|
||||
{
|
||||
throw new FileNotFoundException(name + " was not found inside this mix archive.");
|
||||
}
|
||||
this.fileStart = offset;
|
||||
this.fileLength = length;
|
||||
// Copy reference to parent map. The "CreateViewStream" function takes care of reading the right parts from it.
|
||||
this.mixFileMap = container.mixFileMap;
|
||||
this.ReadMixHeader(mixFileMap, offset, fileLength);
|
||||
}
|
||||
|
||||
private void ReadMixHeader(MemoryMappedFile mixMap, long mixStart, long mixLength)
|
||||
{
|
||||
mixFileContents.Clear();
|
||||
uint readOffset = 0;
|
||||
ushort nrOfFiles = 0;
|
||||
bool hasFlags = false;
|
||||
bool encrypted = false;
|
||||
bool checksum = false;
|
||||
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
|
||||
{
|
||||
ushort start = headerReader.ReadUInt16();
|
||||
if (start == 0)
|
||||
hasFlags = true;
|
||||
else
|
||||
nrOfFiles = start;
|
||||
readOffset += 2;
|
||||
}
|
||||
if (hasFlags)
|
||||
{
|
||||
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
|
||||
{
|
||||
var flags = headerReader.ReadUInt16();
|
||||
checksum = (flags & 1) != 0;
|
||||
encrypted = (flags & 2) != 0;
|
||||
readOffset += 2;
|
||||
}
|
||||
// Not encrypted; read nr of files.
|
||||
if (!encrypted)
|
||||
{
|
||||
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
|
||||
{
|
||||
nrOfFiles = headerReader.ReadUInt16();
|
||||
readOffset += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint headerSize;
|
||||
Byte[] header = null;
|
||||
if (encrypted)
|
||||
{
|
||||
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, 80)))
|
||||
{
|
||||
byte[] blowfishKey = headerReader.ReadAllBytes();
|
||||
readOffset += 80;
|
||||
}
|
||||
// The rest of the blowfish decryption
|
||||
throw new NotSupportedException("Encrypred mix archives are currently not supported.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore data size in header; it's only used for caching.
|
||||
readOffset += 2;
|
||||
headerSize = (UInt32)(nrOfFiles * 12);
|
||||
if (readOffset + headerSize > mixLength)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Not a valid mix file: header length exceeds file length.");
|
||||
}
|
||||
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, headerSize)))
|
||||
{
|
||||
header = headerReader.ReadBytes((Int32)headerSize);
|
||||
// End of header reading; no longer needed.
|
||||
//readOffset += headerSize;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < nrOfFiles; ++i)
|
||||
{
|
||||
using (BinaryReader headerReader = new BinaryReader(new MemoryStream(header)))
|
||||
{
|
||||
uint fileId = headerReader.ReadUInt32();
|
||||
uint fileOffset = headerReader.ReadUInt32();
|
||||
uint fileLength = headerReader.ReadUInt32();
|
||||
if (fileOffset + fileLength > mixLength)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(String.Format("Not a valid mix file: file #{0} with id {1:X08} exceeds archive length.", i, fileId));
|
||||
}
|
||||
mixFileContents.Add(fileId, (fileOffset, fileLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetFileInfo(string filename, out uint offset, out uint length)
|
||||
{
|
||||
offset = 0;
|
||||
length = 0;
|
||||
uint fileId = hashRol.GetNameId(filename);
|
||||
(uint Offset, uint Length) fileLoc;
|
||||
if (!mixFileContents.TryGetValue(fileId, out fileLoc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
offset = fileLoc.Offset;
|
||||
length = fileLoc.Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
public Stream OpenFile(string path)
|
||||
{
|
||||
uint fileId = hashRol.GetNameId(path);
|
||||
(uint Offset, uint Length) fileLoc;
|
||||
if (!mixFileContents.TryGetValue(fileId, out fileLoc))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return CreateViewStream(mixFileMap, fileStart, fileLength, fileLoc.Offset, fileLoc.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a view stream on the current mix file, or on the current embedded mix file.
|
||||
/// </summary>
|
||||
/// <param name="mixMap">The MemoryMappedFile of the mix file or parent mix file</param>
|
||||
/// <param name="mixFileStart">Start of the current mix file inside the mixMap</param>
|
||||
/// <param name="mixFileLength">End of the current mix file inside the mixMap</param>
|
||||
/// <param name="dataReadOffset">Read position inside the current mix file.</param>
|
||||
/// <param name="dataReadLength">Length of the data to read.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="IndexOutOfRangeException">The data is not in the bounds of this mix file.</exception>
|
||||
private Stream CreateViewStream(MemoryMappedFile mixMap, long mixFileStart, long mixFileLength, uint dataReadOffset, uint dataReadLength)
|
||||
{
|
||||
if (disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException("Mixfile");
|
||||
}
|
||||
if (dataReadOffset + dataReadLength > mixFileLength)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Data exceeds mix file bounds.");
|
||||
}
|
||||
return mixMap.CreateViewStream(mixFileStart + dataReadOffset, dataReadLength, MemoryMappedFileAccess.Read);
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
// Only dispose if not an embedded mix file.
|
||||
// If embedded, the mixFileMap is contained in the parent.
|
||||
if (disposing && !isEmbedded)
|
||||
{
|
||||
mixFileMap.Dispose();
|
||||
}
|
||||
mixFileMap = null;
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
263
CnCTDRAMapEditor/Utility/MixfileManager.cs
Normal file
263
CnCTDRAMapEditor/Utility/MixfileManager.cs
Normal file
@ -0,0 +1,263 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MobiusEditor.Interface;
|
||||
using MobiusEditor.Model;
|
||||
|
||||
namespace MobiusEditor.Utility
|
||||
{
|
||||
public class MixfileManager : IArchiveManager
|
||||
{
|
||||
private class MixInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public bool IsContainer { get; set; }
|
||||
public bool CanBeEmbedded { get; set; }
|
||||
public bool IsTheater { get; set; }
|
||||
|
||||
|
||||
public MixInfo(String name, bool isContainer, bool canBeEmbedded, bool isTheater)
|
||||
{
|
||||
this.Name = name;
|
||||
this.IsContainer = isContainer;
|
||||
this.CanBeEmbedded = canBeEmbedded;
|
||||
this.IsTheater = isTheater;
|
||||
}
|
||||
}
|
||||
|
||||
private string applicationPath;
|
||||
private Dictionary<GameType, string> gameFolders;
|
||||
private Dictionary<GameType, List<MixInfo>> gameArchives;
|
||||
private readonly List<MixInfo> currentMixFileInfo = new List<MixInfo>();
|
||||
private List<string> currentMixNames = new List<string>();
|
||||
private readonly Dictionary<string, Mixfile> currentMixFiles = new Dictionary<string, Mixfile>();
|
||||
private GameType currentGameType = GameType.None;
|
||||
|
||||
public string LoadRoot { get { return applicationPath; } }
|
||||
public string[] ExpandModPaths { get; set; }
|
||||
|
||||
public MixfileManager(String applicationPath, Dictionary<GameType, String> gameFolders)
|
||||
{
|
||||
this.applicationPath = applicationPath;
|
||||
this.gameFolders = gameFolders;
|
||||
this.gameArchives = new Dictionary<GameType, List<MixInfo>>();
|
||||
//this.Reset(currentGameType, null);
|
||||
}
|
||||
|
||||
public bool FileExists(String path)
|
||||
{
|
||||
// TODO check mod paths first? Not sure; files tend to need mixfiles
|
||||
using (Stream str = this.OpenFile(path))
|
||||
{
|
||||
return str != null;
|
||||
}
|
||||
}
|
||||
public bool LoadArchive(GameType gameType, String archivePath, bool isContainer, bool canBeEmbedded)
|
||||
{
|
||||
return LoadArchive(gameType, archivePath, isContainer, canBeEmbedded, false);
|
||||
}
|
||||
|
||||
public bool LoadArchive(GameType gameType, String archivePath, bool isContainer, bool canBeEmbedded, bool isTheater)
|
||||
{
|
||||
// Doesn't really 'load' the archive, but instead registers it as known filename for this game type.
|
||||
// The actual loading won't happen until a Reset(...) is executed to specify the game to initialise.
|
||||
if (!gameFolders.TryGetValue(gameType, out string gamePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
List<MixInfo> archivesForGame;
|
||||
if (!gameArchives.TryGetValue(gameType, out archivesForGame))
|
||||
{
|
||||
archivesForGame = new List<MixInfo>();
|
||||
gameArchives[gameType] = archivesForGame;
|
||||
}
|
||||
// Not using hash map since order and iteration will be important.
|
||||
if (archivesForGame.FindIndex(info => String.Equals(info.Name, archivePath, StringComparison.InvariantCultureIgnoreCase)) == -1)
|
||||
{
|
||||
archivesForGame.Add(new MixInfo(archivePath, isContainer, canBeEmbedded, isTheater));
|
||||
}
|
||||
String fullPath = Path.Combine(applicationPath, gamePath, archivePath);
|
||||
// Mod paths might still add it, but this initial check is returned.
|
||||
return File.Exists(fullPath);
|
||||
}
|
||||
|
||||
public Stream OpenFile(String path)
|
||||
{
|
||||
// Game folders dictionary determines which games are "known" to the system.
|
||||
if (!gameFolders.TryGetValue(currentGameType, out string gamePath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// 1. Loose files in game files path
|
||||
string loosePath = Path.Combine(gamePath, path);
|
||||
if (File.Exists(loosePath))
|
||||
{
|
||||
return File.Open(loosePath, FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
// 2. Loose files in mod path
|
||||
if (ExpandModPaths != null && ExpandModPaths.Length > 0)
|
||||
{
|
||||
foreach (string modFilePath in ExpandModPaths)
|
||||
{
|
||||
string modPath = Path.Combine(modFilePath, "ccdata", path);
|
||||
if (File.Exists(modPath))
|
||||
{
|
||||
return File.Open(modPath, FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 3. Contained inside mix files. Note that this automatically takes mods and
|
||||
// embedded mix files into account, since they are loaded in the Reset function.
|
||||
foreach (MixInfo mixInfo in currentMixFileInfo)
|
||||
{
|
||||
if (currentMixFiles.TryGetValue(mixInfo.Name, out Mixfile archive))
|
||||
{
|
||||
Stream stream = archive.OpenFile(path);
|
||||
if (stream != null)
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Reset(GameType gameType, TheaterType theater)
|
||||
{
|
||||
String theaterMixFile = theater == null ? null : theater.ClassicTileset + ".mix";
|
||||
// Clean up previously loaded files.
|
||||
foreach (Mixfile oldMixFile in currentMixFiles.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
oldMixFile.Dispose();
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
}
|
||||
currentMixFiles.Clear();
|
||||
currentMixFileInfo.Clear();
|
||||
currentMixNames = new List<string>();
|
||||
this.currentGameType = GameType.None;
|
||||
// Load current files
|
||||
// Game folders dictionary determines which games are "known" to the system.
|
||||
if (!gameFolders.TryGetValue(gameType, out string gamePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<MixInfo> newMixFileInfo = gameArchives.Where(kv => kv.Key == gameType).SelectMany(kv => kv.Value).ToList();
|
||||
Dictionary<string, Mixfile> newMixFiles = new Dictionary<string, Mixfile>();
|
||||
if (ExpandModPaths != null && ExpandModPaths.Length > 0)
|
||||
{
|
||||
// In each mod folder, try to read all mix files.
|
||||
foreach (string modPath in ExpandModPaths)
|
||||
{
|
||||
foreach (MixInfo mixInfo in newMixFileInfo)
|
||||
{
|
||||
if (mixInfo.IsTheater && !String.Equals(theaterMixFile, mixInfo.Name, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String mixPath = Path.Combine(modPath, "ccdata");
|
||||
// This automatically excludes already-loaded files.
|
||||
this.AddMixFileIfPresent(newMixFiles, newMixFileInfo, mixInfo, mixPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (MixInfo mixInfo in newMixFileInfo)
|
||||
{
|
||||
if (mixInfo.IsTheater && !String.Equals(theaterMixFile, mixInfo.Name, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String mixPath = Path.Combine(gamePath, "ccdata");
|
||||
// This automatically excludes already-loaded files.
|
||||
this.AddMixFileIfPresent(newMixFiles, newMixFileInfo, mixInfo, mixPath);
|
||||
}
|
||||
this.currentGameType = gameType;
|
||||
currentMixFiles.Clear();
|
||||
currentMixFiles.Union(newMixFiles);
|
||||
currentMixFileInfo.Clear();
|
||||
currentMixFileInfo.AddRange(newMixFileInfo);
|
||||
currentMixNames = currentMixFileInfo.Select(info => info.Name).ToList();
|
||||
}
|
||||
|
||||
private bool AddMixFileIfPresent(Dictionary<String, Mixfile> readMixFiles, List<MixInfo> readMixNames, MixInfo mixToAdd, string readFolder)
|
||||
{
|
||||
// 1. Look for file in given folder
|
||||
// 2. if 'canBeEmbedded', look for file inside archives inside mix files list
|
||||
// 3. If found in either, add to list of read mix files
|
||||
//if (File.Exists(newMixPath)) { }
|
||||
string mixName = mixToAdd.Name;
|
||||
if (readMixFiles.ContainsKey(mixName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string localPath = Path.Combine(readFolder, mixName);
|
||||
Mixfile mixFile = null;
|
||||
if (File.Exists(localPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
mixFile = new Mixfile(localPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (mixFile == null && mixToAdd.CanBeEmbedded)
|
||||
{
|
||||
foreach (MixInfo readArchive in readMixNames)
|
||||
{
|
||||
if (readArchive.IsContainer && readMixFiles.TryGetValue(readArchive.Name, out Mixfile container))
|
||||
{
|
||||
// Check if file exists
|
||||
if (container.GetFileInfo(mixName, out _, out _))
|
||||
{
|
||||
// Create as embedded mix file
|
||||
mixFile = new Mixfile(container, mixName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mixFile != null)
|
||||
{
|
||||
readMixFiles.Add(mixName, mixFile);
|
||||
}
|
||||
return mixFile != null;
|
||||
}
|
||||
|
||||
public IEnumerator<string> GetEnumerator()
|
||||
{
|
||||
return currentMixNames.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
//megafiles.ForEach(m => m.Dispose());
|
||||
}
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -28,9 +28,8 @@ using MobiusEditor.Model;
|
||||
|
||||
namespace MobiusEditor.Utility
|
||||
{
|
||||
public class TextureManager
|
||||
public class TextureManager: IDisposable
|
||||
{
|
||||
|
||||
private static string MissingTexture = "DATA\\ART\\TEXTURES\\SRGB\\COMMON\\MISC\\MISSING.TGA";
|
||||
private bool processedMissingTexture = false;
|
||||
|
||||
@ -43,26 +42,24 @@ namespace MobiusEditor.Utility
|
||||
this.megafileManager = megafileManager;
|
||||
}
|
||||
|
||||
public void Reset(GameType gameType, TheaterType theater)
|
||||
public void Reset()
|
||||
{
|
||||
Bitmap[] cachedImages = cachedTextures.Values.ToArray();
|
||||
cachedTextures.Clear();
|
||||
// Bitmaps need to be specifically disposed.
|
||||
for (int i = 0; i < cachedImages.Length; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
cachedImages[i].Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore.
|
||||
}
|
||||
try { cachedImages[i].Dispose(); }
|
||||
catch { /* Ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
public (Bitmap, Rectangle) GetTexture(string filename, ITeamColor teamColor, bool generateFallback)
|
||||
{
|
||||
if (disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException("TextureManager");
|
||||
}
|
||||
if (!cachedTextures.ContainsKey(filename) && generateFallback)
|
||||
{
|
||||
(Bitmap bm, Rectangle bounds) = GetDummyImage();
|
||||
@ -332,5 +329,26 @@ namespace MobiusEditor.Utility
|
||||
}
|
||||
return (bm, r);
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@
|
||||
// GNU General Public License along with permitted additional restrictions
|
||||
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
|
||||
using MobiusEditor.Interface;
|
||||
using MobiusEditor.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -20,7 +22,7 @@ using System.Xml;
|
||||
|
||||
namespace MobiusEditor.Utility
|
||||
{
|
||||
public class TilesetManager
|
||||
public class TilesetManager: ITilesetManager, IDisposable
|
||||
{
|
||||
private readonly Dictionary<string, Tileset> tilesets = new Dictionary<string, Tileset>();
|
||||
|
||||
@ -28,6 +30,9 @@ namespace MobiusEditor.Utility
|
||||
private readonly TextureManager textureManager;
|
||||
private readonly string xmlPath;
|
||||
private readonly string texturesPath;
|
||||
private TheaterType theater;
|
||||
|
||||
public TextureManager TextureManager { get { return textureManager; } }
|
||||
|
||||
public TilesetManager(IArchiveManager megafileManager, TextureManager textureManager, string xmlPath, string texturesPath)
|
||||
{
|
||||
@ -77,22 +82,33 @@ namespace MobiusEditor.Utility
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
public void Reset(TheaterType theater)
|
||||
{
|
||||
this.textureManager.Reset();
|
||||
foreach (var item in tilesets)
|
||||
{
|
||||
item.Value.Reset();
|
||||
}
|
||||
LoadXmlfiles();
|
||||
this.theater = theater;
|
||||
}
|
||||
|
||||
public bool GetTeamColorTileData(IEnumerable<string> searchTilesets, string name, int shape, ITeamColor teamColor, out int fps, out Tile[] tiles, bool generateFallback, bool onlyIfDefined)
|
||||
private bool GetTeamColorTileData(string name, int shape, ITeamColor teamColor, out int fps, out Tile[] tiles, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
if (disposedValue)
|
||||
{
|
||||
throw new ObjectDisposedException("TilesetManager");
|
||||
}
|
||||
fps = 0;
|
||||
tiles = null;
|
||||
Tileset first = null;
|
||||
// Tilesets are now searched in the given order, allowing accurate defining of main tilesets and fallback tilesets.
|
||||
//foreach (var tileset in tilesets.Join(searchTilesets, x => x.Key, y => y, (x, y) => x.Value))
|
||||
if (this.theater == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IEnumerable<string> searchTilesets = this.theater.Tilesets;
|
||||
foreach (string searchTileset in searchTilesets)
|
||||
{
|
||||
if (!tilesets.ContainsKey(searchTileset))
|
||||
@ -111,7 +127,8 @@ namespace MobiusEditor.Utility
|
||||
// Tile found, but contains no data. Re-fetch with dummy generation.
|
||||
if (tileset.GetTileData(name, shape, teamColor, out fps, out tiles, true))
|
||||
{
|
||||
return true;
|
||||
// Signal in return value that dummy was generated.
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -121,85 +138,47 @@ namespace MobiusEditor.Utility
|
||||
// If the tile is not defined at all, and onlyifdefined is not enabled, make a dummy entry anyway.
|
||||
if (!onlyIfDefined && generateFallback && first != null && first.GetTileData(name, shape, teamColor, out fps, out tiles, true))
|
||||
{
|
||||
return true;
|
||||
// Signal in return value that dummy was generated.
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool GetTeamColorTileData(IEnumerable<string> searchTilesets, string name, int shape, ITeamColor teamColor, out int fps, out Tile tile, bool generateFallback, bool onlyIfDefined)
|
||||
private bool GetTeamColorTileData(string name, int shape, ITeamColor teamColor, out int fps, out Tile tile, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
tile = null;
|
||||
if (!GetTeamColorTileData(searchTilesets, name, shape, teamColor, out fps, out Tile[] tiles, generateFallback, onlyIfDefined))
|
||||
bool success = GetTeamColorTileData(name, shape, teamColor, out fps, out Tile[] tiles, generateFallback, onlyIfDefined);
|
||||
tile = tiles == null ? null : tiles[0];
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool GetTeamColorTileData(string name, int shape, ITeamColor teamColor, out Tile tile, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
return GetTeamColorTileData(name, shape, teamColor, out int fps, out tile, generateFallback, onlyIfDefined);
|
||||
}
|
||||
|
||||
public bool GetTeamColorTileData(string name, int shape, ITeamColor teamColor, out Tile tile)
|
||||
{
|
||||
return GetTeamColorTileData(name, shape, teamColor, out int fps, out tile, false, false);
|
||||
}
|
||||
|
||||
public bool GetTileData(string name, int shape, out Tile tile, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
return GetTeamColorTileData(name, shape, null, out tile, generateFallback, onlyIfDefined);
|
||||
}
|
||||
|
||||
public bool GetTileData(string name, int shape, out Tile tile)
|
||||
{
|
||||
return GetTeamColorTileData(name, shape, null, out tile, false, false);
|
||||
}
|
||||
|
||||
public int GetTileDataLength(string name)
|
||||
{
|
||||
if (this.theater == null)
|
||||
{
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
tile = tiles[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GetTeamColorTileData(IEnumerable<string> searchTilesets, string name, int shape, ITeamColor teamColor, out Tile[] tiles, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, teamColor, out int fps, out tiles, generateFallback, onlyIfDefined);
|
||||
}
|
||||
|
||||
public bool GetTeamColorTileData(IEnumerable<string> searchTilesets, string name, int shape, ITeamColor teamColor, out Tile[] tiles)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, teamColor, out int fps, out tiles, false, false);
|
||||
}
|
||||
|
||||
public bool GetTeamColorTileData(IEnumerable<string> searchTilesets, string name, int shape, ITeamColor teamColor, out Tile tile, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, teamColor, out int fps, out tile, generateFallback, onlyIfDefined);
|
||||
}
|
||||
|
||||
public bool GetTeamColorTileData(IEnumerable<string> searchTilesets, string name, int shape, ITeamColor teamColor, out Tile tile)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, teamColor, out int fps, out tile, false, false);
|
||||
}
|
||||
|
||||
public bool GetTileData(IEnumerable<string> searchTilesets, string name, int shape, out int fps, out Tile[] tiles, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, null, out fps, out tiles, generateFallback, onlyIfDefined);
|
||||
}
|
||||
|
||||
public bool GetTileData(IEnumerable<string> searchTilesets, string name, int shape, out int fps, out Tile[] tiles)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, null, out fps, out tiles, false, false);
|
||||
}
|
||||
|
||||
public bool GetTileData(IEnumerable<string> searchTilesets, string name, int shape, out int fps, out Tile tile, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, null, out fps, out tile, generateFallback, onlyIfDefined);
|
||||
}
|
||||
|
||||
public bool GetTileData(IEnumerable<string> searchTilesets, string name, int shape, out int fps, out Tile tile)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, null, out fps, out tile, false, false);
|
||||
}
|
||||
|
||||
public bool GetTileData(IEnumerable<string> searchTilesets, string name, int shape, out Tile[] tiles, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, null, out tiles, generateFallback, onlyIfDefined);
|
||||
}
|
||||
|
||||
public bool GetTileData(IEnumerable<string> searchTilesets, string name, int shape, out Tile[] tiles)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, null, out tiles, false, false);
|
||||
}
|
||||
|
||||
public bool GetTileData(IEnumerable<string> searchTilesets, string name, int shape, out Tile tile, bool generateFallback, bool onlyIfDefined)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, null, out tile, generateFallback, onlyIfDefined);
|
||||
}
|
||||
|
||||
public bool GetTileData(IEnumerable<string> searchTilesets, string name, int shape, out Tile tile)
|
||||
{
|
||||
return GetTeamColorTileData(searchTilesets, name, shape, null, out tile, false, false);
|
||||
}
|
||||
|
||||
public int GetTileDataLength(IEnumerable<string> searchTilesets, string name)
|
||||
{
|
||||
foreach (var tileset in tilesets.Join(searchTilesets, x => x.Key, y => y, (x, y) => x.Value))
|
||||
IEnumerable<string> searchTilesets = this.theater.Tilesets;
|
||||
foreach (Tileset tileset in tilesets.Join(searchTilesets, x => x.Key, y => y, (x, y) => x.Value))
|
||||
{
|
||||
int frames = tileset.GetTileDataLength(name);
|
||||
if (frames != -1)
|
||||
@ -209,5 +188,27 @@ namespace MobiusEditor.Utility
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
TextureManager.Dispose();
|
||||
this.Reset(null);
|
||||
}
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user