Much more work on patching. Now we just depend on extract resources from classic's jar and terrain-patch.png. Snow is still broken.

This commit is contained in:
UnknownShadow200 2015-09-13 08:12:47 +10:00
parent 69fb5dcb89
commit 58e5816acf
23 changed files with 378 additions and 207 deletions

View File

@ -1,6 +1,6 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
# SharpDevelop 4.4
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassicalSharp", "ClassicalSharp\ClassicalSharp.csproj", "{BEB1C785-5CAD-48FF-A886-876BF0A318D4}"
EndProject

View File

@ -162,7 +162,8 @@
<Compile Include="TexturePack\Animations.cs" />
<Compile Include="TexturePack\TerrainAtlas1D.cs" />
<Compile Include="TexturePack\TerrainAtlas2D.cs" />
<Compile Include="TexturePack\ZipExtractor.cs" />
<Compile Include="TexturePack\TexturePackExtractor.cs" />
<Compile Include="TexturePack\ZipReader.cs" />
<Compile Include="Utils\Camera.cs" />
<Compile Include="Utils\FastBitmap.cs" />
<Compile Include="Utils\FastColour.cs" />

View File

@ -306,10 +306,8 @@ namespace ClassicalSharp.Commands {
if( String.IsNullOrEmpty( path ) ) return;
try {
using( FileStream fs = new FileStream( path, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
ZipExtractor extractor = new ZipExtractor( game );
extractor.Extract( fs );
}
TexturePackExtractor extractor = new TexturePackExtractor();
extractor.Extract( path, game );
} catch( FileNotFoundException ) {
game.AddChat( "&e/client texturepack: Couldn't find file \"" + path + "\"" );
}

View File

@ -45,7 +45,7 @@ namespace ClassicalSharp {
public PickingRenderer Picking;
public PickedPos SelectedPos = new PickedPos();
public ModelCache ModelCache;
internal string skinServer, chatInInputBuffer;
internal string skinServer, chatInInputBuffer, defaultTexPack;
internal int defaultIb;
public bool CanUseThirdPersonCamera = true;
FpsScreen fpsScreen;
@ -102,6 +102,7 @@ namespace ClassicalSharp {
public int MouseSensitivity = 30;
public bool HideGui = false;
public Animations Animations;
internal int CloudsTextureId, RainTextureId, SnowTextureId;
void LoadAtlas( Bitmap bmp ) {
TerrainAtlas1D.Dispose();
@ -115,7 +116,11 @@ namespace ClassicalSharp {
Raise( TerrainAtlasChanged );
}
public Game() : base() {
public Game( string username, string mppass, string skinServer, string defaultTexPack ) : base() {
Username = username;
Mppass = mppass;
this.skinServer = skinServer;
this.defaultTexPack = defaultTexPack;
// We can't use enum array initaliser because this causes problems when building with mono
// and running on default .NET (https://bugzilla.xamarin.com/show_bug.cgi?id=572)
#if !__MonoCS__
@ -142,10 +147,11 @@ namespace ClassicalSharp {
ModelCache.InitCache();
AsyncDownloader = new AsyncDownloader( skinServer );
Graphics.PrintGraphicsInfo();
Bitmap terrainBmp = new Bitmap( "terrain.png" );
TerrainAtlas1D = new TerrainAtlas1D( Graphics );
TerrainAtlas = new TerrainAtlas2D( Graphics );
LoadAtlas( terrainBmp );
TexturePackExtractor extractor = new TexturePackExtractor();
extractor.Extract( defaultTexPack, this );
BlockInfo = new BlockInfo();
BlockInfo.Init();
BlockInfo.SetDefaultBlockPermissions( CanPlace, CanDelete );
@ -311,6 +317,10 @@ namespace ClassicalSharp {
Graphics.DeleteIb( defaultIb );
Graphics.Dispose();
Utils2D.Dispose();
Animations.Dispose();
Graphics.DeleteTexture( ref CloudsTextureId );
Graphics.DeleteTexture( ref RainTextureId );
Graphics.DeleteTexture( ref SnowTextureId );
base.Dispose();
}

View File

@ -16,9 +16,6 @@ namespace ClassicalSharp.Model {
RightLeg = MakeLeg( 0f, 0.1875f, 0.0625f, 0.125f );
LeftWing = MakeWing( -0.25f, -0.1875f );
RightWing = MakeWing( 0.1875f, 0.25f );
if( cache.ChickenTexId <= 0 )
cache.ChickenTexId = graphics.CreateTexture( "chicken.png" );
}
ModelPart MakeHead() {

View File

@ -14,9 +14,6 @@ namespace ClassicalSharp.Model {
RightLegFront = MakeLeg( 0, 0.25f, -0.375f, -0.125f );
LeftLegBack = MakeLeg( -0.25f, 0, 0.125f, 0.375f );
RightLegBack = MakeLeg( 0, 0.25f, 0.125f, 0.375f );
if( cache.CreeperTexId <= 0 )
cache.CreeperTexId = graphics.CreateTexture( "creeper.png" );
}
ModelPart MakeHead() {

View File

@ -14,9 +14,6 @@ namespace ClassicalSharp.Model {
RightLegFront = MakeLeg( 0.0625f, 0.3125f, -0.4375f, -0.1875f );
LeftLegBack = MakeLeg( -0.3125f, -0.0625f, 0.3125f, 0.5625f );
RightLegBack = MakeLeg( 0.0625f, 0.3125f, 0.3125f, 0.5625f );
if( cache.PigTexId <= 0 )
cache.PigTexId = graphics.CreateTexture( "pig.png" );
}
ModelPart MakeHead() {

View File

@ -36,11 +36,6 @@ namespace ClassicalSharp.Model {
Set64x64Slim.LeftArm = MakeLeftArm( 32, 48, 0.25f, 0.4375f, 3, true );
Set64x64Slim.RightArm = MakeRightArm( 40, 16, 0.25f, 0.4375f, 3, true );
Set64x64Slim.Hat = Set64x64.Hat;
using( Bitmap bmp = new Bitmap( "char.png" ) ) {
window.DefaultPlayerSkinType = Utils.GetSkinType( bmp );
cache.HumanoidTexId = graphics.CreateTexture( bmp );
}
}
ModelPart MakeLeftArm( int x, int y, float x1, float x2, int width, bool _64x64 ) {

View File

@ -25,11 +25,6 @@ namespace ClassicalSharp.Model {
FurLeftLegBack = MakeFurLeg( -0.34375f, -0.03125f, 0.28125f, 0.59375f );
FurRightLegBack = MakeFurLeg( 0.03125f, 0.34375f, 0.28125f, 0.59375f );
}
if( cache.SheepTexId <= 0 )
cache.SheepTexId = graphics.CreateTexture( "sheep.png" );
if( cache.SheepFurTexId <= 0 )
cache.SheepFurTexId = graphics.CreateTexture( "sheep_fur.png" );
}
ModelPart MakeHead() {

View File

@ -13,9 +13,6 @@ namespace ClassicalSharp.Model {
RightLeg = MakeRightLeg( 0.0625f, 0.1875f );
LeftArm = MakeLeftArm( 0.375f, 0.25f );
RightArm = MakeRightArm( 0.25f, 0.375f );
if( cache.SkeletonTexId <= 0 )
cache.SkeletonTexId = graphics.CreateTexture( "skeleton.png" );
}
ModelPart MakeLeftArm( float x1, float x2 ) {

View File

@ -12,9 +12,6 @@ namespace ClassicalSharp.Model {
End = MakeEnd();
LeftLeg = MakeLeg( -1.1875f, -0.1875f );
RightLeg = MakeLeg( 0.1875f, 1.1875f );
if( cache.SpiderTexId <= 0 )
cache.SpiderTexId = graphics.CreateTexture( "spider.png" );
}
ModelPart MakeHead() {

View File

@ -14,9 +14,6 @@ namespace ClassicalSharp.Model {
RightLeg = MakeRightLeg( 0, 0.25f );
LeftArm = MakeLeftArm( 0.5f, 0.25f );
RightArm = MakeRightArm( 0.25f, 0.5f );
if( cache.ZombieTexId <= 0 )
cache.ZombieTexId = graphics.CreateTexture( "zombie.png" );
}
ModelPart MakeLeftArm( float x1, float x2 ) {

View File

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.IO;
using System.Net;
using System.Windows.Forms;
using ClassicalSharp.TexturePack;
namespace ClassicalSharp {
@ -15,14 +16,15 @@ namespace ClassicalSharp {
}
Utils.Log( "Starting " + Utils.AppName + ".." );
if( !AllResourcesExist( "terrain.png", "char.png", "clouds.png" ) ) {
if( !File.Exists( "default.zip" ) ) {
Fail( "default.zip not found. Cannot start." );
return;
}
if( args.Length == 0 ) {
if( args.Length == 0 || args.Length == 1 ) {
Utils.Log( "Starting singleplayer mode." );
using( Game game = new Game() ) {
game.Username = "LocalPlayer";
game.skinServer = "http://s3.amazonaws.com/MinecraftSkins/";
const string skinServer = "http://s3.amazonaws.com/MinecraftSkins/";
using( Game game = new Game( "LocalPlayer", null, skinServer, "default.zip" ) ) {
game.Run();
}
} else if( args.Length < 4 ) {
@ -48,26 +50,13 @@ namespace ClassicalSharp {
}
string skinServer = args.Length >= 5 ? args[4] : "http://s3.amazonaws.com/MinecraftSkins/";
using( Game game = new Game() ) {
game.Username = args[0];
game.Mppass = args[1];
using( Game game = new Game( args[0], args[1], skinServer, "default.zip" ) ) {
game.IPAddress = ip;
game.Port = port;
game.skinServer = skinServer;
game.Run();
}
}
static bool AllResourcesExist( params string[] resources ) {
foreach( string resource in resources ) {
if( !File.Exists( resource ) ) {
Fail( resource + " not found. Cannot start." );
return false;
}
}
return true;
}
static void Fail( string text ) {
Utils.LogWarning( text );
Utils.Log( "Press any key to exit.." );

View File

@ -12,7 +12,7 @@ namespace ClassicalSharp.Renderers {
map = game.Map;
}
int cloudTexture = -1, cloudsVb = -1, cloudsIndices;
int cloudsVb = -1, cloudsIndices;
int skyOffset = 10, skyVb = -1, skyIndices;
public float CloudsSpeed = 1;
bool legacy;
@ -69,7 +69,6 @@ namespace ClassicalSharp.Renderers {
base.Init();
graphics.Fog = true;
ResetAllEnv( null, null );
cloudTexture = graphics.CreateTexture( "clouds.png" );
game.ViewDistanceChanged += ResetAllEnv;
}
@ -84,7 +83,6 @@ namespace ClassicalSharp.Renderers {
game.ViewDistanceChanged -= ResetAllEnv;
graphics.DeleteVb( skyVb );
graphics.DeleteVb( cloudsVb );
graphics.DeleteTexture( ref cloudTexture );
}
void RenderClouds( double delta ) {
@ -97,7 +95,7 @@ namespace ClassicalSharp.Renderers {
graphics.AlphaTest = true;
graphics.Texturing = true;
graphics.BindTexture( cloudTexture );
graphics.BindTexture( game.CloudsTextureId );
graphics.BeginVbBatch( VertexFormat.Pos3fTex2fCol4b );
graphics.BindVb( cloudsVb );
graphics.DrawIndexedVb_TrisT2fC4b( cloudsIndices, 0 );

View File

@ -19,7 +19,6 @@ namespace ClassicalSharp {
}
int weatherVb;
int rainTexture, snowTexture;
short[] heightmap;
float vOffset;
VertexPos3fTex2fCol4b[] vertices = new VertexPos3fTex2fCol4b[8 * 9 * 9];
@ -28,7 +27,7 @@ namespace ClassicalSharp {
if( weather == Weather.Sunny ) return;
graphics.Texturing = true;
graphics.BindTexture( weather == Weather.Rainy ? rainTexture : snowTexture );
graphics.BindTexture( weather == Weather.Rainy ? game.RainTextureId : game.SnowTextureId );
Vector3I pos = Vector3I.Floor( game.LocalPlayer.Position );
float speed = weather == Weather.Rainy ? 1f : 0.25f;
vOffset = -(float)game.accumulator * speed;
@ -90,8 +89,6 @@ namespace ClassicalSharp {
}
public void Init() {
rainTexture = graphics.CreateTexture( "rain.png" );
snowTexture = graphics.CreateTexture( "snow.png" );
game.OnNewMap += OnNewMap;
game.OnNewMapLoaded += OnNewMapLoaded;
}
@ -99,8 +96,6 @@ namespace ClassicalSharp {
public void Dispose() {
game.OnNewMap -= OnNewMap;
game.OnNewMapLoaded -= OnNewMapLoaded;
graphics.DeleteTexture( ref rainTexture );
graphics.DeleteTexture( ref snowTexture );
}
int GetRainHeight( int x, int z ) {

View File

@ -0,0 +1,82 @@
using System;
using System.Drawing;
using System.IO;
using ClassicalSharp.GraphicsAPI;
using ClassicalSharp.Model;
namespace ClassicalSharp.TexturePack {
public sealed class TexturePackExtractor {
Game game;
public void Extract( string path, Game game ) {
this.game = game;
using( FileStream fs = new FileStream( path, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
ZipReader reader = new ZipReader();
reader.ShouldProcessZipEntry = ShouldProcessZipEntry;
reader.ProcessZipEntry = ProcessZipEntry;
reader.Extract( fs );
}
}
bool ShouldProcessZipEntry( string filename ) {
return true;
}
void ProcessZipEntry( string filename, byte[] data, ZipEntry entry ) {
MemoryStream stream = new MemoryStream( data );
ModelCache cache = game.ModelCache;
IGraphicsApi api = game.Graphics;
switch( filename ) {
case "terrain.png":
game.ChangeTerrainAtlas( new Bitmap( stream ) ); break;
case "mob/chicken.png":
case "chicken.png":
UpdateTexture( ref cache.ChickenTexId, stream, false ); break;
case "mob/creeper.png":
case "creeper.png":
UpdateTexture( ref cache.CreeperTexId, stream, false ); break;
case "mob/pig.png":
case "pig.png":
UpdateTexture( ref cache.PigTexId, stream, false ); break;
case "mob/sheep.png":
case "sheep.png":
UpdateTexture( ref cache.SheepTexId, stream, false ); break;
case "mob/skeleton.png":
case "skeleton.png":
UpdateTexture( ref cache.SkeletonTexId, stream, false ); break;
case "mob/spider.png":
case "spider.png":
UpdateTexture( ref cache.SpiderTexId, stream, false ); break;
case "mob/zombie.png":
case "zombie.png":
UpdateTexture( ref cache.ZombieTexId, stream, false ); break;
case "mob/sheep_fur.png":
case "sheep_fur.png":
UpdateTexture( ref cache.SheepFurTexId, stream, false ); break;
case "char.png":
UpdateTexture( ref cache.HumanoidTexId, stream, true ); break;
case "animations.png":
case "animation.png":
game.Animations.SetAtlas( new Bitmap( stream ) ); break;
case "clouds.png":
case "cloud.png":
UpdateTexture( ref game.CloudsTextureId, stream, false ); break;
case "rain.png":
UpdateTexture( ref game.RainTextureId, stream, false ); break;
case "snow.png":
UpdateTexture( ref game.SnowTextureId, stream, false ); break;
}
}
void UpdateTexture( ref int texId, Stream stream, bool setSkinType ) {
game.Graphics.DeleteTexture( ref texId );
using( Bitmap bmp = new Bitmap( stream ) ) {
if( setSkinType )
game.DefaultPlayerSkinType = Utils.GetSkinType( bmp );
texId = game.Graphics.CreateTexture( bmp );
}
}
}
}

View File

@ -1,19 +1,23 @@
using System;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Text;
using ClassicalSharp.GraphicsAPI;
using ClassicalSharp.Model;
namespace ClassicalSharp.TexturePack {
public sealed class ZipExtractor {
public struct ZipEntry {
public int CompressedDataSize, UncompressedDataSize;
public int LocalHeaderOffset, CentralHeaderOffset;
public uint Crc32;
public string Filename;
}
public sealed class ZipReader {
Game game;
public ZipExtractor( Game game ) {
this.game = game;
}
public Action<string, byte[], ZipEntry> ProcessZipEntry;
public Func<string, bool> ShouldProcessZipEntry;
public ZipEntry[] entries;
public int index;
static Encoding enc = Encoding.ASCII;
public void Extract( Stream stream ) {
@ -27,7 +31,7 @@ namespace ClassicalSharp.TexturePack {
}
int entriesCount, centralDirectoryOffset;
ReadEndOfCentralDirectory( reader, out entriesCount, out centralDirectoryOffset );
ZipData[] entries = new ZipData[entriesCount];
entries = new ZipEntry[entriesCount];
reader.BaseStream.Seek( centralDirectoryOffset, SeekOrigin.Begin );
// Read all the central directory entries
@ -44,21 +48,18 @@ namespace ClassicalSharp.TexturePack {
// Now read the local file header entries
for( int i = 0; i < entriesCount; i++ ) {
ZipData entry = entries[i];
index = i;
ZipEntry entry = entries[i];
reader.BaseStream.Seek( entry.LocalHeaderOffset, SeekOrigin.Begin );
sig = reader.ReadUInt32();
if( sig != 0x04034b50 )
throw new NotSupportedException( "Unsupported signature: " + sig.ToString( "X8" ) );
ReadLocalFileHeader( reader, entry );
}
entries = null;
}
struct ZipData {
public int CompressedSize, UncompressedSize;
public int LocalHeaderOffset;
}
void ReadLocalFileHeader( BinaryReader reader, ZipData entry ) {
void ReadLocalFileHeader( BinaryReader reader, ZipEntry entry ) {
ushort versionNeeded = reader.ReadUInt16();
ushort flags = reader.ReadUInt16();
ushort compressionMethod = reader.ReadUInt16();
@ -66,30 +67,32 @@ namespace ClassicalSharp.TexturePack {
reader.ReadUInt32(); // CRC 32
int compressedSize = reader.ReadInt32();
if( compressedSize == 0 ) compressedSize = entry.CompressedSize;
if( compressedSize == 0 ) compressedSize = entry.CompressedDataSize;
int uncompressedSize = reader.ReadInt32();
if( uncompressedSize == 0 ) uncompressedSize = entry.UncompressedSize;
if( uncompressedSize == 0 ) uncompressedSize = entry.UncompressedDataSize;
ushort fileNameLen = reader.ReadUInt16();
ushort extraFieldLen = reader.ReadUInt16();
string fileName = enc.GetString( reader.ReadBytes( fileNameLen ) );
reader.ReadBytes( extraFieldLen );
if( !ShouldProcessZipEntry( fileName ) ) return;
reader.ReadBytes( extraFieldLen );
if( versionNeeded > 20 )
Utils.LogWarning( "May not be able to properly extract a .zip enty with a version later than 2.0" );
byte[] data = DecompressEntry( reader, compressionMethod, compressedSize, uncompressedSize );
if( data != null )
HandleZipEntry( fileName, data );
ProcessZipEntry( fileName, data, entry );
}
int index;
void ReadCentralDirectory( BinaryReader reader, ZipData[] entries ) {
void ReadCentralDirectory( BinaryReader reader, ZipEntry[] entries ) {
ZipEntry entry;
entry.CentralHeaderOffset = (int)( reader.BaseStream.Position - 4 );
reader.ReadUInt16(); // OS
ushort versionNeeded = reader.ReadUInt16();
ushort flags = reader.ReadUInt16();
ushort compressionMethod = reader.ReadUInt16();
reader.ReadUInt32(); // last modified
reader.ReadUInt32(); // CRC 32
uint crc32 = reader.ReadUInt32();
int compressedSize = reader.ReadInt32();
int uncompressedSize = reader.ReadInt32();
ushort fileNameLen = reader.ReadUInt16();
@ -103,11 +106,12 @@ namespace ClassicalSharp.TexturePack {
string fileName = enc.GetString( reader.ReadBytes( fileNameLen ) );
reader.ReadBytes( extraFieldLen );
reader.ReadBytes( fileCommentLen );
ZipData entry;
entry.CompressedSize = compressedSize;
entry.UncompressedSize = uncompressedSize;
entry.CompressedDataSize = compressedSize;
entry.UncompressedDataSize = uncompressedSize;
entry.LocalHeaderOffset = localHeaderOffset;
entry.Filename = fileName;
entry.Crc32 = crc32;
entries[index++] = entry;
}
@ -144,54 +148,5 @@ namespace ClassicalSharp.TexturePack {
return null;
}
}
void HandleZipEntry( string filename, byte[] data ) {
MemoryStream stream = new MemoryStream( data );
ModelCache cache = game.ModelCache;
IGraphicsApi api = game.Graphics;
switch( filename ) {
case "terrain.png":
game.ChangeTerrainAtlas( new Bitmap( stream ) ); break;
case "mob/chicken.png":
case "chicken.png":
UpdateTexture( ref cache.ChickenTexId, stream, false ); break;
case "mob/creeper.png":
case "creeper.png":
UpdateTexture( ref cache.CreeperTexId, stream, false ); break;
case "mob/pig.png":
case "pig.png":
UpdateTexture( ref cache.PigTexId, stream, false ); break;
case "mob/sheep.png":
case "sheep.png":
UpdateTexture( ref cache.SheepTexId, stream, false ); break;
case "mob/skeleton.png":
case "skeleton.png":
UpdateTexture( ref cache.SkeletonTexId, stream, false ); break;
case "mob/spider.png":
case "spider.png":
UpdateTexture( ref cache.SpiderTexId, stream, false ); break;
case "mob/zombie.png":
case "zombie.png":
UpdateTexture( ref cache.ZombieTexId, stream, false ); break;
case "mob/sheep_fur.png":
case "sheep_fur.png":
UpdateTexture( ref cache.SheepFurTexId, stream, false ); break;
case "char.png":
UpdateTexture( ref cache.HumanoidTexId, stream, true ); break;
case "animations.png":
case "animation.png":
game.Animations.SetAtlas( new Bitmap( stream ) ); break;
}
}
void UpdateTexture( ref int texId, Stream stream, bool setSkinType ) {
game.Graphics.DeleteTexture( ref texId );
using( Bitmap bmp = new Bitmap( stream ) ) {
if( setSkinType )
game.DefaultPlayerSkinType = Utils.GetSkinType( bmp );
texId = game.Graphics.CreateTexture( bmp );
}
}
}
}

View File

@ -71,7 +71,7 @@ namespace ClassicalSharp {
return (int*)( scan0Byte + ( y * Stride ) );
}
internal static void MovePortion( int srcX, int srcY, int dstX, int dstY, FastBitmap src, FastBitmap dst, int size ) {
public static void MovePortion( int srcX, int srcY, int dstX, int dstY, FastBitmap src, FastBitmap dst, int size ) {
for( int y = 0; y < size; y++ ) {
int* srcRow = src.GetRowPtr( srcY + y );
int* dstRow = dst.GetRowPtr( dstY + y );

View File

@ -14,6 +14,7 @@ namespace ClassicalSharp {
public delegate TResult Func<TResult>();
public delegate TResult Func<T1, TResult>( T1 arg1 );
public delegate TResult Func<T1, T2, TResult>( T1 arg1, T2 arg2 );
public delegate TResult Func<T1, T2, T3, TResult>( T1 arg1, T2 arg2, T3 arg3 );
public delegate bool TryParseFunc<T>( string s, out T value );
// ################################################################

View File

@ -33,7 +33,7 @@
<BootstrapperEnabled>true</BootstrapperEnabled>
<SignAssembly>False</SignAssembly>
<DelaySign>False</DelaySign>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
@ -55,6 +55,7 @@
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<StartAction>Project</StartAction>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>..\output\release\</OutputPath>
@ -63,6 +64,8 @@
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<StartAction>Project</StartAction>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -82,9 +85,10 @@
</Compile>
<Compile Include="MainForm.GameState.cs" />
<Compile Include="MinecraftSession.cs" />
<Compile Include="Patcher\ResourceFetcher.cs" />
<Compile Include="Patcher\ZipWriter.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ResourceFetcher.cs" />
<Compile Include="ServerListEntry.cs" />
<Compile Include="WebUtility.cs" />
</ItemGroup>
@ -118,5 +122,14 @@
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ClassicalSharp\ClassicalSharp.csproj">
<Project>{BEB1C785-5CAD-48FF-A886-876BF0A318D4}</Project>
<Name>ClassicalSharp</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Patcher" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,103 @@
using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Windows.Forms;
using ClassicalSharp;
using ClassicalSharp.TexturePack;
namespace Launcher {
public class ResourceFetcher {
const string classicJarUri = "http://s3.amazonaws.com/Minecraft.Download/versions/c0.30_01c/c0.30_01c.jar";
const string modernJarUri = "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.2/1.6.2.jar";
const string terrainPatchUri = "http://static.classicube.net/terrain-patch.png";
static int resourcesCount = 2;
public void Run( MainForm form ) {
using( WebClient client = new GZipWebClient() ) {
WebRequest.DefaultWebProxy = null;
int i = 0;
DownloadData( classicJarUri, client, "classic.jar", form, ref i );
DownloadData( terrainPatchUri, client, "terrain-patch.png", form, ref i );
}
reader = new ZipReader();
reader.ShouldProcessZipEntry = ShouldProcessZipEntry_Classic;
reader.ProcessZipEntry = ProcessZipEntry_Classic;
using( FileStream src = new FileStream( "classic.jar", FileMode.Open, FileAccess.Read, FileShare.Read ),
dst = new FileStream( "default.zip", FileMode.Create, FileAccess.Write ) ) {
writer = new ZipWriter( dst );
reader.Extract( src );
writer.WriteCentralDirectoryRecords();
}
File.Move( "terrain-patch.png", "terrain-patched.png" );
}
ZipReader reader;
ZipWriter writer;
bool ShouldProcessZipEntry_Classic( string filename ) {
return filename.StartsWith( "mob" ) || ( filename.IndexOf( '/' ) < 0 );
}
void ProcessZipEntry_Classic( string filename, byte[] data, ZipEntry entry ) {
if( writer.entries == null )
writer.entries = new ZipEntry[reader.entries.Length];
if( filename != "terrain.png" ) {
writer.WriteZipEntry( entry, data );
return;
}
using( Bitmap dstBitmap = new Bitmap( new MemoryStream( data ) ),
maskBitmap = new Bitmap( "terrain-patch.png" ) ) {
PatchImage( dstBitmap, maskBitmap );
writer.WriteTerrainImage( dstBitmap, entry );
}
}
unsafe void PatchImage( Bitmap dstBitmap, Bitmap maskBitmap ) {
using( FastBitmap dst = new FastBitmap( dstBitmap, true ),
src = new FastBitmap( maskBitmap, true ) ) {
int size = src.Width, tileSize = size / 16;
for( int y = 0; y < size; y += tileSize ) {
int* row = src.GetRowPtr( y );
for( int x = 0; x < size; x += tileSize ) {
if( row[x] != unchecked((int)0x80000000) ) {
FastBitmap.MovePortion( x, y, x, y, src, dst, tileSize );
}
}
}
}
}
public bool CheckAllResourcesExist() {
return File.Exists( "default.zip" ) && File.Exists( "terrain-patched.png" );
}
class GZipWebClient : WebClient {
protected override WebRequest GetWebRequest( Uri address ) {
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest( address );
request.AutomaticDecompression = DecompressionMethods.GZip;
return request;
}
}
static bool DownloadData( string uri, WebClient client, string output, MainForm form, ref int i ) {
i++;
if( File.Exists( output ) ) return true;
form.Text = MainForm.AppName + " - fetching " + output + "(" + i + "/" + resourcesCount + ")";
try {
client.DownloadFile( uri, output );
} catch( WebException ex ) {
Program.LogException( ex );
MessageBox.Show( "Unable to download or save " + output, "Failed to download or save resource", MessageBoxButtons.OK, MessageBoxIcon.Error );
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,114 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using ClassicalSharp.TexturePack;
namespace Launcher {
public sealed class ZipWriter {
BinaryWriter writer;
Stream stream;
public ZipWriter( Stream stream ) {
this.stream = stream;
writer = new BinaryWriter( stream );
}
internal ZipEntry[] entries;
internal int entriesCount;
public void WriteZipEntry( ZipEntry entry, byte[] data ) {
entry.CompressedDataSize = (int)entry.UncompressedDataSize;
entry.LocalHeaderOffset = (int)stream.Position;
entries[entriesCount++] = entry;
WriteLocalFileHeader( entry, data, data.Length );
}
public void WriteTerrainImage( Bitmap bmp, ZipEntry entry ) {
MemoryStream data = new MemoryStream( entry.UncompressedDataSize );
bmp.Save( data, ImageFormat.Png );
byte[] buffer = data.GetBuffer();
entry.UncompressedDataSize = (int)data.Length;
entry.Crc32 = CRC32( buffer, entry.UncompressedDataSize );
entry.CompressedDataSize = entry.UncompressedDataSize;
entry.LocalHeaderOffset = (int)stream.Position;
entries[entriesCount++] = entry;
WriteLocalFileHeader( entry, buffer, entry.UncompressedDataSize );
}
public void WriteCentralDirectoryRecords() {
int dirOffset = (int)stream.Position;
for( int i = 0; i < entriesCount; i++ ) {
WriteCentralDirectoryHeaderEntry( entries[i] );
}
int dirSize = (int)( stream.Position - dirOffset );
WriteEndOfCentralDirectoryRecord( (ushort)entriesCount, dirSize, dirOffset );
}
void WriteLocalFileHeader( ZipEntry entry, byte[] data, int length ) {
writer.Write( 0x04034b50 ); // signature
writer.Write( (ushort)20 ); // version needed
writer.Write( (ushort)8 ); // bitflags
writer.Write( (ushort)0 ); // compression method
writer.Write( 0 ); // last modified
writer.Write( 0 ); // CRC32
writer.Write( 0 ); // compressed size
writer.Write( 0 ); // uncompressed size
writer.Write( (ushort)entry.Filename.Length );
writer.Write( (ushort)0 ); // extra field length
for( int i = 0; i < entry.Filename.Length; i++ )
writer.Write( (byte)entry.Filename[i] );
writer.Write( data, 0, length );
// Data descriptor
writer.Write( entry.Crc32 );
writer.Write( entry.CompressedDataSize );
writer.Write( entry.UncompressedDataSize );
}
void WriteCentralDirectoryHeaderEntry( ZipEntry entry ) {
writer.Write( 0x02014b50 ); // signature
writer.Write( (ushort)20 ); // version
writer.Write( (ushort)20 ); // version needed
writer.Write( (ushort)8 ); // bitflags
writer.Write( (ushort)0 ); // compression method
writer.Write( 0 ); // last modified
writer.Write( entry.Crc32 );
writer.Write( entry.CompressedDataSize );
writer.Write( entry.UncompressedDataSize );
writer.Write( (ushort)entry.Filename.Length );
writer.Write( (ushort)0 ); // extra field length
writer.Write( (ushort)0 ); // file comment length
writer.Write( (ushort)0 ); // disk number
writer.Write( (ushort)0 ); // internal attributes
writer.Write( 0 ); // external attributes
writer.Write( entry.LocalHeaderOffset );
for( int i = 0; i < entry.Filename.Length; i++ )
writer.Write( (byte)entry.Filename[i] );
}
void WriteEndOfCentralDirectoryRecord( ushort entries, int centralDirSize, int centralDirOffset ) {
writer.Write( 0x06054b50 ); // signature
writer.Write( (ushort)0 ); // disk number
writer.Write( (ushort)0 ); // disk number of start
writer.Write( entries ); // disk entries
writer.Write( entries ); // total entries
writer.Write( centralDirSize );
writer.Write( centralDirOffset );
writer.Write( (ushort)0 ); // comment length
}
static uint CRC32( byte[] data, int length ) {
uint crc = 0xffffffffU;
for( int i = 0; i < length; i++ ) {
crc ^= data[i];
for( int j = 0; j < 8; j++ )
crc = (crc >> 1) ^ (crc & 1) * 0xEDB88320;
}
return crc ^ 0xffffffffU;
}
}
}

View File

@ -1,60 +0,0 @@
using System;
using System.IO;
using System.Net;
using System.Windows.Forms;
namespace Launcher {
public class ResourceFetcher {
const string resUri = "https://raw.githubusercontent.com/andrewphorn/ClassiCube-Client/master/src/main/resources/";
static readonly string[] coreList = { "char.png", "clouds.png", "terrain.png", "rain.png", "snow.png" };
static readonly string[] mobsList = { "chicken.png", "creeper.png", "pig.png", "sheep.png",
"sheep_fur.png", "skeleton.png", "spider.png", "zombie.png" };
static int resourcesCount = coreList.Length + mobsList.Length;
public void Run( MainForm form ) {
using( WebClient client = new WebClient() ) {
client.Proxy = null;
int i = 0;
if( DownloadResources( "", client, form, ref i, coreList ) ) {
DownloadResources( "mob/", client, form, ref i, mobsList );
}
}
}
static bool DownloadResources( string prefix, WebClient client, MainForm form, ref int i, params string[] resources ) {
foreach( string resource in resources ) {
if( !DownloadData( prefix + resource, client, resource, form, ref i ) ) return false;
}
return true;
}
static bool DownloadData( string uri, WebClient client, string output, MainForm form, ref int i ) {
i++;
if( File.Exists( output ) ) return true;
form.Text = MainForm.AppName + " - fetching " + output + "(" + i + "/" + resourcesCount + ")";
try {
client.DownloadFile( resUri + uri, output );
} catch( WebException ex ) {
Program.LogException( ex );
MessageBox.Show( "Unable to download or save " + output, "Failed to download or save resource", MessageBoxButtons.OK, MessageBoxIcon.Error );
return false;
}
return true;
}
public bool CheckAllResourcesExist() {
return CheckResourcesExist( coreList ) && CheckResourcesExist( mobsList );
}
static bool CheckResourcesExist( params string[] resources ) {
foreach( string file in resources ) {
if( !File.Exists( file ) )
return false;
}
return true;
}
}
}