diff --git a/ClassicalSharp.sln b/ClassicalSharp.sln index f812403c2..06c6fef8c 100644 --- a/ClassicalSharp.sln +++ b/ClassicalSharp.sln @@ -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 diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj index 6c3af61e6..46e712c18 100644 --- a/ClassicalSharp/ClassicalSharp.csproj +++ b/ClassicalSharp/ClassicalSharp.csproj @@ -162,7 +162,8 @@ - + + diff --git a/ClassicalSharp/Commands/DefaultCommands.cs b/ClassicalSharp/Commands/DefaultCommands.cs index 9660b954f..b8914a950 100644 --- a/ClassicalSharp/Commands/DefaultCommands.cs +++ b/ClassicalSharp/Commands/DefaultCommands.cs @@ -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 + "\"" ); } diff --git a/ClassicalSharp/Game/Game.cs b/ClassicalSharp/Game/Game.cs index 4ba2dd38e..d228878e3 100644 --- a/ClassicalSharp/Game/Game.cs +++ b/ClassicalSharp/Game/Game.cs @@ -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(); } diff --git a/ClassicalSharp/Model/ChickenModel.cs b/ClassicalSharp/Model/ChickenModel.cs index b5e79b1ce..90b85e790 100644 --- a/ClassicalSharp/Model/ChickenModel.cs +++ b/ClassicalSharp/Model/ChickenModel.cs @@ -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() { diff --git a/ClassicalSharp/Model/CreeperModel.cs b/ClassicalSharp/Model/CreeperModel.cs index 277fca294..7a22c5987 100644 --- a/ClassicalSharp/Model/CreeperModel.cs +++ b/ClassicalSharp/Model/CreeperModel.cs @@ -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() { diff --git a/ClassicalSharp/Model/PigModel.cs b/ClassicalSharp/Model/PigModel.cs index de7aff27d..5cf377392 100644 --- a/ClassicalSharp/Model/PigModel.cs +++ b/ClassicalSharp/Model/PigModel.cs @@ -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() { diff --git a/ClassicalSharp/Model/PlayerModel.cs b/ClassicalSharp/Model/PlayerModel.cs index 1026e3c46..283dcb45b 100644 --- a/ClassicalSharp/Model/PlayerModel.cs +++ b/ClassicalSharp/Model/PlayerModel.cs @@ -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 ) { diff --git a/ClassicalSharp/Model/SheepModel.cs b/ClassicalSharp/Model/SheepModel.cs index 6634f0dfa..1a39a05d0 100644 --- a/ClassicalSharp/Model/SheepModel.cs +++ b/ClassicalSharp/Model/SheepModel.cs @@ -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() { diff --git a/ClassicalSharp/Model/SkeletonModel.cs b/ClassicalSharp/Model/SkeletonModel.cs index d6a29463d..ce0fccdd7 100644 --- a/ClassicalSharp/Model/SkeletonModel.cs +++ b/ClassicalSharp/Model/SkeletonModel.cs @@ -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 ) { diff --git a/ClassicalSharp/Model/SpiderModel.cs b/ClassicalSharp/Model/SpiderModel.cs index 020c73966..df6a54a97 100644 --- a/ClassicalSharp/Model/SpiderModel.cs +++ b/ClassicalSharp/Model/SpiderModel.cs @@ -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() { diff --git a/ClassicalSharp/Model/ZombieModel.cs b/ClassicalSharp/Model/ZombieModel.cs index aa378c717..6cfc174d3 100644 --- a/ClassicalSharp/Model/ZombieModel.cs +++ b/ClassicalSharp/Model/ZombieModel.cs @@ -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 ) { diff --git a/ClassicalSharp/Program.cs b/ClassicalSharp/Program.cs index 38ac9d72e..acb050f17 100644 --- a/ClassicalSharp/Program.cs +++ b/ClassicalSharp/Program.cs @@ -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.." ); diff --git a/ClassicalSharp/Rendering/StandardEnvRenderer.cs b/ClassicalSharp/Rendering/StandardEnvRenderer.cs index 71e81c10b..fdcd78fa8 100644 --- a/ClassicalSharp/Rendering/StandardEnvRenderer.cs +++ b/ClassicalSharp/Rendering/StandardEnvRenderer.cs @@ -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 ); diff --git a/ClassicalSharp/Rendering/WeatherRenderer.cs b/ClassicalSharp/Rendering/WeatherRenderer.cs index da72ba29b..dbf433622 100644 --- a/ClassicalSharp/Rendering/WeatherRenderer.cs +++ b/ClassicalSharp/Rendering/WeatherRenderer.cs @@ -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 ) { diff --git a/ClassicalSharp/TexturePack/TexturePackExtractor.cs b/ClassicalSharp/TexturePack/TexturePackExtractor.cs new file mode 100644 index 000000000..1e447b8eb --- /dev/null +++ b/ClassicalSharp/TexturePack/TexturePackExtractor.cs @@ -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 ); + } + } + } +} diff --git a/ClassicalSharp/TexturePack/ZipExtractor.cs b/ClassicalSharp/TexturePack/ZipReader.cs similarity index 61% rename from ClassicalSharp/TexturePack/ZipExtractor.cs rename to ClassicalSharp/TexturePack/ZipReader.cs index 5e3669c88..b1babd39d 100644 --- a/ClassicalSharp/TexturePack/ZipExtractor.cs +++ b/ClassicalSharp/TexturePack/ZipReader.cs @@ -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 ProcessZipEntry; + public Func 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 ); - } - } } } diff --git a/ClassicalSharp/Utils/FastBitmap.cs b/ClassicalSharp/Utils/FastBitmap.cs index b249684d5..d3c771af2 100644 --- a/ClassicalSharp/Utils/FastBitmap.cs +++ b/ClassicalSharp/Utils/FastBitmap.cs @@ -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 ); diff --git a/ClassicalSharp/Utils/Utils.cs b/ClassicalSharp/Utils/Utils.cs index fa30efbc1..477c1baa8 100644 --- a/ClassicalSharp/Utils/Utils.cs +++ b/ClassicalSharp/Utils/Utils.cs @@ -14,6 +14,7 @@ namespace ClassicalSharp { public delegate TResult Func(); public delegate TResult Func( T1 arg1 ); public delegate TResult Func( T1 arg1, T2 arg2 ); + public delegate TResult Func( T1 arg1, T2 arg2, T3 arg3 ); public delegate bool TryParseFunc( string s, out T value ); // ################################################################ diff --git a/Launcher/Launcher.csproj b/Launcher/Launcher.csproj index a19d0fbad..395e9449c 100644 --- a/Launcher/Launcher.csproj +++ b/Launcher/Launcher.csproj @@ -33,7 +33,7 @@ true False False - False + True False 4 False @@ -55,6 +55,7 @@ True DEBUG;TRACE obj\ + Project ..\output\release\ @@ -63,6 +64,8 @@ True False TRACE + obj\ + Project @@ -82,9 +85,10 @@ + + - @@ -118,5 +122,14 @@ true + + + {BEB1C785-5CAD-48FF-A886-876BF0A318D4} + ClassicalSharp + + + + + \ No newline at end of file diff --git a/Launcher/Patcher/ResourceFetcher.cs b/Launcher/Patcher/ResourceFetcher.cs new file mode 100644 index 000000000..63ff2c5f1 --- /dev/null +++ b/Launcher/Patcher/ResourceFetcher.cs @@ -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; + } + } +} diff --git a/Launcher/Patcher/ZipWriter.cs b/Launcher/Patcher/ZipWriter.cs new file mode 100644 index 000000000..b8008d768 --- /dev/null +++ b/Launcher/Patcher/ZipWriter.cs @@ -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; + } + } +} diff --git a/Launcher/ResourceFetcher.cs b/Launcher/ResourceFetcher.cs deleted file mode 100644 index 04785f5f8..000000000 --- a/Launcher/ResourceFetcher.cs +++ /dev/null @@ -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; - } - } -}