Initial commit

This commit is contained in:
Tim Sarbin 2018-11-22 00:18:42 -05:00
commit 4d124ad959
54 changed files with 10522 additions and 0 deletions

334
.gitignore vendored Normal file
View File

@ -0,0 +1,334 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Attributes
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class SceneAttribute : Attribute
{
public SceneAttribute(string sceneName) => SceneName = sceneName;
public string SceneName { get; }
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Enums
{
public enum eMPQFormatVersion
{
Format1,
Format2,
Format3,
Format4
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Interfaces
{
public interface IGameEngine : IDisposable
{
void Run();
}
}

View File

@ -0,0 +1,14 @@
using OpenDiablo2.Common.Models;
using System;
using System.Collections.Generic;
using System.IO;
namespace OpenDiablo2.Common.Interfaces
{
public interface IMPQProvider
{
IEnumerable<MPQ> GetMPQs();
IEnumerable<IEnumerable<String>> GetTextFile(string fileName);
Stream GetStream(string fileName);
}
}

View File

@ -0,0 +1,10 @@
using OpenDiablo2.Common.Models;
using System.Collections.Generic;
namespace OpenDiablo2.Common.Interfaces
{
public interface IPaletteProvider
{
Dictionary<string, Palette> PaletteTable { get; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Interfaces
{
public interface IRenderTarget
{
void Draw(ISprite sprite);
}
}

View File

@ -0,0 +1,20 @@
using OpenDiablo2.Common.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Interfaces
{
public interface IRenderWindow : IDisposable
{
bool IsRunning { get; }
void Update();
void Clear();
void Sync();
ISprite LoadSprite(ImageSet source);
void Draw(ISprite sprite);
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Interfaces
{
public interface IScene : IDisposable
{
void Update(long ms);
void Render();
}
}

View File

@ -0,0 +1,15 @@
using OpenDiablo2.Common.Models;
using System;
using System.Drawing;
namespace OpenDiablo2.Common.Interfaces
{
public interface ISprite : IDisposable
{
Point Location { get; set; }
Size FrameSize { get; set; }
int Frame { get; set; }
int TotalFrames { get; }
Palette CurrentPalette { get; set; }
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.IO;
namespace OpenDiablo2.Common.Models
{
/// <summary>
/// A utility class for reading groups of bits from a stream
/// </summary>
internal class BitStream
{
private Stream _baseStream;
private int _current;
private int _bitCount;
public BitStream(Stream sourceStream)
{
_baseStream = sourceStream;
}
public int ReadBits(int bitCount)
{
if (bitCount > 16)
throw new ArgumentOutOfRangeException("BitCount", "Maximum BitCount is 16");
if (EnsureBits(bitCount) == false) return -1;
int result = _current & (0xffff >> (16 - bitCount));
WasteBits(bitCount);
return result;
}
public int PeekByte()
{
if (EnsureBits(8) == false) return -1;
return _current & 0xff;
}
public bool EnsureBits(int bitCount)
{
if (bitCount <= _bitCount) return true;
if (_baseStream.Position >= _baseStream.Length) return false;
int nextvalue = _baseStream.ReadByte();
_current |= nextvalue << _bitCount;
_bitCount += 8;
return true;
}
private bool WasteBits(int bitCount)
{
_current >>= bitCount;
_bitCount -= bitCount;
return true;
}
}
}

View File

@ -0,0 +1,7 @@
namespace OpenDiablo2.Common.Models
{
public sealed class GlobalConfiguration
{
public string BaseDataPath { get; set; }
}
}

View File

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Models
{
public class ImageFrame
{
public UInt32 Flip;
public UInt32 Width;
public UInt32 Height;
public Int32 OffsetX;
public Int32 OffsetY; // from bottom border, not up
public UInt32 Unknown;
public UInt32 NextBlock;
public UInt32 Length;
public Int16[,] ImageData;
public Color GetColor(int x, int y, Palette palette)
{
var index = ImageData[x, y];
if (index == -1)
return Color.Transparent;
var color = palette.Colors[(int)index];
return Color.FromArgb(255, color.R, color.G, color.B);
}
}
public sealed class ImageSet
{
private UInt32 version;
private UInt32 unknown1; // 01 00 00 00 ???
private UInt32 unknown2; // 00 00 00 00 ???
private UInt32 termination; // EE EE EE EE or CD CD CD CD ???
private UInt32[] framePointers;
public ImageFrame[] Frames { get; private set; }
public UInt32 Directions { get; private set; }
public UInt32 FramesPerDirection { get; private set; }
public static ImageSet LoadFromStream(Stream stream)
{
var br = new BinaryReader(stream);
var result = new ImageSet
{
version = br.ReadUInt32(),
unknown1 = br.ReadUInt32(),
unknown2 = br.ReadUInt32(),
termination = br.ReadUInt32(),
Directions = br.ReadUInt32(),
FramesPerDirection = br.ReadUInt32()
};
result.framePointers = new uint[result.Directions * result.FramesPerDirection];
for (var i = 0; i < result.Directions * result.FramesPerDirection; i++)
result.framePointers[i] = br.ReadUInt32();
result.Frames = new ImageFrame[result.Directions * result.FramesPerDirection];
for (var i = 0; i < result.Directions * result.FramesPerDirection; i++)
{
stream.Seek(result.framePointers[i], SeekOrigin.Begin);
var frame = new ImageFrame
{
Flip = br.ReadUInt32(),
Width = br.ReadUInt32(),
Height = br.ReadUInt32(),
OffsetX = br.ReadInt32(),
OffsetY = br.ReadInt32(),
Unknown = br.ReadUInt32(),
NextBlock = br.ReadUInt32(),
Length = br.ReadUInt32()
};
frame.ImageData = new Int16[frame.Width, frame.Height];
for (int ty = 0; ty < frame.Height; ty++)
for (int tx = 0; tx < frame.Width; tx++)
frame.ImageData[tx, ty] = -1;
int x = 0;
int y = (int)frame.Height - 1;
while (true)
{
var b = br.ReadByte();
if (b == 0x80)
{
if (y == 0)
break;
y--;
x = 0;
continue;
}
if ((b & 0x80) > 0)
{
var transparentPixelsToWrite = b & 0x7F;
for (int p = 0; p < transparentPixelsToWrite; p++)
{
frame.ImageData[x++, y] = -1;
}
continue;
}
for (int p = 0; p < b; p++)
{
frame.ImageData[x++, y] = br.ReadByte();
}
}
result.Frames[i] = frame;
}
return result;
}
}
}

View File

@ -0,0 +1,364 @@
using OpenDiablo2.Common.Enums;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Models
{
public sealed class MPQ : IDisposable
{
private const string HEADER_SIGNATURE = "MPQ\x1A";
private const string USERDATA_SIGNATURE = "MPQ\x1B";
private const string LISTFILE_NAME = "(listfile)";
private const int MPQ_HASH_FILE_KEY = 3;
private const int MPQ_HASH_TABLE_OFFSET = 0;
private const int MPQ_HASH_NAME_A = 1;
private const int MPQ_HASH_NAME_B = 2;
private const UInt32 MPQ_HASH_ENTRY_EMPTY = 0xFFFFFFFF;
private const UInt32 MPQ_HASH_ENTRY_DELETED = 0xFFFFFFFE;
internal struct HeaderRecord
{
public UInt32 HeaderSize;
public UInt32 ArchiveSize;
public UInt16 FormatVersion;
public Byte BlockSize;
public UInt32 HashTablePos;
public UInt32 BlockTablePos;
public UInt32 HashTableSize;
public UInt32 BlockTableSize;
// Other properties are for >0 MPQ version
}
[Flags]
internal enum eBlockRecordFlags : UInt32
{
IsFile = 0x80000000, // Block is a file, and follows the file data format; otherwise, block is free space or unused. If the block is not a file, all other flags should be cleared, and FileSize should be 0.
SingleUnit = 0x01000000, // File is stored as a single unit, rather than split into sectors.
KeyAdjusted = 0x00020000, // The file's encryption key is adjusted by the block offset and file size (explained in detail in the File Data section). File must be encrypted.
IsEncrypted = 0x00010000, // File is encrypted.
IsCompressed = 0x00000200, // File is compressed. File cannot be imploded.
IsImploded = 0x00000100 // File is imploded. File cannot be compressed.
}
internal struct BlockRecord
{
public UInt32 BlockOffset;
public UInt32 BlockSize;
public UInt32 FileSize;
public UInt32 Flags;
public uint EncryptionSeed { get; set; }
public string FileName { get; internal set; }
public bool IsFile => (Flags & (UInt32)eBlockRecordFlags.IsFile) != 0;
public bool SingleUnit => (Flags & (UInt32)eBlockRecordFlags.SingleUnit) != 0;
public bool KeyAdjusted => (Flags & (UInt32)eBlockRecordFlags.KeyAdjusted) != 0;
public bool IsEncrypted => (Flags & (UInt32)eBlockRecordFlags.IsEncrypted) != 0;
public bool IsCompressed => (Flags & (UInt32)eBlockRecordFlags.IsCompressed) != 0;
public bool IsImploded => (Flags & (UInt32)eBlockRecordFlags.IsImploded) != 0;
}
internal struct HashRecord
{
public UInt32 FilePathHashA;
public UInt32 FilePathHashB;
public UInt16 Language;
public UInt16 Platform;
public UInt32 FileBlockIndex;
}
internal static UInt32[] cryptTable = new UInt32[0x500];
internal HeaderRecord Header;
private List<BlockRecord> blockTable = new List<BlockRecord>();
private List<HashRecord> hashTable = new List<HashRecord>();
internal Stream fileStream;
public UInt16 LanguageId = 0;
public const byte Platform = 0;
public string Path { get; private set; }
public eMPQFormatVersion FormatVersion => (eMPQFormatVersion)Header.FormatVersion;
public List<string> Files => GetFilePaths();
private List<string> GetFilePaths()
{
var stream = OpenFile("(listfile)");
if (stream == null)
{
return new List<string>();
}
var sr = new StreamReader(stream);
var text = sr.ReadToEnd();
return text.Split('\n').Where(x => !String.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToList();
}
static MPQ()
{
InitializeCryptTable();
}
public MPQ(string path)
{
this.Path = path;
fileStream = new FileStream(path, FileMode.Open);
using (var br = new BinaryReader(fileStream, Encoding.Default, true))
{
var header = Encoding.ASCII.GetString(br.ReadBytes(4));
if (header != HEADER_SIGNATURE)
throw new ApplicationException($"Unknown header signature '{header}' detected while processing '{Path}'!");
ParseMPQHeader(br);
}
}
private static void InitializeCryptTable()
{
UInt32 seed = 0x00100001;
UInt32 index1 = 0;
UInt32 index2 = 0;
int i;
for (index1 = 0; index1 < 0x100; index1++)
{
for (index2 = index1, i = 0; i < 5; i++, index2 += 0x100)
{
seed = (seed * 125 + 3) % 0x2AAAAB;
var temp = (seed & 0xFFFF) << 0x10;
seed = (seed * 125 + 3) % 0x2AAAAB;
cryptTable[index2] = (temp | (seed & 0xFFFF));
}
}
}
internal static void DecryptBlock(uint[] data, uint seed1)
{
uint seed2 = 0xeeeeeeee;
for (int i = 0; i < data.Length; i++)
{
seed2 += cryptTable[0x400 + (seed1 & 0xff)];
uint result = data[i];
result ^= seed1 + seed2;
seed1 = ((~seed1 << 21) + 0x11111111) | (seed1 >> 11);
seed2 = result + seed2 + (seed2 << 5) + 3;
data[i] = result;
}
}
internal static void DecryptBlock(byte[] data, uint seed1)
{
uint seed2 = 0xeeeeeeee;
// NB: If the block is not an even multiple of 4,
// the remainder is not encrypted
for (int i = 0; i < data.Length - 3; i += 4)
{
seed2 += cryptTable[(int)(0x400 + (seed1 & 0xff))];
uint result = BitConverter.ToUInt32(data, i);
result ^= (seed1 + seed2);
seed1 = ((~seed1 << 21) + 0x11111111) | (seed1 >> 11);
seed2 = result + seed2 + (seed2 << 5) + 3;
data[i + 0] = ((byte)(result & 0xff));
data[i + 1] = ((byte)((result >> 8) & 0xff));
data[i + 2] = ((byte)((result >> 16) & 0xff));
data[i + 3] = ((byte)((result >> 24) & 0xff));
}
}
private void ParseMPQHeader(BinaryReader br)
{
Header = new HeaderRecord
{
HeaderSize = br.ReadUInt32(),
ArchiveSize = br.ReadUInt32(),
FormatVersion = br.ReadUInt16(),
BlockSize = (byte)br.ReadInt16(),
HashTablePos = br.ReadUInt32(),
BlockTablePos = br.ReadUInt32(),
HashTableSize = br.ReadUInt32(),
BlockTableSize = br.ReadUInt32()
};
if (FormatVersion != eMPQFormatVersion.Format1)
throw new ApplicationException($"Unsupported MPQ format version of {Header.FormatVersion} detected for '{Path}'!");
if (br.BaseStream.Position != Header.HeaderSize)
throw new ApplicationException($"Invalid header size detected for '{Path}'. Expected to be at offset {Header.HeaderSize} but we are at offset {br.BaseStream.Position} instead!");
br.BaseStream.Seek(Header.BlockTablePos, SeekOrigin.Begin);
// Process the block table
var bData = br.ReadBytes((int)(16 * Header.BlockTableSize));
DecryptBlock(bData, HashString("(block table)", MPQ_HASH_FILE_KEY));
using (var ms = new MemoryStream(bData))
using (var dr = new BinaryReader(new MemoryStream(bData)))
for (var index = 0; index < Header.BlockTableSize; index++)
{
blockTable.Add(new BlockRecord
{
BlockOffset = dr.ReadUInt32(),
BlockSize = dr.ReadUInt32(),
FileSize = dr.ReadUInt32(),
Flags = dr.ReadUInt32()
});
}
// Process the hash table
br.BaseStream.Seek(Header.HashTablePos, SeekOrigin.Begin);
bData = br.ReadBytes((int)(16 * Header.HashTableSize));
DecryptBlock(bData, HashString("(hash table)", MPQ_HASH_FILE_KEY));
using (var ms = new MemoryStream(bData))
using (var dr = new BinaryReader(new MemoryStream(bData)))
for (var index = 0; index < Header.HashTableSize; index++)
{
hashTable.Add(new HashRecord
{
FilePathHashA = dr.ReadUInt32(),
FilePathHashB = dr.ReadUInt32(),
Language = dr.ReadUInt16(),
Platform = dr.ReadUInt16(),
FileBlockIndex = dr.ReadUInt32()
});
}
}
private uint CalculateEncryptionSeed(BlockRecord record)
{
if (record.FileName == null) return 0;
uint seed = HashString(System.IO.Path.GetFileName(record.FileName), MPQ_HASH_FILE_KEY);
if (record.KeyAdjusted)
seed = (seed + record.BlockOffset) ^ record.FileSize;
return seed;
}
private static UInt32 HashString(string inputString, UInt32 hashType)
{
if (hashType > MPQ_HASH_FILE_KEY)
throw new ApplicationException($"Unknown hash type {hashType} for input string {inputString}");
UInt32 seed1 = 0x7FED7FED;
UInt32 seed2 = 0xEEEEEEEE;
foreach (var ch in inputString)
{
var chInt = (UInt32)char.ToUpper(ch);
seed1 = cryptTable[(hashType * 0x100) + chInt] ^ (seed1 + seed2);
seed2 = chInt + seed1 + seed2 + (seed2 << 5) + 3;
}
return seed1;
}
private static UInt32 ComputeFileKey(string filePath, BlockRecord blockRecord, UInt32 archiveOffset)
{
var fileName = filePath.Split('\\').Last();
// Hash the name to get the base key
var fileKey = HashString(fileName, MPQ_HASH_FILE_KEY);
// Offset-adjust the key if necessary
if (blockRecord.KeyAdjusted)
fileKey = (fileKey + blockRecord.BlockOffset) ^ blockRecord.FileSize;
return fileKey;
}
private bool FindFileInHashTable(string filePath, out UInt32 fileHashEntry)
{
fileHashEntry = 0;
// Find the home entry in the hash table for the file
UInt32 initEntry = HashString(filePath, MPQ_HASH_TABLE_OFFSET) & (UInt32)(Header.HashTableSize - 1);
// Is there anything there at all?
if (hashTable[(int)initEntry].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
return false;
// Compute the hashes to compare the hash table entry against
var nNameHashA = HashString(filePath, MPQ_HASH_NAME_A);
var nNameHashB = HashString(filePath, MPQ_HASH_NAME_B);
var iCurEntry = initEntry;
// Check each entry in the hash table till a termination point is reached
do
{
if (hashTable[(int)iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_DELETED)
{
if (hashTable[(int)iCurEntry].FilePathHashA == nNameHashA
&& hashTable[(int)iCurEntry].FilePathHashB == nNameHashB
&& hashTable[(int)iCurEntry].Language == LanguageId
&& hashTable[(int)iCurEntry].Platform == (UInt16)PlatformID.Win32S)
{
fileHashEntry = iCurEntry;
return true;
}
}
iCurEntry = (iCurEntry + 1) & (UInt32)(Header.HashTableSize - 1);
} while (iCurEntry != initEntry && hashTable[(int)iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_EMPTY);
return false;
}
private bool GetHashRecord(string fileName, out HashRecord hash)
{
uint index = HashString(fileName, 0);
index &= Header.HashTableSize - 1;
uint name1 = HashString(fileName, MPQ_HASH_NAME_A);
uint name2 = HashString(fileName, MPQ_HASH_NAME_B);
for (uint i = index; i < hashTable.Count(); ++i)
{
hash = hashTable[(int)i];
if (hash.FilePathHashA == name1 && hash.FilePathHashB == name2)
return true;
}
for (uint i = 0; i < index; i++)
{
hash = hashTable[(int)i];
if (hash.FilePathHashA == name1 && hash.FilePathHashB == name2)
return true;
}
hash = new HashRecord();
return false;
}
public MPQStream OpenFile(string filename)
{
HashRecord hash;
BlockRecord block;
if (!GetHashRecord(filename, out hash))
throw new FileNotFoundException("File not found: " + filename);
block = blockTable[(int)hash.FileBlockIndex];
block.FileName = filename.ToLower();
block.EncryptionSeed = CalculateEncryptionSeed(block);
return new MPQStream(this, block);
}
public void Dispose()
{
fileStream?.Dispose();
}
}
}

View File

@ -0,0 +1,404 @@
//
// MpqHuffman.cs
//
// Authors:
// Foole (fooleau@gmail.com)
//
// (C) 2006 Foole (fooleau@gmail.com)
// Based on code from StormLib by Ladislav Zezula and ShadowFlare
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Collections;
namespace OpenDiablo2.Common.Models
{
// A node which is both hierachcical (parent/child) and doubly linked (next/prev)
internal class LinkedNode
{
public int DecompressedValue;
public int Weight;
public LinkedNode Parent;
public LinkedNode Child0;
public LinkedNode Child1
{ get { return Child0.Prev; } }
public LinkedNode Next;
public LinkedNode Prev;
public LinkedNode(int decompVal, int weight)
{
DecompressedValue = decompVal;
this.Weight = weight;
}
// TODO: This would be more efficient as a member of the other class
// ie avoid the recursion
public LinkedNode Insert(LinkedNode other)
{
// 'Next' should have a lower weight
// we should return the lower weight
if (other.Weight <= Weight)
{
// insert before
if (Next != null)
{
Next.Prev = other;
other.Next = Next;
}
Next = other;
other.Prev = this;
return other;
}
else
{
if (Prev == null)
{
// Insert after
other.Prev = null;
Prev = other;
other.Next = this;
}
else
{
Prev.Insert(other);
}
}
return this;
}
}
/// <summary>
/// A decompressor for MPQ's huffman compression
/// </summary>
internal static class MpqHuffman
{
private static readonly byte[][] sPrime =
{
// Compression type 0
new byte[]
{
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
},
// Compression type 1
new byte[]
{
0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05,
0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02,
0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04,
0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02,
0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03,
0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B,
},
// Compression type 2
new byte[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01,
0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02,
0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01,
0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A,
0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01
},
// Compression type 3
new byte[]
{
0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03,
0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01,
0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03,
0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03,
0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01,
0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11,
},
// Compression type 4
new byte[]
{
0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17,
},
// Compression type 5
new byte[]
{
0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82,
0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37,
0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D,
0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18
},
// Compression type 6
new byte[]
{
0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7A, 0x46
},
// Compression type 7
new byte[]
{
0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x70, 0x6C
},
// Compression type 8
new byte[]
{
0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10,
0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11,
0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5F, 0x9E
}
};
public static MemoryStream Decompress(Stream data)
{
int comptype = data.ReadByte();
if (comptype == 0)
throw new NotImplementedException("Compression type 0 is not currently supported");
LinkedNode tail = BuildList(sPrime[comptype]);
LinkedNode head = BuildTree(tail);
MemoryStream outputstream = new MemoryStream();
BitStream bitstream = new BitStream(data);
int decoded;
do
{
LinkedNode node = Decode(bitstream, head);
decoded = node.DecompressedValue;
switch (decoded)
{
case 256:
break;
case 257:
int newvalue = bitstream.ReadBits(8);
outputstream.WriteByte((byte)newvalue);
tail = InsertNode(tail, newvalue);
break;
default:
outputstream.WriteByte((byte)decoded);
break;
}
} while (decoded != 256);
outputstream.Seek(0, SeekOrigin.Begin);
return outputstream;
}
private static LinkedNode Decode(BitStream input, LinkedNode head)
{
LinkedNode node = head;
while (node.Child0 != null)
{
int bit = input.ReadBits(1);
if (bit == -1)
throw new Exception("Unexpected end of file");
node = bit == 0 ? node.Child0 : node.Child1;
}
return node;
}
private static LinkedNode BuildList(byte[] primeData)
{
LinkedNode root;
root = new LinkedNode(256, 1);
root = root.Insert(new LinkedNode(257, 1));
for (int i = 0; i < primeData.Length; i++)
{
if (primeData[i] != 0)
root = root.Insert(new LinkedNode(i, primeData[i]));
}
return root;
}
private static LinkedNode BuildTree(LinkedNode tail)
{
LinkedNode current = tail;
while (current != null)
{
LinkedNode child0 = current;
LinkedNode child1 = current.Prev;
if (child1 == null) break;
LinkedNode parent = new LinkedNode(0, child0.Weight + child1.Weight);
parent.Child0 = child0;
child0.Parent = parent;
child1.Parent = parent;
current.Insert(parent);
current = current.Prev.Prev;
}
return current;
}
private static LinkedNode InsertNode(LinkedNode tail, int decomp)
{
LinkedNode parent = tail;
LinkedNode result = tail.Prev; // This will be the new tail after the tree is updated
LinkedNode temp = new LinkedNode(parent.DecompressedValue, parent.Weight);
temp.Parent = parent;
LinkedNode newnode = new LinkedNode(decomp, 0);
newnode.Parent = parent;
parent.Child0 = newnode;
tail.Next = temp;
temp.Prev = tail;
newnode.Prev = temp;
temp.Next = newnode;
AdjustTree(newnode);
// TODO: For compression type 0, AdjustTree should be called
// once for every value written and only once here
AdjustTree(newnode);
return result;
}
// This increases the weight of the new node and its antecendants
// and adjusts the tree if needed
private static void AdjustTree(LinkedNode newNode)
{
LinkedNode current = newNode;
while (current != null)
{
current.Weight++;
LinkedNode insertpoint;
LinkedNode prev;
// Go backwards thru the list looking for the insertion point
insertpoint = current;
while (true)
{
prev = insertpoint.Prev;
if (prev == null) break;
if (prev.Weight >= current.Weight) break;
insertpoint = prev;
}
// No insertion point found
if (insertpoint == current)
{
current = current.Parent;
continue;
}
// The following code basicly swaps insertpoint with current
// remove insert point
if (insertpoint.Prev != null) insertpoint.Prev.Next = insertpoint.Next;
insertpoint.Next.Prev = insertpoint.Prev;
// Insert insertpoint after current
insertpoint.Next = current.Next;
insertpoint.Prev = current;
if (current.Next != null) current.Next.Prev = insertpoint;
current.Next = insertpoint;
// remove current
current.Prev.Next = current.Next;
current.Next.Prev = current.Prev;
// insert current after prev
LinkedNode temp = prev.Next;
current.Next = temp;
current.Prev = prev;
temp.Prev = current;
prev.Next = current;
// Set up parent/child links
LinkedNode currentparent = current.Parent;
LinkedNode insertparent = insertpoint.Parent;
if (currentparent.Child0 == current)
currentparent.Child0 = insertpoint;
if (currentparent != insertparent && insertparent.Child0 == insertpoint)
insertparent.Child0 = current;
current.Parent = insertparent;
insertpoint.Parent = currentparent;
current = current.Parent;
}
}
}
}

View File

@ -0,0 +1,374 @@
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static OpenDiablo2.Common.Models.MPQ;
namespace OpenDiablo2.Common.Models
{
public sealed class MPQStream : Stream
{
private readonly MPQ mpq;
private readonly BlockRecord blockRecord;
private uint[] blockPositions;
private long position;
private byte[] _currentData;
private int _currentBlockIndex = -1;
private int blockSize;
internal MPQStream(MPQ mpq, BlockRecord blockRecord)
{
this.mpq = mpq;
this.blockRecord = blockRecord;
this.blockSize = 0x200 << mpq.Header.BlockSize;
if (blockRecord.IsCompressed && !blockRecord.SingleUnit)
LoadBlockOffsets();
}
private void LoadBlockOffsets()
{
int blockposcount = (int)((blockRecord.FileSize + blockSize - 1) / blockSize) + 1;
blockPositions = new uint[blockposcount];
lock (mpq.fileStream)
{
mpq.fileStream.Seek(blockRecord.BlockOffset, SeekOrigin.Begin);
var br = new BinaryReader(mpq.fileStream);
for (int i = 0; i < blockposcount; i++)
blockPositions[i] = br.ReadUInt32();
}
uint blockpossize = (uint)blockposcount * 4;
if (blockRecord.IsEncrypted)
{
MPQ.DecryptBlock(blockPositions, blockRecord.EncryptionSeed - 1);
if (blockPositions[0] != blockpossize)
throw new ApplicationException("Decryption failed");
if (blockPositions[1] > blockSize + blockpossize)
throw new ApplicationException("Decryption failed");
}
}
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => false;
public override long Length => blockRecord.FileSize;
public override long Position
{
get => position;
set => Seek(value, SeekOrigin.Begin);
}
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count)
{
if (blockRecord.SingleUnit)
return ReadInternalSingleUnit(buffer, offset, count);
int toread = count;
int readtotal = 0;
while (toread > 0)
{
int read = ReadInternal(buffer, offset, toread);
if (read == 0) break;
readtotal += read;
offset += read;
toread -= read;
}
return readtotal;
}
private int ReadInternalSingleUnit(byte[] buffer, int offset, int count)
{
if (position >= Length)
return 0;
if (_currentData == null)
LoadSingleUnit();
int bytestocopy = Math.Min((int)(_currentData.Length - position), count);
Array.Copy(_currentData, position, buffer, offset, bytestocopy);
position += bytestocopy;
return bytestocopy;
}
private void LoadSingleUnit()
{
// Read the entire file into memory
byte[] filedata = new byte[blockSize];
lock (mpq.fileStream)
{
mpq.fileStream.Seek(mpq.Header.HeaderSize + blockRecord.BlockOffset, SeekOrigin.Begin);
int read = mpq.fileStream.Read(filedata, 0, filedata.Length);
if (read != filedata.Length)
throw new ApplicationException("Insufficient data or invalid data length");
}
if (blockSize == blockRecord.FileSize)
_currentData = filedata;
else
_currentData = DecompressMulti(filedata, (int)blockRecord.FileSize);
}
private int ReadInternal(byte[] buffer, int offset, int count)
{
// OW: avoid reading past the contents of the file
if (position >= Length)
return 0;
BufferData();
int localposition = (int)(position % blockSize);
int bytestocopy = Math.Min(_currentData.Length - localposition, count);
if (bytestocopy <= 0) return 0;
Array.Copy(_currentData, localposition, buffer, offset, bytestocopy);
position += bytestocopy;
return bytestocopy;
}
public override int ReadByte()
{
if (position >= Length) return -1;
if (blockRecord.SingleUnit)
return ReadByteSingleUnit();
BufferData();
int localposition = (int)(position % blockSize);
position++;
return _currentData[localposition];
}
private int ReadByteSingleUnit()
{
if (_currentData == null)
LoadSingleUnit();
return _currentData[position++];
}
private void BufferData()
{
int requiredblock = (int)(position / blockSize);
if (requiredblock != _currentBlockIndex)
{
int expectedlength = (int)Math.Min(Length - (requiredblock * blockSize), blockSize);
_currentData = LoadBlock(requiredblock, expectedlength);
_currentBlockIndex = requiredblock;
}
}
private byte[] LoadBlock(int blockIndex, int expectedLength)
{
uint offset;
int toread;
uint encryptionseed;
if (blockRecord.IsCompressed)
{
offset = blockPositions[blockIndex];
toread = (int)(blockPositions[blockIndex + 1] - offset);
}
else
{
offset = (uint)(blockIndex * blockSize);
toread = expectedLength;
}
offset += blockRecord.BlockOffset;
byte[] data = new byte[toread];
lock (mpq.fileStream)
{
mpq.fileStream.Seek(offset, SeekOrigin.Begin);
int read = mpq.fileStream.Read(data, 0, toread);
if (read != toread)
throw new ApplicationException("Insufficient data or invalid data length");
}
if (blockRecord.IsEncrypted && blockRecord.FileSize > 3)
{
if (blockRecord.EncryptionSeed == 0)
throw new ApplicationException("Unable to determine encryption key");
encryptionseed = (uint)(blockIndex + blockRecord.EncryptionSeed);
MPQ.DecryptBlock(data, encryptionseed);
}
if (blockRecord.IsCompressed && (toread != expectedLength))
{
//if ((blockRecord.Flags & MpqFileFlags.CompressedMulti) != 0)
if (!blockRecord.SingleUnit)
data = DecompressMulti(data, expectedLength);
else
data = PKDecompress(new MemoryStream(data), expectedLength);
}
return data;
}
private static byte[] DecompressMulti(byte[] input, int outputLength)
{
Stream sinput = new MemoryStream(input);
byte comptype = (byte)sinput.ReadByte();
switch (comptype)
{
case 1: // Huffman
return MpqHuffman.Decompress(sinput).ToArray();
case 2: // ZLib/Deflate
return ZlibDecompress(sinput, outputLength);
case 8: // PKLib/Impode
return PKDecompress(sinput, outputLength);
case 0x10: // BZip2
return BZip2Decompress(sinput, outputLength);
case 0x80: // IMA ADPCM Stereo
return MpqWavCompression.Decompress(sinput, 2);
case 0x40: // IMA ADPCM Mono
return MpqWavCompression.Decompress(sinput, 1);
case 0x12:
throw new ApplicationException("LZMA compression is not yet supported");
// Combos
case 0x22:
// TODO: sparse then zlib
throw new ApplicationException("Sparse compression + Deflate compression is not yet supported");
case 0x30:
// TODO: sparse then bzip2
throw new ApplicationException("Sparse compression + BZip2 compression is not yet supported");
case 0x41:
sinput = MpqHuffman.Decompress(sinput);
return MpqWavCompression.Decompress(sinput, 1);
case 0x48:
{
byte[] result = PKDecompress(sinput, outputLength);
return MpqWavCompression.Decompress(new MemoryStream(result), 1);
}
case 0x81:
sinput = MpqHuffman.Decompress(sinput);
return MpqWavCompression.Decompress(sinput, 2);
case 0x88:
{
byte[] result = PKDecompress(sinput, outputLength);
return MpqWavCompression.Decompress(new MemoryStream(result), 2);
}
default:
throw new ApplicationException("Compression is not yet supported: 0x" + comptype.ToString("X"));
}
}
private static byte[] BZip2Decompress(Stream data, int expectedLength)
{
using (var output = new MemoryStream(expectedLength))
{
new Ionic.BZip2.BZip2InputStream(data)
.CopyTo(output);
return output.ToArray();
}
}
private static byte[] PKDecompress(Stream data, int expectedLength)
{
PKLibDecompress pk = new PKLibDecompress(data);
return pk.Explode(expectedLength);
}
private static byte[] ZlibDecompress(Stream data, int expectedLength)
{
// This assumes that Zlib won't be used in combination with another compression type
byte[] Output = new byte[expectedLength];
Stream s = new InflaterInputStream(data);
int Offset = 0;
while (expectedLength > 0)
{
int size = s.Read(Output, Offset, expectedLength);
if (size == 0) break;
Offset += size;
expectedLength -= size;
}
return Output;
}
public override long Seek(long offset, SeekOrigin origin)
{
long target;
switch (origin)
{
case SeekOrigin.Begin:
target = offset;
break;
case SeekOrigin.Current:
target = Position + offset;
break;
case SeekOrigin.End:
target = Length + offset;
break;
default:
throw new ArgumentException("Origin", "Invalid SeekOrigin");
}
if (target < 0)
throw new ArgumentOutOfRangeException("Attmpted to Seek before the beginning of the stream");
if (target >= Length)
throw new ArgumentOutOfRangeException("Attmpted to Seek beyond the end of the stream");
position = target;
return position;
}
internal static uint DetectFileSeed(uint value0, uint value1, uint decrypted)
{
uint temp = (value0 ^ decrypted) - 0xeeeeeeee;
for (int i = 0; i < 0x100; i++)
{
uint seed1 = temp - MPQ.cryptTable[0x400 + i];
uint seed2 = 0xeeeeeeee + MPQ.cryptTable[0x400 + (seed1 & 0xff)];
uint result = value0 ^ (seed1 + seed2);
if (result != decrypted)
continue;
uint saveseed1 = seed1;
// Test this result against the 2nd value
seed1 = ((~seed1 << 21) + 0x11111111) | (seed1 >> 11);
seed2 = result + seed2 + (seed2 << 5) + 3;
seed2 += MPQ.cryptTable[0x400 + (seed1 & 0xff)];
result = value1 ^ (seed1 + seed2);
if ((result & 0xfffc0000) == 0)
return saveseed1;
}
return 0;
}
public override void SetLength(long value) => throw new NotImplementedException();
public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
}
}

View File

@ -0,0 +1,125 @@
using System.IO;
namespace OpenDiablo2.Common.Models
{
/// <summary>
/// An IMA ADPCM decompress for Mpq files
/// </summary>
internal static class MpqWavCompression
{
private static readonly int[] sLookup =
{
0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,
0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F,
0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F,
0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133,
0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292,
0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583,
0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0,
0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954,
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B,
0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462,
0x7FFF
};
private static readonly int[] sLookup2 =
{
-1, 0, -1, 4, -1, 2, -1, 6,
-1, 1, -1, 5, -1, 3, -1, 7,
-1, 1, -1, 5, -1, 3, -1, 7,
-1, 2, -1, 4, -1, 6, -1, 8
};
public static byte[] Decompress(Stream data, int channelCount)
{
int[] Array1 = new int[] { 0x2c, 0x2c };
int[] Array2 = new int[channelCount];
BinaryReader input = new BinaryReader(data);
MemoryStream outputstream = new MemoryStream();
BinaryWriter output = new BinaryWriter(outputstream);
input.ReadByte();
byte shift = input.ReadByte();
for (int i = 0; i < channelCount; i++)
{
short temp = input.ReadInt16();
Array2[i] = temp;
output.Write(temp);
}
int channel = channelCount - 1;
while (data.Position < data.Length)
{
byte value = input.ReadByte();
if (channelCount == 2) channel = 1 - channel;
if ((value & 0x80) != 0)
{
switch (value & 0x7f)
{
case 0:
if (Array1[channel] != 0) Array1[channel]--;
output.Write((short)Array2[channel]);
break;
case 1:
Array1[channel] += 8;
if (Array1[channel] > 0x58) Array1[channel] = 0x58;
if (channelCount == 2) channel = 1 - channel;
break;
case 2:
break;
default:
Array1[channel] -= 8;
if (Array1[channel] < 0) Array1[channel] = 0;
if (channelCount == 2) channel = 1 - channel;
break;
}
}
else
{
int temp1 = sLookup[Array1[channel]];
int temp2 = temp1 >> shift;
if ((value & 1) != 0)
temp2 += (temp1 >> 0);
if ((value & 2) != 0)
temp2 += (temp1 >> 1);
if ((value & 4) != 0)
temp2 += (temp1 >> 2);
if ((value & 8) != 0)
temp2 += (temp1 >> 3);
if ((value & 0x10) != 0)
temp2 += (temp1 >> 4);
if ((value & 0x20) != 0)
temp2 += (temp1 >> 5);
int temp3 = Array2[channel];
if ((value & 0x40) != 0)
{
temp3 -= temp2;
if (temp3 <= short.MinValue) temp3 = short.MinValue;
}
else
{
temp3 += temp2;
if (temp3 >= short.MaxValue) temp3 = short.MaxValue;
}
Array2[channel] = temp3;
output.Write((short)temp3);
Array1[channel] += sLookup2[value & 0x1f];
if (Array1[channel] < 0)
Array1[channel] = 0;
else
if (Array1[channel] > 0x58) Array1[channel] = 0x58;
}
}
return outputstream.ToArray();
}
}
}

View File

@ -0,0 +1,229 @@
//
// MpqLibDecompress.cs
//
// Authors:
// Foole (fooleau@gmail.com)
//
// (C) 2006 Foole (fooleau@gmail.com)
// Based on code from StormLib by Ladislav Zezula
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
namespace OpenDiablo2.Common.Models
{
enum CompressionType
{
Binary = 0,
Ascii = 1
}
/// <summary>
/// A decompressor for PKLib implode/explode
/// </summary>
public class PKLibDecompress
{
private BitStream _bitstream;
private CompressionType _compressionType;
private int _dictSizeBits; // Dictionary size in bits
private static byte[] sPosition1;
private static byte[] sPosition2;
private static readonly byte[] sLenBits =
{
3, 2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7
};
private static readonly byte[] sLenCode =
{
5, 3, 1, 6, 10, 2, 12, 20, 4, 24, 8, 48, 16, 32, 64, 0
};
private static readonly byte[] sExLenBits =
{
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8
};
private static readonly UInt16[] sLenBase =
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x000A, 0x000E, 0x0016, 0x0026, 0x0046, 0x0086, 0x0106
};
private static readonly byte[] sDistBits =
{
2, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
};
private static readonly byte[] sDistCode =
{
0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A,
0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C,
0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00
};
static PKLibDecompress()
{
sPosition1 = GenerateDecodeTable(sDistBits, sDistCode);
sPosition2 = GenerateDecodeTable(sLenBits, sLenCode);
}
public PKLibDecompress(Stream input)
{
_bitstream = new BitStream(input);
_compressionType = (CompressionType)input.ReadByte();
if (_compressionType != CompressionType.Binary && _compressionType != CompressionType.Ascii)
throw new InvalidDataException("Invalid compression type: " + _compressionType);
_dictSizeBits = input.ReadByte();
// This is 6 in test cases
if (4 > _dictSizeBits || _dictSizeBits > 6)
throw new InvalidDataException("Invalid dictionary size: " + _dictSizeBits);
}
public byte[] Explode(int expectedSize)
{
byte[] outputbuffer = new byte[expectedSize];
Stream outputstream = new MemoryStream(outputbuffer);
int instruction;
while ((instruction = DecodeLit()) != -1)
{
if (instruction < 0x100)
{
outputstream.WriteByte((byte)instruction);
}
else
{
// If instruction is greater than 0x100, it means "Repeat n - 0xFE bytes"
int copylength = instruction - 0xFE;
int moveback = DecodeDist(copylength);
if (moveback == 0) break;
int source = (int)outputstream.Position - moveback;
// We can't just outputstream.Write the section of the array
// because it might overlap with what is currently being written
while (copylength-- > 0)
outputstream.WriteByte(outputbuffer[source++]);
}
}
if (outputstream.Position == expectedSize)
{
return outputbuffer;
}
else
{
// Resize the array
byte[] result = new byte[outputstream.Position];
Array.Copy(outputbuffer, 0, result, 0, result.Length);
return result;
}
}
// Return values:
// 0x000 - 0x0FF : One byte from compressed file.
// 0x100 - 0x305 : Copy previous block (0x100 = 1 byte)
// -1 : EOF
private int DecodeLit()
{
switch (_bitstream.ReadBits(1))
{
case -1:
return -1;
case 1:
// The next bits are position in buffers
int pos = sPosition2[_bitstream.PeekByte()];
// Skip the bits we just used
if (_bitstream.ReadBits(sLenBits[pos]) == -1) return -1;
int nbits = sExLenBits[pos];
if (nbits != 0)
{
// TODO: Verify this conversion
int val2 = _bitstream.ReadBits(nbits);
if (val2 == -1 && (pos + val2 != 0x10e)) return -1;
pos = sLenBase[pos] + val2;
}
return pos + 0x100; // Return number of bytes to repeat
case 0:
if (_compressionType == CompressionType.Binary)
return _bitstream.ReadBits(8);
// TODO: Text mode
throw new NotImplementedException("Text mode is not yet implemented");
default:
return 0;
}
}
private int DecodeDist(int length)
{
if (_bitstream.EnsureBits(8) == false) return 0;
int pos = sPosition1[_bitstream.PeekByte()];
byte skip = sDistBits[pos]; // Number of bits to skip
// Skip the appropriate number of bits
if (_bitstream.ReadBits(skip) == -1) return 0;
if (length == 2)
{
if (_bitstream.EnsureBits(2) == false) return 0;
pos = (pos << 2) | _bitstream.ReadBits(2);
}
else
{
if (_bitstream.EnsureBits(_dictSizeBits) == false) return 0;
pos = ((pos << _dictSizeBits)) | _bitstream.ReadBits(_dictSizeBits);
}
return pos + 1;
}
private static byte[] GenerateDecodeTable(byte[] bits, byte[] codes)
{
byte[] result = new byte[256];
for (int i = bits.Length - 1; i >= 0; i--)
{
UInt32 idx1 = codes[i];
UInt32 idx2 = (UInt32)1 << bits[i];
do
{
result[idx1] = (byte)i;
idx1 += idx2;
} while (idx1 < 0x100);
}
return result;
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Models
{
public struct PaletteEntry
{
public int R;
public int G;
public int B;
}
public struct Palette
{
public string Name { get; set; }
public PaletteEntry[] Colors;
public static Palette LoadFromStream(Stream stream)
{
var result = new Palette
{
Colors = new PaletteEntry[256]
};
var br = new BinaryReader(stream);
for (var i = 0; i <= 255; i++)
result.Colors[i] = new PaletteEntry
{
B = br.ReadByte(),
G = br.ReadByte(),
R = br.ReadByte()
};
return result;
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Models
{
public struct SoundEntry
{
public string Handle { get; set; }
public int Index { get; set; }
public string FileName { get; set; }
public byte Volume { get; set; }
public int GroupSize { get; set; }
public bool Loop { get; set; }
public int FadeIn { get; set; }
public int FadeOut { get; set; }
public int DeferInst { get; set; }
public int StopInst { get; set; }
public int Duration { get; set; }
public int Compound { get; set; }
public bool Reverb { get; set; }
public int Falloff { get; set; }
public int Cache { get; set; }
public bool AsyncOnly { get; set; }
public int Priority { get; set; }
public int Stream { get; set; }
public int Stereo { get; set; }
public int Tracking { get; set; }
public int Solo { get; set; }
public int MusicVol { get; set; }
public int Block1 { get; set; }
public int Block2 { get; set; }
public int Block3 { get; set; }
}
public static class SoundEntryHelper
{
public static SoundEntry ToSoundEntry(this string source)
{
var props = source.Split('\t');
return new SoundEntry
{
Handle = props[0],
Index = Convert.ToInt32(props[1]),
FileName = props[2],
Volume = Convert.ToByte(props[3]),
GroupSize = Convert.ToInt32(props[4]),
Loop = Convert.ToInt32(props[5]) == 1,
FadeIn = Convert.ToInt32(props[6]),
FadeOut = Convert.ToInt32(props[7]),
DeferInst = Convert.ToInt32(props[8]),
StopInst = Convert.ToInt32(props[9]),
Duration = Convert.ToInt32(props[10]),
Compound = Convert.ToInt32(props[11]),
Reverb = Convert.ToInt32(props[12]) == 1,
Falloff = Convert.ToInt32(props[13]),
Cache = Convert.ToInt32(props[14]),
AsyncOnly = Convert.ToInt32(props[15]) == 1,
Priority = Convert.ToInt32(props[16]),
Stream = Convert.ToInt32(props[17]),
Stereo = Convert.ToInt32(props[18]),
Tracking = Convert.ToInt32(props[19]),
Solo = Convert.ToInt32(props[20]),
MusicVol = Convert.ToInt32(props[21]),
Block1 = Convert.ToInt32(props[22]),
Block2 = Convert.ToInt32(props[23]),
Block3 = Convert.ToInt32(props[24]),
};
}
}
}

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B743160E-A0BB-45DC-9998-967A85E50562}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenDiablo2.Common</RootNamespace>
<AssemblyName>OpenDiablo2.Common</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="DotNetZip, Version=1.12.0.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
<HintPath>..\packages\DotNetZip.1.12.0\lib\net20\DotNetZip.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.84.0.0, Culture=neutral, PublicKeyToken=1b03e6acf1164f73">
<HintPath>..\packages\NetSword.Common.ICSharpCode.SharpZipLib.0.84.0\lib\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Attributes\SceneAttribute.cs" />
<Compile Include="Enums\eMPQFormatVersion.cs" />
<Compile Include="Interfaces\IGameEngine.cs" />
<Compile Include="Interfaces\IMPQProvider.cs" />
<Compile Include="Interfaces\IPaletteProvider.cs" />
<Compile Include="Interfaces\IRenderTarget.cs" />
<Compile Include="Interfaces\IRenderWindow.cs" />
<Compile Include="Interfaces\IScene.cs" />
<Compile Include="Interfaces\ISprite.cs" />
<Compile Include="Models\BitStream.cs" />
<Compile Include="Models\GlobalConfiguration.cs" />
<Compile Include="Models\ImageSet.cs" />
<Compile Include="Models\MPQ.cs" />
<Compile Include="Models\MPQHuffman.cs" />
<Compile Include="Models\MPQStream.cs" />
<Compile Include="Models\MPQWavCompression.cs" />
<Compile Include="Models\Palette.cs" />
<Compile Include="Models\PKLibDecompress.cs" />
<Compile Include="Models\SoundEntry.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenDiablo2.Common")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OpenDiablo2.Common")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b743160e-a0bb-45dc-9998-967a85e50562")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotNetZip" version="1.12.0" targetFramework="net461" />
<package id="NetSword.Common.ICSharpCode.SharpZipLib" version="0.84.0" targetFramework="net461" developmentDependency="true" />
<package id="SharpZipLib" version="1.0.0" targetFramework="net461" />
</packages>

View File

@ -0,0 +1,23 @@
using Autofac;
using OpenDiablo2.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Core
{
public sealed class AutofacModule : Module
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override void Load(ContainerBuilder builder)
{
log.Info("Configuring OpenDiablo2.Core service implementations.");
builder.RegisterType<GameEngine>().AsImplementedInterfaces().SingleInstance();
builder.RegisterType<MPQProvider>().As<IMPQProvider>().SingleInstance();
}
}
}

View File

@ -0,0 +1,97 @@
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace OpenDiablo2.Core
{
public sealed class GameEngine : IGameEngine, IPaletteProvider
{
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly IMPQProvider mpqProvider;
private readonly IRenderWindow renderWindow;
private readonly Func<string, IScene> getScene;
private IScene currentScene;
private readonly MPQ[] MPQs;
private Dictionary<string, SoundEntry> soundTable = new Dictionary<string, SoundEntry>();
public Dictionary<string, Palette> PaletteTable { get; private set; } = new Dictionary<string, Palette>();
private Stopwatch sw = new Stopwatch();
public GameEngine(IMPQProvider mpqProvider, IRenderWindow renderWindow, Func<string, IScene> getScene)
{
this.mpqProvider = mpqProvider;
this.renderWindow = renderWindow;
this.getScene = getScene;
MPQs = mpqProvider.GetMPQs().ToArray();
}
private void LoadPalettes()
{
log.Info("Loading palettes");
var paletteFiles = MPQs.SelectMany(x => x.Files).Where(x => x.StartsWith("data\\global\\palette\\") && x.EndsWith(".dat"));
foreach (var paletteFile in paletteFiles)
{
var paletteNameParts = paletteFile.Split('\\');
var paletteName = paletteNameParts[paletteNameParts.Count() - 2];
PaletteTable[paletteName] = Palette.LoadFromStream(mpqProvider.GetStream(paletteFile));
}
}
private void LoadSoundData()
{
log.Info("Loading sound configuration data");
foreach (var soundDescFile in mpqProvider.GetTextFile("data\\global\\excel\\Sounds.txt"))
{
foreach (var row in soundDescFile.Skip(1).Where(x => !String.IsNullOrWhiteSpace(x)))
{
var soundEntry = row.ToSoundEntry();
soundTable[soundEntry.Handle] = soundEntry;
}
}
}
public void Run()
{
LoadPalettes();
LoadSoundData();
currentScene = getScene("Main Menu");
sw.Start();
while (renderWindow.IsRunning)
{
while (sw.ElapsedMilliseconds < 16)
Thread.Sleep(1); // Oh yes we did
var ms = sw.ElapsedMilliseconds;
// Prevent falco-punch updates
if (ms > 1000)
{
sw.Restart();
continue;
}
sw.Restart();
renderWindow.Update();
currentScene.Update(ms);
currentScene.Render();
}
}
public void Dispose()
{
}
}
}

View File

@ -0,0 +1,40 @@
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Core
{
public sealed class MPQProvider : IMPQProvider
{
private readonly GlobalConfiguration globalConfiguration;
private readonly MPQ[] mpqs;
public MPQProvider(GlobalConfiguration globalConfiguration)
{
this.globalConfiguration = globalConfiguration;
this.mpqs = Directory
.EnumerateFiles(globalConfiguration.BaseDataPath, "*.mpq")
.Where(x => !Path.GetFileName(x).StartsWith("patch"))
.Select(file => new MPQ(file))
.ToArray();
}
public IEnumerable<MPQ> GetMPQs() => mpqs;
public Stream GetStream(string fileName)
=> mpqs.First(x => x.Files.Any(z =>z.ToLower() == fileName.ToLower())).OpenFile(fileName);
public IEnumerable<IEnumerable<string>> GetTextFile(string fileName)
{
foreach (var stream in mpqs.Where(x => x.Files.Contains(fileName)).Select(x => x.OpenFile(fileName)))
yield return new StreamReader(stream).ReadToEnd().Split('\n').Select(x => x.Trim());
}
}
}

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8FC6BF7D-835A-47C1-A6B2-125495FA0900}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenDiablo2.Core</RootNamespace>
<AssemblyName>OpenDiablo2.Core</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.8.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.4.8.1\lib\net45\Autofac.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="Xabe.FFmpeg, Version=3.1.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xabe.FFmpeg.3.1.4\lib\netstandard2.0\Xabe.FFmpeg.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AutofacModule.cs" />
<Compile Include="GameEngine.cs" />
<Compile Include="MPQProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenDiablo2.Common\OpenDiablo2.Common.csproj">
<Project>{b743160e-a0bb-45dc-9998-967a85e50562}</Project>
<Name>OpenDiablo2.Common</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenDiablo2.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OpenDiablo2.Core")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8fc6bf7d-835a-47c1-a6b2-125495fa0900")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.8.1" targetFramework="net461" />
<package id="log4net" version="2.0.8" targetFramework="net461" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
<package id="Xabe.FFmpeg" version="3.1.4" targetFramework="net461" />
</packages>

View File

@ -0,0 +1,18 @@
using Autofac;
using OpenDiablo2.Common.Interfaces;
namespace OpenDiablo2.SDL2_
{
public sealed class AutofacModule : Module
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override void Load(ContainerBuilder builder)
{
log.Info("Configuring OpenDiablo2.Core service implementations.");
builder.RegisterType<SDL2RenderWindow>().AsImplementedInterfaces().SingleInstance();
}
}
}

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1F8731D5-393B-4561-9CEA-887A2F466576}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenDiablo2.SDL2</RootNamespace>
<AssemblyName>OpenDiablo2.SDL2</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.8.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.4.8.1\lib\net45\Autofac.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AutofacModule.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SDL2-CS\LPUtf8StrMarshaler.cs" />
<Compile Include="SDL2-CS\SDL2.cs" />
<Compile Include="SDL2-CS\SDL2_image.cs" />
<Compile Include="SDL2-CS\SDL2_mixer.cs" />
<Compile Include="SDL2-CS\SDL2_ttf.cs" />
<Compile Include="SDL2RenderWindow.cs" />
<Compile Include="SDL2Sprite.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenDiablo2.Common\OpenDiablo2.Common.csproj">
<Project>{b743160e-a0bb-45dc-9998-967a85e50562}</Project>
<Name>OpenDiablo2.Common</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="SDL2.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenDiablo2.SDL2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OpenDiablo2.SDL2")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1f8731d5-393b-4561-9cea-887a2f466576")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,106 @@
/* SDL2# - C# Wrapper for SDL2
*
* Copyright (c) 2013-2015 Ethan Lee.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
*
*/
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace SDL2
{
internal unsafe class LPUtf8StrMarshaler : ICustomMarshaler
{
public const string LeaveAllocated = "LeaveAllocated";
private static ICustomMarshaler
_leaveAllocatedInstance = new LPUtf8StrMarshaler(true),
_defaultInstance = new LPUtf8StrMarshaler(false);
public static ICustomMarshaler GetInstance(string cookie)
{
switch (cookie)
{
case "LeaveAllocated":
return _leaveAllocatedInstance;
default:
return _defaultInstance;
}
}
private bool _leaveAllocated;
public LPUtf8StrMarshaler(bool leaveAllocated)
{
_leaveAllocated = leaveAllocated;
}
public object MarshalNativeToManaged(IntPtr pNativeData)
{
if (pNativeData == IntPtr.Zero)
return null;
var ptr = (byte*)pNativeData;
while (*ptr != 0)
{
ptr++;
}
var bytes = new byte[ptr - (byte*)pNativeData];
Marshal.Copy(pNativeData, bytes, 0, bytes.Length);
return Encoding.UTF8.GetString(bytes);
}
public IntPtr MarshalManagedToNative(object ManagedObj)
{
if (ManagedObj == null)
return IntPtr.Zero;
var str = ManagedObj as string;
if (str == null)
{
throw new ArgumentException("ManagedObj must be a string.", "ManagedObj");
}
var bytes = Encoding.UTF8.GetBytes(str);
var mem = SDL.SDL_malloc((IntPtr) (bytes.Length + 1));
Marshal.Copy(bytes, 0, mem, bytes.Length);
((byte*)mem)[bytes.Length] = 0;
return mem;
}
public void CleanUpManagedData(object ManagedObj)
{
}
public void CleanUpNativeData(IntPtr pNativeData)
{
if (!_leaveAllocated)
{
SDL.SDL_free(pNativeData);
}
}
public int GetNativeDataSize ()
{
return -1;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
#region License
/* SDL2# - C# Wrapper for SDL2
*
* Copyright (c) 2013-2015 Ethan Lee.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
*
*/
#endregion
#region Using Statements
using System;
using System.Runtime.InteropServices;
#endregion
namespace SDL2
{
public static class SDL_image
{
#region SDL2# Variables
/* Used by DllImport to load the native library. */
private const string nativeLibName = "SDL2_image.dll";
#endregion
#region SDL_image.h
/* Similar to the headers, this is the version we're expecting to be
* running with. You will likely want to check this somewhere in your
* program!
*/
public const int SDL_IMAGE_MAJOR_VERSION = 2;
public const int SDL_IMAGE_MINOR_VERSION = 0;
public const int SDL_IMAGE_PATCHLEVEL = 0;
[Flags]
public enum IMG_InitFlags
{
IMG_INIT_JPG = 0x00000001,
IMG_INIT_PNG = 0x00000002,
IMG_INIT_TIF = 0x00000004,
IMG_INIT_WEBP = 0x00000008
}
public static void SDL_IMAGE_VERSION(out SDL.SDL_version X)
{
X.major = SDL_IMAGE_MAJOR_VERSION;
X.minor = SDL_IMAGE_MINOR_VERSION;
X.patch = SDL_IMAGE_PATCHLEVEL;
}
[DllImport(nativeLibName, EntryPoint = "IMG_LinkedVersion", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr INTERNAL_IMG_LinkedVersion();
public static SDL.SDL_version IMG_LinkedVersion()
{
SDL.SDL_version result;
IntPtr result_ptr = INTERNAL_IMG_LinkedVersion();
result = (SDL.SDL_version) Marshal.PtrToStructure(
result_ptr,
typeof(SDL.SDL_version)
);
return result;
}
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int IMG_Init(IMG_InitFlags flags);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void IMG_Quit();
/* IntPtr refers to an SDL_Surface* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IMG_Load(
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string file
);
/* src refers to an SDL_RWops*, IntPtr to an SDL_Surface* */
/* THIS IS A PUBLIC RWops FUNCTION! */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IMG_Load_RW(
IntPtr src,
int freesrc
);
/* src refers to an SDL_RWops*, IntPtr to an SDL_Surface* */
/* THIS IS A PUBLIC RWops FUNCTION! */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IMG_LoadTyped_RW(
IntPtr src,
int freesrc,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string type
);
/* IntPtr refers to an SDL_Texture*, renderer to an SDL_Renderer* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IMG_LoadTexture(
IntPtr renderer,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string file
);
/* renderer refers to an SDL_Renderer*.
* src refers to an SDL_RWops*.
* IntPtr to an SDL_Texture*.
*/
/* THIS IS A PUBLIC RWops FUNCTION! */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IMG_LoadTexture_RW(
IntPtr renderer,
IntPtr src,
int freesrc
);
/* renderer refers to an SDL_Renderer*.
* src refers to an SDL_RWops*.
* IntPtr to an SDL_Texture*.
*/
/* THIS IS A PUBLIC RWops FUNCTION! */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IMG_LoadTextureTyped_RW(
IntPtr renderer,
IntPtr src,
int freesrc,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string type
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int IMG_InvertAlpha(int on);
/* IntPtr refers to an SDL_Surface* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IMG_ReadXPMFromArray(
[In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)]
string[] xpm
);
/* surface refers to an SDL_Surface* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int IMG_SavePNG(
IntPtr surface,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string file
);
/* surface refers to an SDL_Surface*, dst to an SDL_RWops* */
/* THIS IS A PUBLIC RWops FUNCTION! */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int IMG_SavePNG_RW(
IntPtr surface,
IntPtr dst,
int freedst
);
#endregion
}
}

View File

@ -0,0 +1,489 @@
#region License
/* SDL2# - C# Wrapper for SDL2
*
* Copyright (c) 2013-2015 Ethan Lee.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
*
*/
#endregion
#region Using Statements
using System;
using System.Runtime.InteropServices;
#endregion
namespace SDL2
{
public static class SDL_mixer
{
#region SDL2# Variables
/* Used by DllImport to load the native library. */
private const string nativeLibName = "SDL2_mixer.dll";
#endregion
#region SDL_mixer.h
/* Similar to the headers, this is the version we're expecting to be
* running with. You will likely want to check this somewhere in your
* program!
*/
public const int SDL_MIXER_MAJOR_VERSION = 2;
public const int SDL_MIXER_MINOR_VERSION = 0;
public const int SDL_MIXER_PATCHLEVEL = 0;
/* In C, you can redefine this value before including SDL_mixer.h.
* We're not going to allow this in SDL2#, since the value of this
* variable is persistent and not dependent on preprocessor ordering.
*/
public const int MIX_CHANNELS = 8;
public static readonly int MIX_DEFAULT_FREQUENCY = 22050;
public static readonly ushort MIX_DEFAULT_FORMAT =
BitConverter.IsLittleEndian ? SDL.AUDIO_S16LSB : SDL.AUDIO_S16MSB;
public static readonly int MIX_DEFAULT_CHANNELS = 2;
public static readonly byte MIX_MAX_VOLUME = 128;
[Flags]
public enum MIX_InitFlags
{
MIX_INIT_FLAC = 0x00000001,
MIX_INIT_MOD = 0x00000002,
MIX_INIT_MP3 = 0x00000004,
MIX_INIT_OGG = 0x00000008,
MIX_INIT_FLUIDSYNTH = 0x00000010,
}
public enum Mix_Fading
{
MIX_NO_FADING,
MIX_FADING_OUT,
MIX_FADING_IN
}
public enum Mix_MusicType
{
MUS_NONE,
MUS_CMD,
MUS_WAV,
MUS_MOD,
MUS_MID,
MUS_OGG,
MUS_MP3,
MUS_MP3_MAD,
MUS_FLAC,
MUS_MODPLUG
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MixFuncDelegate(
IntPtr udata, // void*
IntPtr stream, // Uint8*
int len
);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Mix_EffectFunc_t(
int chan,
IntPtr stream, // void*
int len,
IntPtr udata // void*
);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Mix_EffectDone_t(
int chan,
IntPtr udata // void*
);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MusicFinishedDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ChannelFinishedDelegate(int channel);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int SoundFontDelegate(
IntPtr a, // const char*
IntPtr b // void*
);
public static void SDL_MIXER_VERSION(out SDL.SDL_version X)
{
X.major = SDL_MIXER_MAJOR_VERSION;
X.minor = SDL_MIXER_MINOR_VERSION;
X.patch = SDL_MIXER_PATCHLEVEL;
}
[DllImport(nativeLibName, EntryPoint = "MIX_Linked_Version", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr INTERNAL_MIX_Linked_Version();
public static SDL.SDL_version MIX_Linked_Version()
{
SDL.SDL_version result;
IntPtr result_ptr = INTERNAL_MIX_Linked_Version();
result = (SDL.SDL_version) Marshal.PtrToStructure(
result_ptr,
typeof(SDL.SDL_version)
);
return result;
}
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_Init(MIX_InitFlags flags);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_Quit();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_OpenAudio(
int frequency,
ushort format,
int channels,
int chunksize
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_AllocateChannels(int numchans);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_QuerySpec(
out int frequency,
out ushort format,
out int channels
);
/* src refers to an SDL_RWops*, IntPtr to a Mix_Chunk* */
/* THIS IS A PUBLIC RWops FUNCTION! */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Mix_LoadWAV_RW(
IntPtr src,
int freesrc
);
/* IntPtr refers to a Mix_Chunk* */
/* This is an RWops macro in the C header. */
public static IntPtr Mix_LoadWAV(string file)
{
IntPtr rwops = SDL.INTERNAL_SDL_RWFromFile(file, "rb");
return Mix_LoadWAV_RW(rwops, 1);
}
/* IntPtr refers to a Mix_Music* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Mix_LoadMUS(
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string file
);
/* IntPtr refers to a Mix_Chunk* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Mix_QuickLoad_WAV(
[In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)]
byte[] mem
);
/* IntPtr refers to a Mix_Chunk* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Mix_QuickLoad_RAW(
[In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)]
byte[] mem,
uint len
);
/* chunk refers to a Mix_Chunk* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_FreeChunk(IntPtr chunk);
/* music refers to a Mix_Music* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_FreeMusic(IntPtr music);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GetNumChunkDecoders();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler), MarshalCookie = LPUtf8StrMarshaler.LeaveAllocated)]
public static extern string Mix_GetChunkDecoder(int index);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GetNumMusicDecoders();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler), MarshalCookie = LPUtf8StrMarshaler.LeaveAllocated)]
public static extern string Mix_GetMusicDecoder(int index);
/* music refers to a Mix_Music* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern Mix_MusicType Mix_GetMusicType(IntPtr music);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_SetPostMix(
MixFuncDelegate mix_func,
IntPtr arg // void*
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_HookMusic(
MixFuncDelegate mix_func,
IntPtr arg // void*
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_HookMusicFinished(
MusicFinishedDelegate music_finished
);
/* IntPtr refers to a void* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Mix_GetMusicHookData();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_ChannelFinished(
ChannelFinishedDelegate channel_finished
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_RegisterEffect(
int chan,
Mix_EffectFunc_t f,
Mix_EffectDone_t d,
IntPtr arg // void*
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_UnregisterEffect(
int channel,
Mix_EffectFunc_t f
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_UnregisterAllEffects(int channel);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_SetPanning(
int channel,
byte left,
byte right
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_SetPosition(
int channel,
short angle,
byte distance
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_SetDistance(int channel, byte distance);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_SetReverseStereo(int channel, int flip);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_ReserveChannels(int num);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GroupChannel(int which, int tag);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GroupChannels(int from, int to, int tag);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GroupAvailable(int tag);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GroupCount(int tag);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GroupOldest(int tag);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GroupNewer(int tag);
/* chunk refers to a Mix_Chunk* */
public static int Mix_PlayChannel(
int channel,
IntPtr chunk,
int loops
) {
return Mix_PlayChannelTimed(channel, chunk, loops, -1);
}
/* chunk refers to a Mix_Chunk* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_PlayChannelTimed(
int channel,
IntPtr chunk,
int loops,
int ticks
);
/* music refers to a Mix_Music* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_PlayMusic(IntPtr music, int loops);
/* music refers to a Mix_Music* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_FadeInMusic(
IntPtr music,
int loops,
int ms
);
/* music refers to a Mix_Music* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_FadeInMusicPos(
IntPtr music,
int loops,
int ms,
double position
);
/* chunk refers to a Mix_Chunk* */
public static int Mix_FadeInChannel(
int channel,
IntPtr chunk,
int loops,
int ms
) {
return Mix_FadeInChannelTimed(channel, chunk, loops, ms, -1);
}
/* chunk refers to a Mix_Chunk* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_FadeInChannelTimed(
int channel,
IntPtr chunk,
int loops,
int ms,
int ticks
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_Volume(int channel, int volume);
/* chunk refers to a Mix_Chunk* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_VolumeChunk(
IntPtr chunk,
int volume
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_VolumeMusic(int volume);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_HaltChannel(int channel);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_HaltGroup(int tag);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_HaltMusic();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_ExpireChannel(int channel, int ticks);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_FadeOutChannel(int which, int ms);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_FadeOutGroup(int tag, int ms);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_FadeOutMusic(int ms);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern Mix_Fading Mix_FadingMusic();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern Mix_Fading Mix_FadingChannel(int which);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_Pause(int channel);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_Resume(int channel);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_Paused(int channel);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_PauseMusic();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_ResumeMusic();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_RewindMusic();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_PausedMusic();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_SetMusicPosition(double position);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_Playing(int channel);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_PlayingMusic();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_SetMusicCMD(
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string command
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_SetSynchroValue(int value);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_GetSynchroValue();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_SetSoundFonts(
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string paths
);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler), MarshalCookie = LPUtf8StrMarshaler.LeaveAllocated)]
public static extern string Mix_GetSoundFonts();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int Mix_EachSoundFont(
SoundFontDelegate function,
IntPtr data // void*
);
/* IntPtr refers to a Mix_Chunk* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Mix_GetChunk(int channel);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void Mix_CloseAudio();
#endregion
}
}

View File

@ -0,0 +1,406 @@
#region License
/* SDL2# - C# Wrapper for SDL2
*
* Copyright (c) 2013-2015 Ethan Lee.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
*
*/
#endregion
#region Using Statements
using System;
using System.Runtime.InteropServices;
#endregion
namespace SDL2
{
public static class SDL_ttf
{
#region SDL2# Variables
/* Used by DllImport to load the native library. */
private const string nativeLibName = "SDL2_ttf.dll";
#endregion
#region SDL_ttf.h
/* Similar to the headers, this is the version we're expecting to be
* running with. You will likely want to check this somewhere in your
* program!
*/
public const int SDL_TTF_MAJOR_VERSION = 2;
public const int SDL_TTF_MINOR_VERSION = 0;
public const int SDL_TTF_PATCHLEVEL = 12;
public const int UNICODE_BOM_NATIVE = 0xFEFF;
public const int UNICODE_BOM_SWAPPED = 0xFFFE;
public const int TTF_STYLE_NORMAL = 0x00;
public const int TTF_STYLE_BOLD = 0x01;
public const int TTF_STYLE_ITALIC = 0x02;
public const int TTF_STYLE_UNDERLINE = 0x04;
public const int TTF_STYLE_STRIKETHROUGH = 0x08;
public const int TTF_HINTING_NORMAL = 0;
public const int TTF_HINTING_LIGHT = 1;
public const int TTF_HINTING_MONO = 2;
public const int TTF_HINTING_NONE = 3;
public static void SDL_TTF_VERSION(out SDL.SDL_version X)
{
X.major = SDL_TTF_MAJOR_VERSION;
X.minor = SDL_TTF_MINOR_VERSION;
X.patch = SDL_TTF_PATCHLEVEL;
}
[DllImport(nativeLibName, EntryPoint = "TTF_LinkedVersion", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr INTERNAL_TTF_LinkedVersion();
public static SDL.SDL_version TTF_LinkedVersion()
{
SDL.SDL_version result;
IntPtr result_ptr = INTERNAL_TTF_LinkedVersion();
result = (SDL.SDL_version) Marshal.PtrToStructure(
result_ptr,
typeof(SDL.SDL_version)
);
return result;
}
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TTF_ByteSwappedUNICODE(int swapped);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_Init();
/* IntPtr refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_OpenFont(
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string file,
int ptsize
);
/* src refers to an SDL_RWops*, IntPtr to a TTF_Font* */
/* THIS IS A PUBLIC RWops FUNCTION! */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_OpenFontRW(
IntPtr src,
int freesrc,
int ptsize
);
/* IntPtr refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_OpenFontIndex(
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string file,
int ptsize,
long index
);
/* src refers to an SDL_RWops*, IntPtr to a TTF_Font* */
/* THIS IS A PUBLIC RWops FUNCTION! */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_OpenFontIndexRW(
IntPtr src,
int freesrc,
int ptsize,
long index
);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_GetFontStyle(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TTF_SetFontStyle(IntPtr font, int style);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_GetFontOutline(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TTF_SetFontOutline(IntPtr font, int outline);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_GetFontHinting(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TTF_SetFontHinting(IntPtr font, int hinting);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_FontHeight(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_FontAscent(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_FontDescent(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_FontLineSkip(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_GetFontKerning(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TTF_SetFontKerning(IntPtr font, int allowed);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern long TTF_FontFaces(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_FontFaceIsFixedWidth(IntPtr font);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler), MarshalCookie = LPUtf8StrMarshaler.LeaveAllocated)]
public static extern string TTF_FontFaceFamilyName(
IntPtr font
);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler), MarshalCookie = LPUtf8StrMarshaler.LeaveAllocated)]
public static extern string TTF_FontFaceStyleName(
IntPtr font
);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_GlyphIsProvided(IntPtr font, ushort ch);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_GlyphMetrics(
IntPtr font,
ushort ch,
out int minx,
out int maxx,
out int miny,
out int maxy,
out int advance
);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_SizeText(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string text,
out int w,
out int h
);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_SizeUTF8(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string text,
out int w,
out int h
);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_SizeUNICODE(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPWStr)]
string text,
out int w,
out int h
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderText_Solid(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPStr)]
string text,
SDL.SDL_Color fg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderUTF8_Solid(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string text,
SDL.SDL_Color fg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderUNICODE_Solid(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPWStr)]
string text,
SDL.SDL_Color fg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderGlyph_Solid(
IntPtr font,
ushort ch,
SDL.SDL_Color fg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderText_Shaded(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPStr)]
string text,
SDL.SDL_Color fg,
SDL.SDL_Color bg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderUTF8_Shaded(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string text,
SDL.SDL_Color fg,
SDL.SDL_Color bg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderUNICODE_Shaded(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPWStr)]
string text,
SDL.SDL_Color fg,
SDL.SDL_Color bg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderGlyph_Shaded(
IntPtr font,
ushort ch,
SDL.SDL_Color fg,
SDL.SDL_Color bg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderText_Blended(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPStr)]
string text,
SDL.SDL_Color fg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderUTF8_Blended(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string text,
SDL.SDL_Color fg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderUNICODE_Blended(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPWStr)]
string text,
SDL.SDL_Color fg
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderText_Blended_Wrapped(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPStr)]
string text,
SDL.SDL_Color fg,
uint wrapped
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderUTF8_Blended_Wrapped(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(LPUtf8StrMarshaler))]
string text,
SDL.SDL_Color fg,
uint wrapped
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderUNICODE_Blended_Wrapped(
IntPtr font,
[In()] [MarshalAs(UnmanagedType.LPWStr)]
string text,
SDL.SDL_Color fg,
uint wrapped
);
/* IntPtr refers to an SDL_Surface*, font to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TTF_RenderGlyph_Blended(
IntPtr font,
ushort ch,
SDL.SDL_Color fg
);
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TTF_CloseFont(IntPtr font);
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TTF_Quit();
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TTF_WasInit();
/* font refers to a TTF_Font* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int SDL_GetFontKerningSize(
IntPtr font,
int prev_index,
int index
);
#endregion
}
}

BIN
OpenDiablo2.SDL2/SDL2.dll Normal file

Binary file not shown.

View File

@ -0,0 +1,91 @@
using OpenDiablo2.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SDL2;
using System.IO;
using System.Drawing;
using OpenDiablo2.Common.Models;
using Autofac;
namespace OpenDiablo2.SDL2_
{
public sealed class SDL2RenderWindow : IRenderWindow, IRenderTarget
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IntPtr window, renderer;
private bool running;
public bool IsRunning => running;
private readonly ILifetimeScope lifetimeScope;
public SDL2RenderWindow(ILifetimeScope lifetimeScope)
{
this.lifetimeScope = lifetimeScope;
SDL.SDL_Init(SDL.SDL_INIT_EVERYTHING);
if (SDL.SDL_SetHint(SDL.SDL_HINT_RENDER_SCALE_QUALITY, "0") == SDL.SDL_bool.SDL_FALSE)
throw new ApplicationException($"Unable to Init hinting: {SDL.SDL_GetError()}");
window = SDL.SDL_CreateWindow("OpenDiablo2", SDL.SDL_WINDOWPOS_UNDEFINED, SDL.SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN);
if (window == IntPtr.Zero)
throw new ApplicationException($"Unable to create SDL Window: {SDL.SDL_GetError()}");
renderer = SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED);
if (renderer == IntPtr.Zero)
throw new ApplicationException($"Unable to create SDL Window: {SDL.SDL_GetError()}");
SDL.SDL_SetRenderDrawBlendMode(renderer, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
running = true;
}
public void Dispose()
{
SDL.SDL_DestroyRenderer(renderer);
SDL.SDL_DestroyWindow(window);
SDL.SDL_Quit();
}
public void Clear()
{
SDL.SDL_RenderClear(renderer);
}
public void Sync()
{
SDL.SDL_RenderPresent(renderer);
}
public void Update()
{
while (SDL.SDL_PollEvent(out SDL.SDL_Event evt) != 0)
{
if (evt.type == SDL.SDL_EventType.SDL_QUIT)
running = false;
}
}
public void Draw(ISprite sprite)
{
var spr = sprite as SDL2Sprite;
var loc = spr.GetRenderPoint();
var destRect = new SDL.SDL_Rect
{
x = loc.X,
y = loc.Y,
w = spr.FrameSize.Width,
h = spr.FrameSize.Height
};
SDL.SDL_RenderCopy(renderer, spr.textures[spr.Frame], IntPtr.Zero, ref destRect);
}
public ISprite LoadSprite(ImageSet source)
=> new SDL2Sprite(source, renderer);
}
}

View File

@ -0,0 +1,123 @@
using OpenDiablo2.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SDL2;
using System.Runtime.InteropServices;
using OpenDiablo2.Common.Models;
namespace OpenDiablo2.SDL2_
{
public sealed class SDL2Sprite : ISprite
{
public Point Location { get; set; } = new Point();
public Size FrameSize { get; set; } = new Size();
public int Frame { get; set; }
public int TotalFrames { get; internal set; }
private Palette palette;
public Palette CurrentPalette
{
get => palette;
set
{
palette = value;
UpdateTextureData();
}
}
private readonly ImageSet source;
private readonly IntPtr renderer;
internal IntPtr[] textures = new IntPtr[0];
public SDL2Sprite(ImageSet source, IntPtr renderer)
{
this.source = source;
this.renderer = renderer;
TotalFrames = source.Frames.Count();
FrameSize = new Size(Pow2((int)source.Frames.Max(x => x.Width)), Pow2((int)source.Frames.Max(x => x.Height)));
}
internal Point GetRenderPoint()
=> new Point(
Location.X + source.Frames[Frame].OffsetX,
(Location.Y - FrameSize.Height) + source.Frames[Frame].OffsetY
);
private void UpdateTextureData()
{
foreach (var texture in textures)
{
SDL.SDL_DestroyTexture(texture);
}
textures = new IntPtr[TotalFrames];
for (var i = 0; i < source.Frames.Count(); i++)
textures[i] = LoadFrame(source.Frames[i], renderer);
}
// TODO: Less dumb color correction
private Color AdjustColor(Color source)
=> Color.FromArgb(
source.A,
(byte)Math.Min((float)source.R * 1.2, 255),
(byte)Math.Min((float)source.G * 1.2, 255),
(byte)Math.Min((float)source.B * 1.2, 255)
);
private IntPtr LoadFrame(ImageFrame frame, IntPtr renderer)
{
var texture = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_ARGB8888, (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, Pow2(FrameSize.Width), Pow2(FrameSize.Height));
if (texture == IntPtr.Zero)
throw new ApplicationException("Unaple to initialize texture.");
SDL.SDL_SetTextureBlendMode(texture, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
SDL.SDL_SetRenderTarget(renderer, texture);
SDL.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL.SDL_RenderFillRect(renderer, IntPtr.Zero);
SDL.SDL_SetRenderTarget(renderer, IntPtr.Zero);
var binaryData = new UInt32[frame.Width * frame.Height];
for (int y = 0; y < frame.Height; y++)
for (int x = 0; x < frame.Width; x++)
{
var col = AdjustColor(frame.GetColor(x, y, CurrentPalette));
binaryData[x + y * frame.Width] = (uint)col.ToArgb();
}
var rect = new SDL.SDL_Rect { x = 0, y = FrameSize.Height - (int)frame.Height, w = (int)frame.Width, h = (int)frame.Height };
GCHandle pinnedArray = GCHandle.Alloc(binaryData, GCHandleType.Pinned);
SDL.SDL_UpdateTexture(texture, ref rect, pinnedArray.AddrOfPinnedObject(), (int)frame.Width * 4);
pinnedArray.Free();
return texture;
}
private int Pow2(int val)
{
int result = 1;
while (result < val)
result *= 2;
return result;
}
public void Dispose()
{
foreach (var texture in textures)
{
SDL.SDL_DestroyTexture(texture);
}
}
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.8.1" targetFramework="net461" />
<package id="log4net" version="2.0.8" targetFramework="net461" />
<package id="SDL2-CS" version="2.0.0.0" targetFramework="net461" />
</packages>

View File

@ -0,0 +1,31 @@
using Autofac;
using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Scenes
{
public sealed class AutofacModule : Module
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override void Load(ContainerBuilder builder)
{
log.Info("Configuring OpenDiablo2.Scenes service implementations.");
var types = ThisAssembly.GetTypes().Where(x => typeof(IScene).IsAssignableFrom(x) && x.IsClass);
foreach (var type in types)
{
var att = type.GetCustomAttributes(true).First(x => typeof(SceneAttribute).IsAssignableFrom(x.GetType())) as SceneAttribute;
builder
.RegisterType(type)
.Keyed<IScene>(att.SceneName)
.InstancePerDependency();
}
}
}
}

View File

@ -0,0 +1,96 @@
using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Scenes
{
[Scene("Main Menu")]
public class MainMenu : IScene
{
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly IRenderWindow renderWindow;
private readonly IPaletteProvider paletteProvider;
private readonly IMPQProvider mpqProvider;
private float logoFrame;
private ISprite backgroundSprite, diabloLogoLeft, diabloLogoRight, diabloLogoLeftBlack, diabloLogoRightBlack;
public MainMenu(
IRenderWindow renderWindow,
IPaletteProvider paletteProvider,
IMPQProvider mpqProvider
)
{
this.renderWindow = renderWindow;
this.paletteProvider = paletteProvider;
this.mpqProvider = mpqProvider;
//var texture = renderWindow.LoadSprite(ImageSet.LoadFromStream(mpqProvider.GetStream("data\\global\\ui\\Logo\\logo.DC6")));
backgroundSprite = renderWindow.LoadSprite(ImageSet.LoadFromStream(mpqProvider.GetStream("data\\global\\ui\\FrontEnd\\trademarkscreenEXP.DC6")));
backgroundSprite.CurrentPalette = paletteProvider.PaletteTable["Sky"];
diabloLogoLeft = renderWindow.LoadSprite(ImageSet.LoadFromStream(mpqProvider.GetStream("data\\global\\ui\\FrontEnd\\D2logoFireLeft.DC6")));
diabloLogoLeft.CurrentPalette = paletteProvider.PaletteTable["Units"];
diabloLogoRight = renderWindow.LoadSprite(ImageSet.LoadFromStream(mpqProvider.GetStream("data\\global\\ui\\FrontEnd\\D2logoFireRight.DC6")));
diabloLogoRight.CurrentPalette = paletteProvider.PaletteTable["Units"];
diabloLogoLeftBlack = renderWindow.LoadSprite(ImageSet.LoadFromStream(mpqProvider.GetStream("data\\global\\ui\\FrontEnd\\D2logoBlackLeft.DC6")));
diabloLogoLeftBlack.CurrentPalette = paletteProvider.PaletteTable["Units"];
diabloLogoRightBlack = renderWindow.LoadSprite(ImageSet.LoadFromStream(mpqProvider.GetStream("data\\global\\ui\\FrontEnd\\D2logoBlackRight.DC6")));
diabloLogoRightBlack.CurrentPalette = paletteProvider.PaletteTable["Units"];
logoFrame = 0f;
diabloLogoLeft.Location = new Point(400, 120);
diabloLogoRight.Location = new Point(400, 120);
diabloLogoLeftBlack.Location = new Point(400, 120);
diabloLogoRightBlack.Location = new Point(400, 120);
}
public void Render()
{
renderWindow.Clear();
for (int y = 0; y < 3; y++)
for (int x = 0; x < 4; x++)
{
backgroundSprite.Frame = x + (y * 4);
backgroundSprite.Location = new Point(x * backgroundSprite.FrameSize.Width, (y + 1) * backgroundSprite.FrameSize.Height);
renderWindow.Draw(backgroundSprite);
}
diabloLogoLeftBlack.Frame = (int)((float)diabloLogoLeftBlack.TotalFrames * logoFrame);
renderWindow.Draw(diabloLogoLeftBlack);
diabloLogoRightBlack.Frame = (int)((float)diabloLogoRightBlack.TotalFrames * logoFrame);
renderWindow.Draw(diabloLogoRightBlack);
diabloLogoLeft.Frame = (int)((float)diabloLogoLeft.TotalFrames * logoFrame);
renderWindow.Draw(diabloLogoLeft);
diabloLogoRight.Frame = (int)((float)diabloLogoRight.TotalFrames * logoFrame);
renderWindow.Draw(diabloLogoRight);
renderWindow.Sync();
}
public void Update(long ms)
{
float seconds = ((float)ms / 1000f);
logoFrame += seconds;
while (logoFrame >= 1f)
logoFrame -= 1f;
}
public void Dispose()
{
}
}
}

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{05224FE7-293F-4184-B1D6-89F5171B60E0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenDiablo2.Scenes</RootNamespace>
<AssemblyName>OpenDiablo2.Scenes</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.8.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.4.8.1\lib\net45\Autofac.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AutofacModule.cs" />
<Compile Include="MainMenu.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenDiablo2.Common\OpenDiablo2.Common.csproj">
<Project>{b743160e-a0bb-45dc-9998-967a85e50562}</Project>
<Name>OpenDiablo2.Common</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenDiablo2.Scenes")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OpenDiablo2.Scenes")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("05224fe7-293f-4184-b1d6-89f5171b60e0")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.8.1" targetFramework="net461" />
<package id="log4net" version="2.0.8" targetFramework="net461" />
</packages>

71
OpenDiablo2.sln Normal file
View File

@ -0,0 +1,71 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28306.52
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2", "OpenDiablo2\OpenDiablo2.csproj", "{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.Common", "OpenDiablo2.Common\OpenDiablo2.Common.csproj", "{B743160E-A0BB-45DC-9998-967A85E50562}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.Core", "OpenDiablo2.Core\OpenDiablo2.Core.csproj", "{8FC6BF7D-835A-47C1-A6B2-125495FA0900}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.SDL2", "OpenDiablo2.SDL2\OpenDiablo2.SDL2.csproj", "{1F8731D5-393B-4561-9CEA-887A2F466576}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.Scenes", "OpenDiablo2.Scenes\OpenDiablo2.Scenes.csproj", "{05224FE7-293F-4184-B1D6-89F5171B60E0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Debug|x64.ActiveCfg = Debug|x64
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Debug|x64.Build.0 = Debug|x64
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Release|Any CPU.Build.0 = Release|Any CPU
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Release|x64.ActiveCfg = Release|x64
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Release|x64.Build.0 = Release|x64
{B743160E-A0BB-45DC-9998-967A85E50562}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B743160E-A0BB-45DC-9998-967A85E50562}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B743160E-A0BB-45DC-9998-967A85E50562}.Debug|x64.ActiveCfg = Debug|x64
{B743160E-A0BB-45DC-9998-967A85E50562}.Debug|x64.Build.0 = Debug|x64
{B743160E-A0BB-45DC-9998-967A85E50562}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B743160E-A0BB-45DC-9998-967A85E50562}.Release|Any CPU.Build.0 = Release|Any CPU
{B743160E-A0BB-45DC-9998-967A85E50562}.Release|x64.ActiveCfg = Release|x64
{B743160E-A0BB-45DC-9998-967A85E50562}.Release|x64.Build.0 = Release|x64
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Debug|x64.ActiveCfg = Debug|x64
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Debug|x64.Build.0 = Debug|x64
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Release|Any CPU.Build.0 = Release|Any CPU
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Release|x64.ActiveCfg = Release|x64
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Release|x64.Build.0 = Release|x64
{1F8731D5-393B-4561-9CEA-887A2F466576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F8731D5-393B-4561-9CEA-887A2F466576}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F8731D5-393B-4561-9CEA-887A2F466576}.Debug|x64.ActiveCfg = Debug|x64
{1F8731D5-393B-4561-9CEA-887A2F466576}.Debug|x64.Build.0 = Debug|x64
{1F8731D5-393B-4561-9CEA-887A2F466576}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F8731D5-393B-4561-9CEA-887A2F466576}.Release|Any CPU.Build.0 = Release|Any CPU
{1F8731D5-393B-4561-9CEA-887A2F466576}.Release|x64.ActiveCfg = Release|x64
{1F8731D5-393B-4561-9CEA-887A2F466576}.Release|x64.Build.0 = Release|x64
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Debug|x64.ActiveCfg = Debug|Any CPU
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Debug|x64.Build.0 = Debug|Any CPU
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Release|Any CPU.Build.0 = Release|Any CPU
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Release|x64.ActiveCfg = Release|Any CPU
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {782826E1-7E8E-4878-88FB-1B564D82C621}
EndGlobalSection
EndGlobal

6
OpenDiablo2/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>

View File

@ -0,0 +1,10 @@
using CommandLine;
namespace OpenDiablo2
{
public sealed class CommandLineOptions
{
[Option('p', "datapath", Required = false, HelpText = "Specifies the root data path")]
public string DataPath { get; set; }
}
}

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>OpenDiablo2</RootNamespace>
<AssemblyName>OpenDiablo2</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.8.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.4.8.1\lib\net45\Autofac.dll</HintPath>
</Reference>
<Reference Include="CommandLine, Version=2.3.0.0, Culture=neutral, PublicKeyToken=de6f01bd326f8c32, processorArchitecture=MSIL">
<HintPath>..\packages\CommandLineParser.2.3.0\lib\net45\CommandLine.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Console, Version=4.0.1.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Console.4.3.1\lib\net46\System.Console.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Reflection.TypeExtensions, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.TypeExtensions.4.5.1\lib\net461\System.Reflection.TypeExtensions.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CommandLineOptions.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenDiablo2.Common\OpenDiablo2.Common.csproj">
<Project>{b743160e-a0bb-45dc-9998-967a85e50562}</Project>
<Name>OpenDiablo2.Common</Name>
</ProjectReference>
<ProjectReference Include="..\OpenDiablo2.Core\OpenDiablo2.Core.csproj">
<Project>{8fc6bf7d-835a-47c1-a6b2-125495fa0900}</Project>
<Name>OpenDiablo2.Core</Name>
</ProjectReference>
<ProjectReference Include="..\OpenDiablo2.Scenes\OpenDiablo2.Scenes.csproj">
<Project>{05224fe7-293f-4184-b1d6-89f5171b60e0}</Project>
<Name>OpenDiablo2.Scenes</Name>
</ProjectReference>
<ProjectReference Include="..\OpenDiablo2.SDL2\OpenDiablo2.SDL2.csproj">
<Project>{1f8731d5-393b-4561-9cea-887a2f466576}</Project>
<Name>OpenDiablo2.SDL2</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

88
OpenDiablo2/Program.cs Normal file
View File

@ -0,0 +1,88 @@
using CommandLine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autofac;
using OpenDiablo2.Common.Models;
using System.Reflection;
using OpenDiablo2.Common.Interfaces;
using System.Diagnostics;
namespace OpenDiablo2
{
static class Program
{
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static void Main(string[] args)
{
log.Info("OpenDiablo 2: The Free and Open Source Diablo 2 clone!");
#if !DEBUG
try
{
#endif
BuildContainer()
.ResolveCommandLineOptions(args)
.Resolve<IGameEngine>()
.Run();
#if !DEBUG
}
catch (Exception ex)
{
log.Fatal("Uncaught exception detected, the game has been terminated!", ex);
}
#endif
}
static IContainer BuildContainer() => new ContainerBuilder()
.RegisterLocalTypes()
.LoadAssemblyModules()
.Build();
static IContainer ResolveCommandLineOptions(this IContainer container, IEnumerable<string> args)
{
var globalConfiguration = container.Resolve<GlobalConfiguration>();
Parser.Default.ParseArguments<CommandLineOptions>(args).WithParsed<CommandLineOptions>(o =>
{
globalConfiguration.BaseDataPath = Path.GetFullPath(o.DataPath ?? Directory.GetCurrentDirectory());
});
return container;
}
static ContainerBuilder RegisterLocalTypes(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<GlobalConfiguration>().AsSelf().SingleInstance();
containerBuilder.Register<Func<string, IScene>>(c =>
{
var componentContext = c.Resolve<IComponentContext>();
return (sceneName) => componentContext.ResolveKeyed<IScene>(sceneName);
});
return containerBuilder;
}
static ContainerBuilder LoadAssemblyModules(this ContainerBuilder containerBuilder)
{
var filesToLoad = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll");
foreach (var file in filesToLoad)
{
try
{
var assembly = Assembly.LoadFrom(file);
containerBuilder.RegisterAssemblyModules(assembly);
}
catch { /* Silently ignore assembly load errors as not all DLLs are our modules... */ }
}
return containerBuilder;
}
}
}

View File

@ -0,0 +1,37 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenDiablo 2")]
[assembly: AssemblyDescription("The free and open source Diablo 2 remake!")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OpenDiablo 2")]
[assembly: AssemblyCopyright("GLP v3")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2b0cf1ac-06dd-4322-ae8b-ff8a8c70a3cd")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")]

View File

@ -0,0 +1,23 @@
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
<appender name="console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level %logger - %message%newline" />
</layout>
</appender>
<appender name="file" type="log4net.Appender.RollingFileAppender">
<file value="OpenDiablo2.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger - %message%newline" />
</layout>
</appender>
</log4net>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.8.1" targetFramework="net461" />
<package id="CommandLineParser" version="2.3.0" targetFramework="net461" />
<package id="log4net" version="2.0.8" targetFramework="net461" />
<package id="System.Collections" version="4.3.0" targetFramework="net461" />
<package id="System.Console" version="4.3.1" targetFramework="net461" />
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" />
<package id="System.Globalization" version="4.3.0" targetFramework="net461" />
<package id="System.IO" version="4.3.0" targetFramework="net461" />
<package id="System.Linq" version="4.3.0" targetFramework="net461" />
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net461" />
<package id="System.Reflection" version="4.3.0" targetFramework="net461" />
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net461" />
<package id="System.Reflection.TypeExtensions" version="4.5.1" targetFramework="net461" />
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net461" />
<package id="System.Runtime" version="4.3.0" targetFramework="net461" />
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net461" />
</packages>