redesign how file and directory methods work

This commit is contained in:
UnknownShadow200 2018-04-14 15:37:20 +10:00
parent c68eb4df4b
commit 533cca344f
42 changed files with 343 additions and 295 deletions

View File

@ -13,15 +13,13 @@ namespace ClassicalSharp.Gui.Screens {
public LoadLevelScreen(Game game) : base(game) { public LoadLevelScreen(Game game) : base(game) {
titleText = "Select a level"; titleText = "Select a level";
string dir = Path.Combine(Program.AppDirectory, "maps"); string[] rawFiles = Platform.DirectoryFiles("maps");
string[] rawFiles = Directory.GetFiles(dir);
int count = 0; int count = 0;
// Only add map files // Only add map files
for (int i = 0; i < rawFiles.Length; i++) { for (int i = 0; i < rawFiles.Length; i++) {
string file = rawFiles[i]; string file = rawFiles[i];
if (file.EndsWith(".cw") || file.EndsWith(".dat") if (file.EndsWith(".cw") || file.EndsWith(".dat") || file.EndsWith(".fcm") || file.EndsWith(".lvl")) {
|| file.EndsWith(".fcm") || file.EndsWith(".lvl")) {
count++; count++;
} else { } else {
rawFiles[i] = null; rawFiles[i] = null;
@ -38,13 +36,9 @@ namespace ClassicalSharp.Gui.Screens {
} }
protected override void TextButtonClick(Game game, Widget widget) { protected override void TextButtonClick(Game game, Widget widget) {
string path = Path.Combine(Program.AppDirectory, "maps"); string path = Path.Combine("maps", ((ButtonWidget)widget).Text);
path = Path.Combine(path, ((ButtonWidget)widget).Text); if (!Platform.FileExists(path)) return;
if (File.Exists(path))
LoadMap(path);
}
void LoadMap(string path) {
IMapFormatImporter importer = null; IMapFormatImporter importer = null;
if (path.EndsWith(".dat")) { if (path.EndsWith(".dat")) {
importer = new MapDatImporter(); importer = new MapDatImporter();
@ -57,7 +51,7 @@ namespace ClassicalSharp.Gui.Screens {
} }
try { try {
using (FileStream fs = File.OpenRead(path)) { using (Stream fs = Platform.FileOpen(path)) {
int width, height, length; int width, height, length;
game.World.Reset(); game.World.Reset();
game.WorldEvents.RaiseOnNewMap(); game.WorldEvents.RaiseOnNewMap();

View File

@ -15,14 +15,30 @@ namespace ClassicalSharp.Gui.Screens {
InputWidget input; InputWidget input;
const int overwriteIndex = 2; const int overwriteIndex = 2;
static FastColour grey = new FastColour(150, 150, 150); static FastColour grey = new FastColour(150, 150, 150);
string textPath;
public override void Render(double delta) { public override void Render(double delta) {
base.Render(delta); base.Render(delta);
int cX = game.Width / 2, cY = game.Height / 2; int cX = game.Width / 2, cY = game.Height / 2;
game.Graphics.Draw2DQuad(cX - 250, cY + 90, 500, 2, grey); game.Graphics.Draw2DQuad(cX - 250, cY + 90, 500, 2, grey);
if (textPath == null) return; if (textPath == null) return;
SaveMap(textPath);
bool classic = textPath.EndsWith(".cw");
try {
using (Stream fs = Platform.FileCreate(textPath)) {
IMapFormatExporter exporter = null;
if (classic) exporter = new MapCwExporter();
else exporter = new MapSchematicExporter();
exporter.Save(fs, game);
}
} catch (Exception ex) {
ErrorHandler.LogError("saving map", ex);
MakeDescWidget("&cError while trying to save map");
return;
}
game.Chat.Add("&eSaved map to: " + textPath);
game.Gui.SetNewScreen(new PauseScreen(game));
textPath = null; textPath = null;
} }
@ -79,23 +95,23 @@ namespace ClassicalSharp.Gui.Screens {
void SaveSchematic(Game game, Widget widget) { DoSave(widget, ".schematic"); } void SaveSchematic(Game game, Widget widget) { DoSave(widget, ".schematic"); }
void DoSave(Widget widget, string ext) { void DoSave(Widget widget, string ext) {
string text = input.Text.ToString(); string file = input.Text.ToString();
if (text.Length == 0) { if (file.Length == 0) {
MakeDescWidget("&ePlease enter a filename"); return; MakeDescWidget("&ePlease enter a filename"); return;
} }
string file = Path.ChangeExtension(text, ext);
text = Path.Combine(Program.AppDirectory, "maps");
text = Path.Combine(text, file);
file = Path.ChangeExtension(file, ext);
string path = Path.Combine("maps", file);
ButtonWidget btn = (ButtonWidget)widget; ButtonWidget btn = (ButtonWidget)widget;
if (File.Exists(text) && btn.OptName == null) {
if (Platform.FileExists(path) && btn.OptName == null) {
btn.SetText("&cOverwrite existing?"); btn.SetText("&cOverwrite existing?");
btn.OptName = "O"; btn.OptName = "O";
} else { } else {
// NOTE: We don't immediately save here, because otherwise the 'saving...' // NOTE: We don't immediately save here, because otherwise the 'saving...'
// will not be rendered in time because saving is done on the main thread. // will not be rendered in time because saving is done on the main thread.
MakeDescWidget("Saving.."); MakeDescWidget("Saving..");
textPath = text; textPath = path;
RemoveOverwrites(); RemoveOverwrites();
} }
} }
@ -113,27 +129,6 @@ namespace ClassicalSharp.Gui.Screens {
button.SetText(defaultText); button.SetText(defaultText);
} }
string textPath;
void SaveMap(string path) {
bool classic = path.EndsWith(".cw");
try {
if (File.Exists(path))
File.Delete(path);
using (FileStream fs = new FileStream(path, FileMode.CreateNew, FileAccess.Write)) {
IMapFormatExporter exporter = null;
if (classic) exporter = new MapCwExporter();
else exporter = new MapSchematicExporter();
exporter.Save(fs, game);
}
} catch (Exception ex) {
ErrorHandler.LogError("saving map", ex);
MakeDescWidget("&cError while trying to save map");
return;
}
game.Chat.Add("&eSaved map to: " + Path.GetFileName(path));
game.Gui.SetNewScreen(new PauseScreen(game));
}
void MakeDescWidget(string text) { void MakeDescWidget(string text) {
DisposeDescWidget(); DisposeDescWidget();
widgets[widgets.Length - 1] = TextWidget.Create(game, text, textFont) widgets[widgets.Length - 1] = TextWidget.Create(game, text, textFont)

View File

@ -10,19 +10,18 @@ namespace ClassicalSharp.Gui.Screens {
public TexturePackScreen(Game game) : base(game) { public TexturePackScreen(Game game) : base(game) {
titleText = "Select a texture pack zip"; titleText = "Select a texture pack zip";
string dir = Path.Combine(Program.AppDirectory, "texpacks"); entries = Platform.DirectoryFiles("texpacks", "*.zip");
entries = Directory.GetFiles(dir, "*.zip");
for (int i = 0; i < entries.Length; i++) for (int i = 0; i < entries.Length; i++) {
entries[i] = Path.GetFileName(entries[i]); entries[i] = Path.GetFileName(entries[i]);
}
Array.Sort(entries); Array.Sort(entries);
} }
protected override void TextButtonClick(Game game, Widget widget) { protected override void TextButtonClick(Game game, Widget widget) {
string file = ((ButtonWidget)widget).Text; string file = ((ButtonWidget)widget).Text;
string dir = Path.Combine(Program.AppDirectory, "texpacks"); string path = Path.Combine("texpacks", file);
string path = Path.Combine(dir, file); if (!Platform.FileExists(path)) return;
if (!File.Exists(path)) return;
int curPage = currentIndex; int curPage = currentIndex;
game.DefaultTexturePack = file; game.DefaultTexturePack = file;

View File

@ -17,11 +17,11 @@ namespace ClassicalSharp.Audio {
public void Init(Game game) { public void Init(Game game) {
this.game = game; this.game = game;
string path = Path.Combine(Program.AppDirectory, "audio"); if (Platform.DirectoryExists("audio")) {
if (Directory.Exists(path)) files = Platform.DirectoryFiles("audio");
files = Directory.GetFiles(path); } else {
else
files = new string[0]; files = new string[0];
}
game.MusicVolume = GetVolume(OptionsKey.MusicVolume, OptionsKey.UseMusic); game.MusicVolume = GetVolume(OptionsKey.MusicVolume, OptionsKey.UseMusic);
SetMusic(game.MusicVolume); SetMusic(game.MusicVolume);
@ -60,7 +60,8 @@ namespace ClassicalSharp.Audio {
musicFiles = new string[musicCount]; musicFiles = new string[musicCount];
for (int i = 0, j = 0; i < files.Length; i++) { for (int i = 0, j = 0; i < files.Length; i++) {
if (!Utils.CaselessEnds(files[i], ".ogg")) continue; if (!Utils.CaselessEnds(files[i], ".ogg")) continue;
musicFiles[j] = files[i]; j++; musicFiles[j] = Path.GetFileName(files[i]);
j++;
} }
disposingMusic = false; disposingMusic = false;
@ -75,10 +76,9 @@ namespace ClassicalSharp.Audio {
Random rnd = new Random(); Random rnd = new Random();
while (!disposingMusic) { while (!disposingMusic) {
string file = musicFiles[rnd.Next(0, musicFiles.Length)]; string file = musicFiles[rnd.Next(0, musicFiles.Length)];
string path = Path.Combine(Program.AppDirectory, file);
Utils.LogDebug("playing music file: " + file); Utils.LogDebug("playing music file: " + file);
using (FileStream fs = File.OpenRead(path)) { using (Stream fs = Platform.FileOpen(file)) {
OggContainer container = new OggContainer(fs); OggContainer container = new OggContainer(fs);
try { try {
musicOut.SetVolume(game.MusicVolume / 100.0f); musicOut.SetVolume(game.MusicVolume / 100.0f);

View File

@ -65,7 +65,7 @@ namespace ClassicalSharp.Audio {
} }
Sound ReadWave(string file) { Sound ReadWave(string file) {
using (FileStream fs = File.OpenRead(file)) using (Stream fs = File.OpenRead(file))
using (BinaryReader r = new BinaryReader(fs)) using (BinaryReader r = new BinaryReader(fs))
{ {
string fourCC = GetFourCC(r); string fourCC = GetFourCC(r);

View File

@ -126,20 +126,19 @@ namespace ClassicalSharp {
} }
void OpenChatFile(DateTime now) { void OpenChatFile(DateTime now) {
string basePath = Path.Combine(Program.AppDirectory, "logs"); if (!Platform.DirectoryExists("logs")) {
if (!Directory.Exists(basePath)) Platform.DirectoryCreate("logs");
Directory.CreateDirectory(basePath); }
string date = now.ToString("yyyy-MM-dd"); string date = now.ToString("yyyy-MM-dd");
// Ensure multiple instances do not end up overwriting each other's log entries. // Ensure multiple instances do not end up overwriting each other's log entries.
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
string id = i == 0 ? "" : " _" + i; string id = i == 0 ? "" : " _" + i;
string fileName = date + " " + logName + id + ".log"; string path = Path.Combine("logs", date + " " + logName + id + ".log");
string path = Path.Combine(basePath, fileName);
FileStream stream = null; Stream stream = null;
try { try {
stream = File.Open(path, FileMode.Append, FileAccess.Write, FileShare.Read); stream = Platform.FileAppend(path);
} catch (IOException ex) { } catch (IOException ex) {
int hresult = Marshal.GetHRForException(ex); int hresult = Marshal.GetHRForException(ex);
uint errorCode = (uint)hresult & 0xFFFF; uint errorCode = (uint)hresult & 0xFFFF;

View File

@ -161,9 +161,12 @@ namespace ClassicalSharp {
defTexturePack = Options.Get(OptionsKey.DefaultTexturePack) ?? "default.zip"; defTexturePack = Options.Get(OptionsKey.DefaultTexturePack) ?? "default.zip";
TexturePack extractor = new TexturePack(); TexturePack extractor = new TexturePack();
extractor.Extract("default.zip", this); extractor.Extract("default.zip", this);
// in case the user's default texture pack doesn't have all required textures // in case the user's default texture pack doesn't have all required textures
if (DefaultTexturePack != "default.zip") string defTexPack = DefaultTexturePack;
extractor.Extract(DefaultTexturePack, this); if (defTexPack != "default.zip") {
extractor.Extract(defTexPack, this);
}
} }
void LoadOptions() { void LoadOptions() {

View File

@ -9,7 +9,6 @@ using ClassicalSharp.Commands;
using ClassicalSharp.Entities; using ClassicalSharp.Entities;
using ClassicalSharp.Events; using ClassicalSharp.Events;
using ClassicalSharp.GraphicsAPI; using ClassicalSharp.GraphicsAPI;
using ClassicalSharp.Gui;
using ClassicalSharp.Map; using ClassicalSharp.Map;
using ClassicalSharp.Mode; using ClassicalSharp.Mode;
using ClassicalSharp.Model; using ClassicalSharp.Model;
@ -231,9 +230,8 @@ namespace ClassicalSharp {
/// this method returns "default.zip". </remarks> /// this method returns "default.zip". </remarks>
public string DefaultTexturePack { public string DefaultTexturePack {
get { get {
string path = Path.Combine(Program.AppDirectory, "texpacks"); string texPath = Path.Combine("texpacks", defTexturePack);
path = Path.Combine(path, defTexturePack); return Platform.FileExists(texPath) && !ClassicMode ? defTexturePack : "default.zip";
return File.Exists(path) && !ClassicMode ? defTexturePack : "default.zip";
} }
set { set {
defTexturePack = value; defTexturePack = value;

View File

@ -202,13 +202,14 @@ namespace ClassicalSharp {
} }
void TakeScreenshot() { void TakeScreenshot() {
string path = PathIO.Combine(Program.AppDirectory, "screenshots"); if (!Platform.DirectoryExists("screenshots")) {
if (!Directory.Exists(path)) Platform.DirectoryCreate("screenshots");
Directory.CreateDirectory(path); }
string timestamp = DateTime.Now.ToString("dd-MM-yyyy-HH-mm-ss"); string timestamp = DateTime.Now.ToString("dd-MM-yyyy-HH-mm-ss");
string file = "screenshot_" + timestamp + ".png"; string file = "screenshot_" + timestamp + ".png";
path = PathIO.Combine(path, file); string path = PathIO.Combine("screenshots", file);
Graphics.TakeScreenshot(path, Width, Height); Graphics.TakeScreenshot(path, Width, Height);
Chat.Add("&eTaken screenshot as: " + file); Chat.Add("&eTaken screenshot as: " + file);
screenshotRequested = false; screenshotRequested = false;

View File

@ -21,20 +21,20 @@ namespace ClassicalSharp {
internal static Game game; internal static Game game;
internal static List<string> LoadAll() { internal static List<string> LoadAll() {
string dir = Path.Combine(Program.AppDirectory, "plugins"); if (!Platform.DirectoryExists("plugins")) {
if (!Directory.Exists(dir)) Platform.DirectoryCreate("plugins");
Directory.CreateDirectory(dir); }
Accepted = new EntryList("plugins", "accepted.txt"); Accepted = new EntryList("plugins", "accepted.txt");
Denied = new EntryList("plugins", "denied.txt"); Denied = new EntryList("plugins", "denied.txt");
Accepted.Load(); Accepted.Load();
Denied.Load(); Denied.Load();
return LoadPlugins(dir); return LoadPlugins();
} }
static List<string> LoadPlugins(string dir) { static List<string> LoadPlugins() {
string[] dlls = Directory.GetFiles(dir, "*.dll"); string[] dlls = Platform.DirectoryFiles("plugins", "*.dll");
List<string> nonLoaded = null; List<string> nonLoaded = null;
for (int i = 0; i < dlls.Length; i++) { for (int i = 0; i < dlls.Length; i++) {
@ -55,8 +55,7 @@ namespace ClassicalSharp {
public static void Load(string pluginName, bool needsInit) { public static void Load(string pluginName, bool needsInit) {
try { try {
string dir = Path.Combine(Program.AppDirectory, "plugins"); string path = Path.Combine("plguins", pluginName + ".dll");
string path = Path.Combine(dir, pluginName + ".dll");
Assembly lib = Assembly.LoadFrom(path); Assembly lib = Assembly.LoadFrom(path);
Type[] types = lib.GetTypes(); Type[] types = lib.GetTypes();

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using OpenTK; using OpenTK;
@ -553,9 +554,10 @@ namespace ClassicalSharp.GraphicsAPI {
device.GetRenderTargetData(backbuffer, tempSurface); device.GetRenderTargetData(backbuffer, tempSurface);
LockedRectangle rect = tempSurface.LockRectangle(LockFlags.ReadOnly | LockFlags.NoDirtyUpdate); LockedRectangle rect = tempSurface.LockRectangle(LockFlags.ReadOnly | LockFlags.NoDirtyUpdate);
using (Bitmap bmp = new Bitmap(width, height, width * sizeof(int), using (Bitmap bmp = new Bitmap(width, height, width * sizeof(int), PixelFormat.Format32bppRgb, rect.DataPointer)) {
PixelFormat.Format32bppRgb, rect.DataPointer)) { using (Stream fs = Platform.FileCreate(output)) {
bmp.Save(output, ImageFormat.Png); Platform.WriteBmp(bmp, fs);
}
} }
tempSurface.UnlockRectangle(); tempSurface.UnlockRectangle();
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using OpenTK; using OpenTK;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
@ -551,7 +552,10 @@ namespace ClassicalSharp.GraphicsAPI {
using (FastBitmap fastBmp = new FastBitmap(bmp, true, false)) using (FastBitmap fastBmp = new FastBitmap(bmp, true, false))
GL.ReadPixels(0, 0, width, height, GlPixelFormat.Bgra, PixelType.UnsignedByte, fastBmp.Scan0); GL.ReadPixels(0, 0, width, height, GlPixelFormat.Bgra, PixelType.UnsignedByte, fastBmp.Scan0);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
bmp.Save(output, ImageFormat.Png);
using (Stream fs = Platform.FileCreate(output)) {
Platform.WriteBmp(bmp, fs);
}
} }
} }

View File

@ -342,8 +342,9 @@ namespace ClassicalSharp.GraphicsAPI {
} }
} }
} }
using (FileStream fs = File.Create(output)) using (Stream fs = Platform.FileCreate(output)) {
Platform.WriteBmp(bmp, fs); Platform.WriteBmp(bmp, fs);
}
} }
} }

View File

@ -74,12 +74,12 @@ namespace ClassicalSharp {
internal void DownloadTexturePack(string url) { internal void DownloadTexturePack(string url) {
if (game.DeniedUrls.Has(url)) return; if (game.DeniedUrls.Has(url)) return;
string path = TextureCache.MakePath(url), etag = null; string etag = null;
DateTime lastModified = DateTime.MinValue; DateTime lastModified = DateTime.MinValue;
if (File.Exists(path)) { if (TextureCache.HasUrl(url)) {
lastModified = TextureCache.GetLastModified(url, path, game.LastModified); lastModified = TextureCache.GetLastModified(url, game.LastModified);
etag = TextureCache.GetETag(url, path, game.ETags); etag = TextureCache.GetETag(url, game.ETags);
} }
TexturePack.ExtractCurrent(game, url); TexturePack.ExtractCurrent(game, url);

View File

@ -41,13 +41,14 @@ namespace ClassicalSharp {
} }
public void LoadIcon() { public void LoadIcon() {
string launcherPath = Path.Combine(Program.AppDirectory, "Launcher2.exe"); string launcherFile = "Launcher2.exe";
if (!File.Exists(launcherPath)) { if (!Platform.FileExists(launcherFile)) {
launcherPath = Path.Combine(Program.AppDirectory, "Launcher.exe"); launcherFile = "Launcher.exe";
} }
if (!File.Exists(launcherPath)) return; if (!Platform.FileExists(launcherFile)) return;
try { try {
string launcherPath = Path.Combine(Platform.AppDirectory, launcherFile);
Icon = Icon.ExtractAssociatedIcon(launcherPath); Icon = Icon.ExtractAssociatedIcon(launcherPath);
} catch (Exception ex) { } catch (Exception ex) {
ErrorHandler.LogError("DesktopWindow.LoadIcon()", ex); ErrorHandler.LogError("DesktopWindow.LoadIcon()", ex);

View File

@ -13,6 +13,8 @@ namespace ClassicalSharp {
/// <summary> Abstracts away platform specific operations. </summary> /// <summary> Abstracts away platform specific operations. </summary>
public static class Platform { public static class Platform {
public static string AppDirectory;
public static bool ValidBitmap(Bitmap bmp) { public static bool ValidBitmap(Bitmap bmp) {
// Mono seems to be returning a bitmap with a native pointer of zero in some weird cases. // Mono seems to be returning a bitmap with a native pointer of zero in some weird cases.
// We can detect this as property access raises an ArgumentException. // We can detect this as property access raises an ArgumentException.
@ -70,5 +72,65 @@ namespace ClassicalSharp {
return config != null && config == Bitmap.Config.Argb8888; return config != null && config == Bitmap.Config.Argb8888;
#endif #endif
} }
public static FileStream FileOpen(string relPath) {
string path = Path.Combine(AppDirectory, relPath);
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
}
public static FileStream FileCreate(string relPath) {
string path = Path.Combine(AppDirectory, relPath);
return new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
}
public static FileStream FileAppend(string relPath) {
string path = Path.Combine(AppDirectory, relPath);
return new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read);
}
public static bool FileExists(string relPath) {
string path = Path.Combine(AppDirectory, relPath);
return File.Exists(path);
}
public static DateTime FileGetWriteTime(string relPath) {
string path = Path.Combine(AppDirectory, relPath);
return File.GetLastWriteTimeUtc(path);
}
public static void FileSetWriteTime(string relPath, DateTime time) {
string path = Path.Combine(AppDirectory, relPath);
File.SetLastWriteTimeUtc(path, time);
}
public static bool DirectoryExists(string relPath) {
string path = Path.Combine(AppDirectory, relPath);
return Directory.Exists(path);
}
public static void DirectoryCreate(string relPath) {
string path = Path.Combine(AppDirectory, relPath);
Directory.CreateDirectory(path);
}
public static string[] DirectoryFiles(string relPath) {
string path = Path.Combine(AppDirectory, relPath);
return Directory.GetFiles(relPath);
}
public static string[] DirectoryFiles(string relPath, string filter) {
string path = Path.Combine(AppDirectory, relPath);
return Directory.GetFiles(relPath, filter);
}
public static void WriteAllText(string relPath, string text) {
string path = Path.Combine(AppDirectory, relPath);
File.WriteAllText(path, text);
}
public static void WriteAllBytes(string relPath, byte[] data) {
string path = Path.Combine(AppDirectory, relPath);
File.WriteAllBytes(path, data);
}
} }
} }

View File

@ -3,7 +3,6 @@ using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Windows.Forms; using System.Windows.Forms;
using ClassicalSharp.Textures;
using OpenTK; using OpenTK;
namespace ClassicalSharp { namespace ClassicalSharp {
@ -12,21 +11,21 @@ namespace ClassicalSharp {
public const string AppName = "ClassicalSharp 0.99.9.94"; public const string AppName = "ClassicalSharp 0.99.9.94";
public static string AppDirectory;
#if !LAUNCHER #if !LAUNCHER
[STAThread] [STAThread]
static void Main(string[] args) { static void Main(string[] args) {
AppDirectory = AppDomain.CurrentDomain.BaseDirectory; Platform.AppDirectory = AppDomain.CurrentDomain.BaseDirectory;
CleanupMainDirectory(); CleanupMainDirectory();
string path = Path.Combine(Program.AppDirectory, "texpacks"); string defPath = Path.Combine("texpacks", "default.zip");
if (!File.Exists(Path.Combine(path, "default.zip"))) { if (!Platform.FileExists(defPath)) {
Message("default.zip not found, try running the launcher first."); return; ErrorHandler.ShowDialog("Missing file", "default.zip not found, try running the launcher first.");
return;
} }
path = Path.Combine(AppDirectory, "OpenTK.dll"); if (!Platform.FileExists("OpenTK.dll")) {
if (!File.Exists(path)) { ErrorHandler.ShowDialog("Missing file", "OpenTK.dll needs to be in the same folder as the game");
Message("OpenTK.dll needs to be in the same folder as the game"); return; return;
} }
// NOTE: we purposely put this in another method, as we need to ensure // NOTE: we purposely put this in another method, as we need to ensure
@ -36,8 +35,7 @@ namespace ClassicalSharp {
} }
static void RunGame(string[] args) { static void RunGame(string[] args) {
string logPath = Path.Combine(AppDirectory, "client.log"); ErrorHandler.InstallHandler("client.log");
ErrorHandler.InstallHandler(logPath);
OpenTK.Configuration.SkipPerfCountersHack(); OpenTK.Configuration.SkipPerfCountersHack();
Utils.LogDebug("Starting " + AppName + ".."); Utils.LogDebug("Starting " + AppName + "..");
@ -70,9 +68,6 @@ namespace ClassicalSharp {
} }
} }
// put in separate function, because we don't want to load winforms assembly if possible
static void Message(string message) { MessageBox.Show(message, "Missing file"); }
static void RunMultiplayer(string[] args, bool nullContext, int width, int height) { static void RunMultiplayer(string[] args, bool nullContext, int width, int height) {
IPAddress ip = null; IPAddress ip = null;
if (!IPAddress.TryParse(args[2], out ip)) { if (!IPAddress.TryParse(args[2], out ip)) {
@ -97,14 +92,14 @@ namespace ClassicalSharp {
} }
#endif #endif
internal static void CleanupMainDirectory() { public static void CleanupMainDirectory() {
string mapPath = Path.Combine(Program.AppDirectory, "maps"); if (!Platform.DirectoryExists("maps")) {
if (!Directory.Exists(mapPath)) Platform.DirectoryCreate("maps");
Directory.CreateDirectory(mapPath); }
string texPath = Path.Combine(Program.AppDirectory, "texpacks"); if (!Platform.DirectoryExists("texpacks")) {
if (!Directory.Exists(texPath)) Platform.DirectoryCreate("texpacks");
Directory.CreateDirectory(texPath); }
} }
} }
} }

View File

@ -43,7 +43,7 @@ namespace ClassicalSharp.Textures {
animsBuffer = new FastBitmap(animBmp, true, true); animsBuffer = new FastBitmap(animBmp, true, true);
} else if (e.Name == "animations.txt" || e.Name == "animation.txt") { } else if (e.Name == "animations.txt" || e.Name == "animation.txt") {
MemoryStream stream = new MemoryStream(e.Data); MemoryStream stream = new MemoryStream(e.Data);
StreamReader reader = new StreamReader(stream); StreamReader reader = new StreamReader(stream, false);
ReadAnimationsDescription(reader); ReadAnimationsDescription(reader);
} else if (e.Name == "uselavaanim") { } else if (e.Name == "uselavaanim") {
useLavaAnim = true; useLavaAnim = true;

View File

@ -25,12 +25,11 @@ namespace ClassicalSharp.Textures {
} }
public bool Load() { public bool Load() {
string path = Path.Combine(Program.AppDirectory, folder); string path = Path.Combine(folder, file);
path = Path.Combine(path, file); if (!Platform.FileExists(path)) return true;
if (!File.Exists(path)) return true;
try { try {
using (Stream fs = File.OpenRead(path)) using (Stream fs = Platform.FileOpen(path))
using (StreamReader reader = new StreamReader(fs, false)) using (StreamReader reader = new StreamReader(fs, false))
{ {
string line; string line;
@ -49,11 +48,12 @@ namespace ClassicalSharp.Textures {
public bool Save() { public bool Save() {
try { try {
string path = Path.Combine(Program.AppDirectory, folder); if (!Platform.DirectoryExists(folder)) {
if (!Directory.Exists(path)) Platform.DirectoryCreate(folder);
Directory.CreateDirectory(path); }
using (Stream fs = File.Create(Path.Combine(path, file))) string path = Path.Combine(folder, file);
using (Stream fs = Platform.FileCreate(path))
using (StreamWriter writer = new StreamWriter(fs)) using (StreamWriter writer = new StreamWriter(fs))
{ {
for (int i = 0; i < Entries.Count; i++) for (int i = 0; i < Entries.Count; i++)

View File

@ -14,18 +14,16 @@ namespace ClassicalSharp.Textures {
public static class TextureCache { public static class TextureCache {
/// <summary> Gets whether the given url has data associated with it in the cache. </summary> /// <summary> Gets whether the given url has data associated with it in the cache. </summary>
public static bool HasUrl(string url) { public static bool HasUrl(string url) { return Platform.FileExists(MakePath(url)); }
return File.Exists(MakePath(url));
}
/// <summary> Gets the stream of data associated with the url from the cache, returning null if the /// <summary> Gets the stream of data associated with the url from the cache, returning null if the
/// data for the url was not found in the cache. </summary> /// data for the url was not found in the cache. </summary>
public static FileStream GetStream(string url) { public static FileStream GetStream(string url) {
string path = MakePath(url); string path = MakePath(url);
if (!File.Exists(path)) return null; if (!Platform.FileExists(path)) return null;
try { try {
return File.OpenRead(path); return Platform.FileOpen(path);
} catch (IOException ex) { } catch (IOException ex) {
ErrorHandler.LogError("Cache.GetData", ex); ErrorHandler.LogError("Cache.GetData", ex);
return null; return null;
@ -34,17 +32,18 @@ namespace ClassicalSharp.Textures {
/// <summary> Gets the time the data associated with the url from the cache was last modified, /// <summary> Gets the time the data associated with the url from the cache was last modified,
/// returning DateTime.MinValue if data for the url was not found in the cache. </summary> /// returning DateTime.MinValue if data for the url was not found in the cache. </summary>
public static DateTime GetLastModified(string url, string path, EntryList tags) { public static DateTime GetLastModified(string url, EntryList tags) {
string entry = GetFromTags(url, tags); string entry = GetFromTags(url, tags);
long ticks = 0; long ticks = 0;
if (entry != null && long.TryParse(entry, out ticks)) { if (entry != null && long.TryParse(entry, out ticks)) {
return new DateTime(ticks, DateTimeKind.Utc); return new DateTime(ticks, DateTimeKind.Utc);
} else { } else {
return File.GetLastWriteTimeUtc(path); string path = MakePath(url);
return Platform.FileGetWriteTime(path);
} }
} }
public static string GetETag(string url, string path, EntryList tags) { public static string GetETag(string url, EntryList tags) {
return GetFromTags(url, tags); return GetFromTags(url, tags);
} }
@ -67,12 +66,13 @@ namespace ClassicalSharp.Textures {
public static void Add(string url, Bitmap bmp) { public static void Add(string url, Bitmap bmp) {
string path = MakePath(url); string path = MakePath(url);
try { try {
string basePath = PathIO.Combine(Program.AppDirectory, Folder); if (!Platform.DirectoryExists(Folder)) {
if (!Directory.Exists(basePath)) Platform.DirectoryCreate(Folder);
Directory.CreateDirectory(basePath); }
using (FileStream fs = File.Create(path)) using (Stream fs = Platform.FileCreate(path)) {
Platform.WriteBmp(bmp, fs); Platform.WriteBmp(bmp, fs);
}
} catch (IOException ex) { } catch (IOException ex) {
ErrorHandler.LogError("Cache.AddToCache", ex); ErrorHandler.LogError("Cache.AddToCache", ex);
} }
@ -82,10 +82,11 @@ namespace ClassicalSharp.Textures {
public static void Add(string url, byte[] data) { public static void Add(string url, byte[] data) {
string path = MakePath(url); string path = MakePath(url);
try { try {
string basePath = PathIO.Combine(Program.AppDirectory, Folder); if (!Platform.DirectoryExists(Folder)) {
if (!Directory.Exists(basePath)) Platform.DirectoryCreate(Folder);
Directory.CreateDirectory(basePath); }
File.WriteAllBytes(path, data);
Platform.WriteAllBytes(path, data);
} catch (IOException ex) { } catch (IOException ex) {
ErrorHandler.LogError("Cache.AddToCache", ex); ErrorHandler.LogError("Cache.AddToCache", ex);
} }
@ -114,12 +115,7 @@ namespace ClassicalSharp.Textures {
const string Folder = "texturecache"; const string Folder = "texturecache";
public static string MakePath(string url) { return PathIO.Combine(Folder, CRC32(url)); }
public static string MakePath(string url) {
string crc32 = CRC32(url);
string basePath = PathIO.Combine(Program.AppDirectory, Folder);
return PathIO.Combine(basePath, crc32);
}
static string CRC32(string url) { static string CRC32(string url) {
byte[] data = Encoding.UTF8.GetBytes(url); byte[] data = Encoding.UTF8.GetBytes(url);

View File

@ -17,11 +17,11 @@ namespace ClassicalSharp.Textures {
Game game; Game game;
public void Extract(string path, Game game) { public void Extract(string file, Game game) {
path = PathIO.Combine("texpacks", path); string path = PathIO.Combine("texpacks", file);
path = PathIO.Combine(Program.AppDirectory, path); using (Stream fs = Platform.FileOpen(path)) {
using (Stream fs = File.OpenRead(path))
Extract(fs, game); Extract(fs, game);
}
} }
public void Extract(Stream stream, Game game) { public void Extract(Stream stream, Game game) {

View File

@ -11,14 +11,12 @@ namespace ClassicalSharp {
/// and also logs it to a specified log file. </summary> /// and also logs it to a specified log file. </summary>
public static class ErrorHandler { public static class ErrorHandler {
static string logFile = "crash.log";
static string fileName = "crash.log"; static string fileName = "crash.log";
/// <summary> Adds a handler for when a unhandled exception occurs, unless /// <summary> Adds a handler for when a unhandled exception occurs, unless
/// a debugger is attached to the process in which case this does nothing. </summary> /// a debugger is attached to the process in which case this does nothing. </summary>
public static void InstallHandler(string logFile) { public static void InstallHandler(string logFile) {
ErrorHandler.logFile = logFile; fileName = logFile;
fileName = Path.GetFileName(logFile);
if (!Debugger.IsAttached) if (!Debugger.IsAttached)
AppDomain.CurrentDomain.UnhandledException += UnhandledException; AppDomain.CurrentDomain.UnhandledException += UnhandledException;
} }
@ -40,7 +38,9 @@ namespace ClassicalSharp {
Exception ex = (Exception)e.ExceptionObject; Exception ex = (Exception)e.ExceptionObject;
bool wroteToCrashLog = true; bool wroteToCrashLog = true;
try { try {
using (StreamWriter w = new StreamWriter(logFile, true)) { using (Stream fs = Platform.FileAppend(fileName))
using (StreamWriter w = new StreamWriter(fs))
{
w.WriteLine("=== crash occurred ==="); w.WriteLine("=== crash occurred ===");
w.WriteLine("Time: " + DateTime.Now); w.WriteLine("Time: " + DateTime.Now);
@ -72,12 +72,12 @@ namespace ClassicalSharp {
string line1 = "ClassicalSharp crashed."; string line1 = "ClassicalSharp crashed.";
if (wroteToCrashLog) { if (wroteToCrashLog) {
line1 += " The cause has also been logged to \"" + fileName + "\" in " + Program.AppDirectory; line1 += " The cause has also been logged to \"" + fileName + "\" in " + Platform.AppDirectory;
} }
string line2 = "Please report the crash to github.com/UnknownShadow200/ClassicalSharp/issues so we can fix it."; string line2 = "Please report the crash to github.com/UnknownShadow200/ClassicalSharp/issues so we can fix it.";
line2 += Environment.NewLine + Environment.NewLine + Format(ex); line2 += Environment.NewLine + Environment.NewLine + Format(ex);
MessageBox.Show(line1 + Environment.NewLine + Environment.NewLine + line2, "We're sorry"); ShowDialog("We're sorry", line1 + Environment.NewLine + Environment.NewLine + line2);
Environment.Exit(1); Environment.Exit(1);
} }
@ -93,16 +93,23 @@ namespace ClassicalSharp {
/// <summary> Logs an error that occured at the specified location to the log file. </summary> /// <summary> Logs an error that occured at the specified location to the log file. </summary>
public static bool LogError(string location, string text) { public static bool LogError(string location, string text) {
try { try {
using (StreamWriter writer = new StreamWriter(logFile, true)) { using (Stream fs = Platform.FileAppend(fileName))
writer.WriteLine("=== handled error ==="); using (StreamWriter w = new StreamWriter(fs))
writer.WriteLine("Occured when: " + location); {
writer.WriteLine(text); w.WriteLine("=== handled error ===");
writer.WriteLine(); w.WriteLine("Occured when: " + location);
w.WriteLine(text);
w.WriteLine();
} }
} catch (Exception) { } catch (Exception) {
return false; return false;
} }
return true; return true;
} }
// put in separate function, because we don't want to load winforms assembly if possible
public static void ShowDialog(string title, string msg) {
MessageBox.Show(msg, title);
}
} }
} }

View File

@ -190,14 +190,8 @@ namespace ClassicalSharp {
public static bool Load() { public static bool Load() {
// Both of these are from when running from the launcher
if (Program.AppDirectory == null)
Program.AppDirectory = AppDomain.CurrentDomain.BaseDirectory;
Program.CleanupMainDirectory();
try { try {
string path = Path.Combine(Program.AppDirectory, Filename); using (Stream fs = Platform.FileOpen(Filename))
using (Stream fs = File.OpenRead(path))
using (StreamReader reader = new StreamReader(fs, false)) using (StreamReader reader = new StreamReader(fs, false))
{ {
LoadFrom(reader); LoadFrom(reader);
@ -239,8 +233,7 @@ namespace ClassicalSharp {
public static bool Save() { public static bool Save() {
try { try {
string path = Path.Combine(Program.AppDirectory, Filename); using (Stream fs = Platform.FileCreate(Filename))
using (Stream fs = File.Create(path))
using (StreamWriter writer = new StreamWriter(fs)) using (StreamWriter writer = new StreamWriter(fs))
{ {
SaveTo(writer); SaveTo(writer);

View File

@ -1,6 +1,6 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT // ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System; using System;
using System.IO; using ClassicalSharp;
using ClassicalSharp.Network; using ClassicalSharp.Network;
using Launcher.Gui.Views; using Launcher.Gui.Views;
using Launcher.Patcher; using Launcher.Patcher;
@ -90,7 +90,7 @@ namespace Launcher.Gui.Screens {
void GotoNextMenu(int x, int y) { void GotoNextMenu(int x, int y) {
game.Downloader.Clear(); game.Downloader.Clear();
if (File.Exists("options.txt")) { if (Platform.FileExists("options.txt")) {
game.SetScreen(new MainScreen(game)); game.SetScreen(new MainScreen(game));
} else { } else {
game.SetScreen(new ChooseModeScreen(game, true)); game.SetScreen(new ChooseModeScreen(game, true));

View File

@ -39,11 +39,11 @@ namespace Launcher.Gui.Views {
const string dateFormat = "dd-MM-yyyy HH:mm"; const string dateFormat = "dd-MM-yyyy HH:mm";
protected override void MakeWidgets() { protected override void MakeWidgets() {
widgetIndex = 0; widgetIndex = 0;
string exePath = Path.Combine(Program.AppDirectory, "ClassicalSharp.exe"); DateTime writeTime = Platform.FileGetWriteTime("ClassicalSharp.exe");
Makers.Label(this, "Your build:", textFont) Makers.Label(this, "Your build:", textFont)
.SetLocation(Anchor.Centre, Anchor.Centre, -55, -120); .SetLocation(Anchor.Centre, Anchor.Centre, -55, -120);
string yourBuild = File.GetLastWriteTime(exePath).ToString(dateFormat); string yourBuild = writeTime.ToLocalTime().ToString(dateFormat);
Makers.Label(this, yourBuild, textFont) Makers.Label(this, yourBuild, textFont)
.SetLocation(Anchor.Centre, Anchor.Centre, 70, -120); .SetLocation(Anchor.Centre, Anchor.Centre, 70, -120);

View File

@ -17,28 +17,31 @@ namespace Launcher {
fontPng = false; terrainPng = false; fontPng = false; terrainPng = false;
Options.Load(); Options.Load();
LauncherSkin.LoadFromOptions(); LauncherSkin.LoadFromOptions();
if (Options.Get("nostalgia-classicbg") != null)
if (Options.Get("nostalgia-classicbg") != null) {
ClassicBackground = Options.GetBool("nostalgia-classicbg", false); ClassicBackground = Options.GetBool("nostalgia-classicbg", false);
else } else {
ClassicBackground = Options.GetBool("mode-classic", false); ClassicBackground = Options.GetBool("mode-classic", false);
}
string texDir = Path.Combine(Program.AppDirectory, "texpacks");
string texPack = Options.Get(OptionsKey.DefaultTexturePack) ?? "default.zip"; string texPack = Options.Get(OptionsKey.DefaultTexturePack) ?? "default.zip";
texPack = Path.Combine(texDir, texPack); string texPath = Path.Combine("texpacks", texPack);
if (!File.Exists(texPack)) if (!Platform.FileExists(texPath)) {
texPack = Path.Combine(texDir, "default.zip"); texPath = Path.Combine("texpacks", "default.zip");
if (!File.Exists(texPack)) return; }
if (!Platform.FileExists(texPath)) return;
ExtractTexturePack(texPack); ExtractTexturePack(texPath);
// user selected texture pack is missing some required .png files
if (!fontPng || !terrainPng) { if (!fontPng || !terrainPng) {
texPack = Path.Combine(texDir, "default.zip"); texPath = Path.Combine("texpacks", "default.zip");
ExtractTexturePack(texPack); ExtractTexturePack(texPath);
} }
} }
void ExtractTexturePack(string texPack) { void ExtractTexturePack(string relPath) {
using (Stream fs = new FileStream(texPack, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (Stream fs = Platform.FileOpen(relPath)) {
ZipReader reader = new ZipReader(); ZipReader reader = new ZipReader();
reader.SelectZipEntry = SelectZipEntry; reader.SelectZipEntry = SelectZipEntry;
reader.ProcessZipEntry = ProcessZipEntry; reader.ProcessZipEntry = ProcessZipEntry;

View File

@ -74,6 +74,8 @@ namespace Launcher {
Window.FocusedChanged += FocusedChanged; Window.FocusedChanged += FocusedChanged;
Window.WindowStateChanged += Resize; Window.WindowStateChanged += Resize;
Window.Keyboard.KeyDown += KeyDown; Window.Keyboard.KeyDown += KeyDown;
ClassicalSharp.Program.CleanupMainDirectory();
LoadFont(); LoadFont();
logoFont = new Font(FontName, 32, FontStyle.Regular); logoFont = new Font(FontName, 32, FontStyle.Regular);

View File

@ -1,6 +1,7 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT // ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System; using System;
using System.IO; using System.IO;
using ClassicalSharp;
using ClassicalSharp.Textures; using ClassicalSharp.Textures;
namespace Launcher.Patcher { namespace Launcher.Patcher {
@ -8,21 +9,21 @@ namespace Launcher.Patcher {
public sealed class ResourceChecker { public sealed class ResourceChecker {
public void CheckResourceExistence() { public void CheckResourceExistence() {
string audioPath = Path.Combine(Program.AppDirectory, "audio"); if (!Platform.DirectoryExists("audio")) {
if (!Directory.Exists(audioPath)) Platform.DirectoryCreate("audio");
Directory.CreateDirectory(audioPath); }
DigSoundsExist = CheckDigSoundsExist(); DigSoundsExist = CheckDigSoundsExist();
StepSoundsExist = CheckStepSoundsExist(); StepSoundsExist = CheckStepSoundsExist();
AllResourcesExist = DigSoundsExist && StepSoundsExist; AllResourcesExist = DigSoundsExist && StepSoundsExist;
string texDir = Path.Combine(Program.AppDirectory, "texpacks"); string defPath = Path.Combine("texpacks", "default.zip");
string zipPath = Path.Combine(texDir, "default.zip"); if (Platform.FileExists(defPath)) {
bool defaultZipExists = File.Exists(zipPath); CheckDefaultZip(defPath);
if (File.Exists(zipPath)) }
CheckDefaultZip(zipPath);
CheckTexturePack(); CheckTexturePack();
CheckMusic(audioPath); CheckMusic();
CheckSounds(); CheckSounds();
} }
@ -44,11 +45,11 @@ namespace Launcher.Patcher {
} }
} }
void CheckMusic(string audioPath) { void CheckMusic() {
string[] files = ResourceList.MusicFiles; string[] files = ResourceList.MusicFiles;
for (int i = 0; i < files.Length; i++) { for (int i = 0; i < files.Length; i++) {
string file = Path.Combine(audioPath, files[i]); string path = Path.Combine("audio", files[i]);
musicExists[i] = File.Exists(file); musicExists[i] = Platform.FileExists(path);
if (musicExists[i]) continue; if (musicExists[i]) continue;
DownloadSize += musicSizes[i] / 1024f; DownloadSize += musicSizes[i] / 1024f;
@ -73,13 +74,14 @@ namespace Launcher.Patcher {
public int ResourcesCount; public int ResourcesCount;
internal bool[] musicExists = new bool[7]; internal bool[] musicExists = new bool[7];
void CheckDefaultZip(string path) { void CheckDefaultZip(string relPath) {
ZipReader reader = new ZipReader(); ZipReader reader = new ZipReader();
reader.SelectZipEntry = SelectZipEntry; reader.SelectZipEntry = SelectZipEntry;
reader.ProcessZipEntry = ProcessZipEntry; reader.ProcessZipEntry = ProcessZipEntry;
using (Stream src = new FileStream(path, FileMode.Open, FileAccess.Read)) using (Stream src = Platform.FileOpen(relPath)) {
reader.Extract(src); reader.Extract(src);
}
} }
bool SelectZipEntry(string filename) { bool SelectZipEntry(string filename) {
@ -98,20 +100,18 @@ namespace Launcher.Patcher {
bool CheckDigSoundsExist() { bool CheckDigSoundsExist() {
string[] files = ResourceList.DigSounds; string[] files = ResourceList.DigSounds;
string path = Path.Combine(Program.AppDirectory, "audio");
for (int i = 0; i < files.Length; i++) { for (int i = 0; i < files.Length; i++) {
string file = "dig_" + files[i] + ".wav"; string path = Path.Combine("audio", "dig_" + files[i] + ".wav");
if (!File.Exists(Path.Combine(path, file))) return false; if (!Platform.FileExists(path)) return false;
} }
return true; return true;
} }
bool CheckStepSoundsExist() { bool CheckStepSoundsExist() {
string[] files = ResourceList.StepSounds; string[] files = ResourceList.StepSounds;
string path = Path.Combine(Program.AppDirectory, "audio");
for (int i = 0; i < files.Length; i++) { for (int i = 0; i < files.Length; i++) {
string file = "step_" + files[i] + ".wav"; string path = Path.Combine("audio", "step_" + files[i] + ".wav");
if (!File.Exists(Path.Combine(path, file))) return false; if (!Platform.FileExists(path)) return false;
} }
return true; return true;
} }

View File

@ -128,9 +128,8 @@ namespace Launcher.Patcher {
return false; return false;
if (data == null) continue; if (data == null) continue;
string path = Path.Combine(Program.AppDirectory, "audio"); string path = Path.Combine("audio", file);
path = Path.Combine(path, file); Platform.WriteAllBytes(path, data);
File.WriteAllBytes(path, data);
} }
return true; return true;
} }

View File

@ -32,11 +32,16 @@ namespace Launcher.Patcher {
reader = new ZipReader(); reader = new ZipReader();
reader.SelectZipEntry = SelectZipEntry_Classic; reader.SelectZipEntry = SelectZipEntry_Classic;
reader.ProcessZipEntry = ProcessZipEntry_Classic; reader.ProcessZipEntry = ProcessZipEntry_Classic;
string texDir = Path.Combine(Program.AppDirectory, "texpacks"); string defPath = Path.Combine("texpacks", "default.zip");
string path = Path.Combine(texDir, "default.zip");
ExtractExisting(path);
using (Stream dst = new FileStream(path, FileMode.Create, FileAccess.Write)) { if (Platform.FileExists(defPath)) {
using (Stream src = Platform.FileOpen(defPath)) {
reader.ProcessZipEntry = ExtractExisting;
reader.Extract(src);
}
}
using (Stream dst = Platform.FileCreate(defPath)) {
writer = new ZipWriter(dst); writer = new ZipWriter(dst);
writer.entries = new ZipEntry[100]; writer.entries = new ZipEntry[100];
for (int i = 0; i < entries.Count; i++) for (int i = 0; i < entries.Count; i++)
@ -60,14 +65,6 @@ namespace Launcher.Patcher {
List<ZipEntry> entries = new List<ZipEntry>(); List<ZipEntry> entries = new List<ZipEntry>();
List<byte[]> datas = new List<byte[]>(); List<byte[]> datas = new List<byte[]>();
void ExtractExisting(string path) {
if (!File.Exists(path)) return;
using (Stream src = new FileStream(path, FileMode.Open, FileAccess.Read)) {
reader.ProcessZipEntry = ExtractExisting;
reader.Extract(src);
}
}
void ExtractExisting(string filename, byte[] data, ZipEntry entry) { void ExtractExisting(string filename, byte[] data, ZipEntry entry) {
filename = ResourceList.GetFile(filename); filename = ResourceList.GetFile(filename);

View File

@ -57,10 +57,9 @@ namespace Launcher.Patcher {
} }
void DecodeSound(string name, byte[] rawData) { void DecodeSound(string name, byte[] rawData) {
string path = Path.Combine(Program.AppDirectory, "audio"); string path = Path.Combine("audio", prefix + name + ".wav");
path = Path.Combine(path, prefix + name + ".wav");
using (FileStream dst = File.Create(path)) using (FileStream dst = Platform.FileCreate(path))
using (MemoryStream src = new MemoryStream(rawData)) using (MemoryStream src = new MemoryStream(rawData))
{ {
dst.SetLength(44); dst.SetLength(44);

View File

@ -10,22 +10,20 @@ namespace Launcher {
public const string AppName = "ClassicalSharp Launcher 0.99.9.94"; public const string AppName = "ClassicalSharp Launcher 0.99.9.94";
public static string AppDirectory;
public static bool ShowingErrorDialog = false; public static bool ShowingErrorDialog = false;
[STAThread] [STAThread]
static void Main(string[] args) { static void Main(string[] args) {
AppDirectory = AppDomain.CurrentDomain.BaseDirectory; Platform.AppDirectory = AppDomain.CurrentDomain.BaseDirectory;
string path = Path.Combine(AppDirectory, "ClassicalSharp.exe"); if (!Platform.FileExists("ClassicalSharp.exe")) {
if (!File.Exists(path)) { ErrorHandler.ShowDialog("Missing file", "ClassicalSharp.exe needs to be in the same folder as the launcher.");
Message("ClassicalSharp.exe needs to be in the same folder as the launcher."); return; return;
} }
path = Path.Combine(AppDirectory, "OpenTK.dll"); if (!Platform.FileExists("OpenTK.dll")) {
if (!File.Exists(path)) { ErrorHandler.ShowDialog("Missing file", "OpenTK.dll needs to be in the same folder as the launcher.");
Message("OpenTK.dll needs to be in the same folder as the launcher."); return; return;
} }
// NOTE: we purposely put this in another method, as we need to ensure // NOTE: we purposely put this in another method, as we need to ensure
@ -34,13 +32,9 @@ namespace Launcher {
RunLauncher(); RunLauncher();
} }
// put in separate function, because we don't want to load winforms assembly if possible
static void Message(string message) { MessageBox.Show(message, "Missing file"); }
static void RunLauncher() { static void RunLauncher() {
string logPath = Path.Combine(AppDirectory, "launcher.log");
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
ErrorHandler.InstallHandler(logPath); ErrorHandler.InstallHandler("launcher.log");
OpenTK.Configuration.SkipPerfCountersHack(); OpenTK.Configuration.SkipPerfCountersHack();
LauncherWindow window = new LauncherWindow(); LauncherWindow window = new LauncherWindow();
window.Run(); window.Run();

View File

@ -27,16 +27,16 @@ namespace Launcher.Updater {
ProcessStartInfo info = new ProcessStartInfo(); ProcessStartInfo info = new ProcessStartInfo();
info.CreateNoWindow = false; info.CreateNoWindow = false;
info.UseShellExecute = true; info.UseShellExecute = true;
info.WorkingDirectory = Program.AppDirectory; info.WorkingDirectory = Platform.AppDirectory;
if (OpenTK.Configuration.RunningOnWindows) { if (OpenTK.Configuration.RunningOnWindows) {
string path = Path.Combine(Program.AppDirectory, "update.bat"); Platform.WriteAllText("update.bat", Scripts.BatchFile);
File.WriteAllText(path, Scripts.BatchFile);
info.FileName = "cmd"; info.Arguments = "/C start cmd /C update.bat"; info.FileName = "cmd"; info.Arguments = "/C start cmd /C update.bat";
Process.Start(info); Process.Start(info);
} else { } else {
string path = Path.Combine(Program.AppDirectory, "update.sh"); string path = Path.Combine(Platform.AppDirectory, "update.sh");
File.WriteAllText(path, Scripts.BashFile.Replace("\r\n", "\n")); Platform.WriteAllText("update.sh", Scripts.BashFile.Replace("\r\n", "\n"));
const int flags = 0x7;// read | write | executable const int flags = 0x7;// read | write | executable
int code = chmod(path, (flags << 6) | (flags << 3) | 4); int code = chmod(path, (flags << 6) | (flags << 3) | 4);
if (code != 0) if (code != 0)
@ -55,23 +55,20 @@ namespace Launcher.Updater {
internal static extern int chmod(string path, int mode); internal static extern int chmod(string path, int mode);
static void MakeUpdatesFolder(byte[] zipData) { static void MakeUpdatesFolder(byte[] zipData) {
Platform.DirectoryCreate("CS_Update");
using (MemoryStream stream = new MemoryStream(zipData)) { using (MemoryStream stream = new MemoryStream(zipData)) {
ZipReader reader = new ZipReader(); ZipReader reader = new ZipReader();
string path = Path.Combine(Program.AppDirectory, "CS_Update");
Directory.CreateDirectory(path);
reader.ProcessZipEntry = ProcessZipEntry; reader.ProcessZipEntry = ProcessZipEntry;
reader.Extract(stream); reader.Extract(stream);
} }
} }
static void ProcessZipEntry(string filename, byte[] data, ZipEntry entry) { static void ProcessZipEntry(string filename, byte[] data, ZipEntry entry) {
string path = Path.Combine(Program.AppDirectory, "CS_Update"); string path = Path.Combine("CS_Update", Path.GetFileName(filename));
path = Path.Combine(path, Path.GetFileName(filename)); Platform.WriteAllBytes(path, data);
File.WriteAllBytes(path, data);
try { try {
File.SetLastWriteTimeUtc(path, PatchTime); Platform.FileSetWriteTime(path, PatchTime);
} catch (IOException ex) { } catch (IOException ex) {
ErrorHandler.LogError("I/O exception when trying to set modified time for: " + filename, ex); ErrorHandler.LogError("I/O exception when trying to set modified time for: " + filename, ex);
} catch (UnauthorizedAccessException ex) { } catch (UnauthorizedAccessException ex) {

View File

@ -28,15 +28,12 @@ namespace Launcher {
return StartImpl(null, true, args, ref shouldExit); return StartImpl(null, true, args, ref shouldExit);
} }
static bool StartImpl(ClientStartData data, bool classicubeSkins, static bool StartImpl(ClientStartData data, bool ccSkins, string args, ref bool shouldExit) {
string args, ref bool shouldExit) { if (!Platform.FileExists("ClassicalSharp.exe")) return false;
string path = Path.Combine(Program.AppDirectory, "ClassicalSharp.exe");
if (!File.Exists(path))
return false;
CheckSettings(data, classicubeSkins, out shouldExit); CheckSettings(data, ccSkins, out shouldExit);
try { try {
StartProcess(path, args); StartProcess(args);
} catch (Win32Exception ex) { } catch (Win32Exception ex) {
if ((uint)ex.ErrorCode != 0x80004005) if ((uint)ex.ErrorCode != 0x80004005)
throw; // HRESULT when user clicks 'cancel' to 'are you sure you want to run ClassicalSharp.exe' throw; // HRESULT when user clicks 'cancel' to 'are you sure you want to run ClassicalSharp.exe'
@ -46,7 +43,8 @@ namespace Launcher {
return true; return true;
} }
static void StartProcess(string path, string args) { static void StartProcess(string args) {
string path = Path.Combine(Platform.AppDirectory, "ClassicalSharp.exe");
if (Configuration.RunningOnMono) { if (Configuration.RunningOnMono) {
// We also need to handle the case of running Mono through wine // We also need to handle the case of running Mono through wine
if (Configuration.RunningOnWindows) { if (Configuration.RunningOnWindows) {

View File

@ -80,7 +80,7 @@ void Chat_OpenLog(DateTime* now) {
String_AppendConst(&path, ".log"); String_AppendConst(&path, ".log");
void* file; void* file;
ReturnCode code = Platform_FileOpen(&file, &path, false); ReturnCode code = Platform_FileAppend(&file, &path);
if (code != 0 && code != ReturnCode_FileShareViolation) { if (code != 0 && code != ReturnCode_FileShareViolation) {
ErrorHandler_FailWithCode(code, "Chat - opening log file"); ErrorHandler_FailWithCode(code, "Chat - opening log file");
} }

View File

@ -13,4 +13,5 @@ void ErrorHandler_Log(STRING_PURE String* msg);
void ErrorHandler_Fail(const UInt8* raw_msg); void ErrorHandler_Fail(const UInt8* raw_msg);
void ErrorHandler_FailWithCode(ReturnCode returnCode, const UInt8* raw_msg); void ErrorHandler_FailWithCode(ReturnCode returnCode, const UInt8* raw_msg);
#define ErrorHandler_CheckOrFail(returnCode, raw_msg) if (returnCode != 0) { ErrorHandler_FailWithCode(returnCode, raw_msg); } #define ErrorHandler_CheckOrFail(returnCode, raw_msg) if (returnCode != 0) { ErrorHandler_FailWithCode(returnCode, raw_msg); }
void ErrorHandler_ShowDialog(const UInt8* title, const UInt8* msg);
#endif #endif

View File

@ -1275,7 +1275,7 @@ void LoadLevelScreen_EntryClick(GuiElement* screenElem, GuiElement* w) {
if (!Platform_FileExists(&path)) return; if (!Platform_FileExists(&path)) return;
void* file; void* file;
ReturnCode code = Platform_FileOpen(&file, &path, true); ReturnCode code = Platform_FileOpen(&file, &path);
ErrorHandler_CheckOrFail(code, "Failed to open map file"); ErrorHandler_CheckOrFail(code, "Failed to open map file");
Stream stream; Stream_FromFile(&stream, file, &path); Stream stream; Stream_FromFile(&stream, file, &path);
@ -1504,7 +1504,7 @@ void ClassicHacksKeyBindingsScreen_ContextRecreated(void* obj) {
KeyBindingsScreen_MakeWidgets(screen, -90, -40, 3, "Hacks controls", 260); KeyBindingsScreen_MakeWidgets(screen, -90, -40, 3, "Hacks controls", 260);
} }
Screen* ClassicHackKeyBindingsScreen_MakeInstance(void) { Screen* ClassicHacksKeyBindingsScreen_MakeInstance(void) {
static KeyBind binds[6] = { KeyBind_Speed, KeyBind_NoClip, KeyBind_HalfSpeed, KeyBind_Fly, KeyBind_FlyUp, KeyBind_FlyDown }; static KeyBind binds[6] = { KeyBind_Speed, KeyBind_NoClip, KeyBind_HalfSpeed, KeyBind_Fly, KeyBind_FlyUp, KeyBind_FlyDown };
static const UInt8* descs[6] = { "Speed", "Noclip", "Half speed", "Fly", "Fly up", "Fly down" }; static const UInt8* descs[6] = { "Speed", "Noclip", "Half speed", "Fly", "Fly up", "Fly down" };
static ButtonWidget buttons[6]; static ButtonWidget buttons[6];

View File

@ -141,7 +141,7 @@ void Options_Set(const UInt8* keyRaw, STRING_PURE String* value) {
void Options_Load(void) { void Options_Load(void) {
void* file = NULL; void* file = NULL;
String path = String_FromConst("options.txt"); String path = String_FromConst("options.txt");
ReturnCode result = Platform_FileOpen(&file, &path, true); ReturnCode result = Platform_FileOpen(&file, &path);
if (result == ReturnCode_FileNotFound) return; if (result == ReturnCode_FileNotFound) return;
/* TODO: Should we just log failure to open? */ /* TODO: Should we just log failure to open? */
@ -182,7 +182,7 @@ void Options_Load(void) {
void Options_Save(void) { void Options_Save(void) {
void* file = NULL; void* file = NULL;
String path = String_FromConst("options.txt"); String path = String_FromConst("options.txt");
ReturnCode result = Platform_FileOpen(&file, &path, true); ReturnCode result = Platform_FileOpen(&file, &path);
/* TODO: Should we just log failure to save? */ /* TODO: Should we just log failure to save? */
ErrorHandler_CheckOrFail(result, "Options - Saving"); ErrorHandler_CheckOrFail(result, "Options - Saving");

View File

@ -31,7 +31,8 @@ typedef void Platform_EnumFilesCallback(STRING_PURE String* path, void* obj);
ReturnCode Platform_EnumFiles(STRING_PURE String* path, void* obj, Platform_EnumFilesCallback callback); ReturnCode Platform_EnumFiles(STRING_PURE String* path, void* obj, Platform_EnumFilesCallback callback);
ReturnCode Platform_FileCreate(void** file, STRING_PURE String* path); ReturnCode Platform_FileCreate(void** file, STRING_PURE String* path);
ReturnCode Platform_FileOpen(void** file, STRING_PURE String* path, bool readOnly); ReturnCode Platform_FileOpen(void** file, STRING_PURE String* path);
ReturnCode Platform_FileAppend(void** file, STRING_PURE String* path);
ReturnCode Platform_FileRead(void* file, UInt8* buffer, UInt32 count, UInt32* bytesRead); ReturnCode Platform_FileRead(void* file, UInt8* buffer, UInt32 count, UInt32* bytesRead);
ReturnCode Platform_FileWrite(void* file, UInt8* buffer, UInt32 count, UInt32* bytesWritten); ReturnCode Platform_FileWrite(void* file, UInt8* buffer, UInt32 count, UInt32* bytesWritten);
ReturnCode Platform_FileClose(void* file); ReturnCode Platform_FileClose(void* file);

View File

@ -24,7 +24,7 @@ int main(int argc, char* argv[]) {
/*void* file; /*void* file;
String path = String_FromConstant("H:\\PortableApps\\GitPortable\\App\\Git\\ClassicalSharp\\output\\release\\texpacks\\skybox.png"); String path = String_FromConstant("H:\\PortableApps\\GitPortable\\App\\Git\\ClassicalSharp\\output\\release\\texpacks\\skybox.png");
ReturnCode openCode = Platform_FileOpen(&file, &path, true); ReturnCode openCode = Platform_FileOpen(&file, &path);
Stream fileStream; Stream fileStream;
Stream_FromFile(&fileStream, file, &path); Stream_FromFile(&fileStream, file, &path);
Bitmap bmp; Bitmap bmp;
@ -61,7 +61,7 @@ int main(int argc, char* argv[]) {
/*void* file; /*void* file;
String path = String_FromConstant("H:\\PortableApps\\GitPortable\\App\\Git\\ClassicalSharp\\output\\release\\texpacks\\default.zip"); String path = String_FromConstant("H:\\PortableApps\\GitPortable\\App\\Git\\ClassicalSharp\\output\\release\\texpacks\\default.zip");
ReturnCode openCode = Platform_FileOpen(&file, &path, true); ReturnCode openCode = Platform_FileOpen(&file, &path);
Stream fileStream; Stream fileStream;
Stream_FromFile(&fileStream, file, &path); Stream_FromFile(&fileStream, file, &path);
ZipState state; ZipState state;
@ -71,7 +71,7 @@ int main(int argc, char* argv[]) {
void* file; void* file;
String path = String_FromConst("H:\\PortableApps\\GitPortable\\App\\Git\\ClassicalSharp\\src\\x64\\Release\\canyon.lvl"); String path = String_FromConst("H:\\PortableApps\\GitPortable\\App\\Git\\ClassicalSharp\\src\\x64\\Release\\canyon.lvl");
ReturnCode openCode = Platform_FileOpen(&file, &path, true); ReturnCode openCode = Platform_FileOpen(&file, &path);
Stream fileStream; Stream fileStream;
Stream_FromFile(&fileStream, file, &path); Stream_FromFile(&fileStream, file, &path);
Lvl_Load(&fileStream); Lvl_Load(&fileStream);
@ -79,7 +79,7 @@ int main(int argc, char* argv[]) {
/*void* file; /*void* file;
String path = String_FromConstant("H:\\PortableApps\\GitPortable\\App\\Git\\\ClassicalSharp\\src\\Debug\\gunzip.c.gz"); String path = String_FromConstant("H:\\PortableApps\\GitPortable\\App\\Git\\\ClassicalSharp\\src\\Debug\\gunzip.c.gz");
ReturnCode openCode = Platform_FileOpen(&file, &path, true); ReturnCode openCode = Platform_FileOpen(&file, &path);
Stream fileStream; Stream fileStream;
Stream_FromFile(&fileStream, file, &path); Stream_FromFile(&fileStream, file, &path);

View File

@ -32,8 +32,7 @@ void ErrorHandler_Fail(const UInt8* raw_msg) {
ErrorHandler_WriteLogBody(raw_msg); ErrorHandler_WriteLogBody(raw_msg);
ErrorHandler_WriteLogEnd(); ErrorHandler_WriteLogEnd();
HWND win = GetActiveWindow(); ErrorHandler_ShowDialog("We're sorry", logMsg.buffer);
MessageBoxA(win, logMsg.buffer, "We're sorry", 0);
ExitProcess(1); ExitProcess(1);
} }
@ -45,7 +44,11 @@ void ErrorHandler_FailWithCode(ReturnCode code, const UInt8* raw_msg) {
String_AppendConst(&logMsg, "\r\n"); String_AppendConst(&logMsg, "\r\n");
ErrorHandler_WriteLogEnd(); ErrorHandler_WriteLogEnd();
HWND win = GetActiveWindow(); /* TODO: It's probably wrong to use GetActiveWindow() here */ ErrorHandler_ShowDialog("We're sorry", logMsg.buffer);
MessageBoxA(win, logMsg.buffer, "We're sorry", 0);
ExitProcess(code); ExitProcess(code);
} }
void ErrorHandler_ShowDialog(const UInt8* title, const UInt8* msg) {
HWND win = GetActiveWindow(); /* TODO: It's probably wrong to use GetActiveWindow() here */
MessageBoxA(win, msg, title, 0);
}

View File

@ -151,23 +151,28 @@ ReturnCode Platform_EnumFiles(STRING_PURE String* path, void* obj, Platform_Enum
} }
ReturnCode Platform_FileOpen(void** file, STRING_PURE String* path, bool readOnly) { ReturnCode Platform_FileOpen(void** file, STRING_PURE String* path) {
UINT32 access = GENERIC_READ; HANDLE handle = CreateFileA(path->buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (!readOnly) access |= GENERIC_WRITE;
HANDLE handle = CreateFileA(path->buffer, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
*file = (void*)handle; *file = (void*)handle;
return handle != INVALID_HANDLE_VALUE ? 0 : GetLastError(); return handle != INVALID_HANDLE_VALUE ? 0 : GetLastError();
} }
ReturnCode Platform_FileCreate(void** file, STRING_PURE String* path) { ReturnCode Platform_FileCreate(void** file, STRING_PURE String* path) {
UINT32 access = GENERIC_READ | GENERIC_WRITE; HANDLE handle = CreateFileA(path->buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE handle = CreateFileA(path->buffer, access, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
*file = (void*)handle; *file = (void*)handle;
return handle != INVALID_HANDLE_VALUE ? 0 : GetLastError(); return handle != INVALID_HANDLE_VALUE ? 0 : GetLastError();
} }
ReturnCode Platform_FileAppend(void** file, STRING_PURE String* path) {
HANDLE handle = CreateFileA(path->buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
*file = (void*)handle;
if (handle == INVALID_HANDLE_VALUE) return GetLastError();
return Platform_FileSeek(*file, 0, STREAM_SEEKFROM_END);
}
ReturnCode Platform_FileRead(void* file, UInt8* buffer, UInt32 count, UInt32* bytesRead) { ReturnCode Platform_FileRead(void* file, UInt8* buffer, UInt32 count, UInt32* bytesRead) {
BOOL success = ReadFile((HANDLE)file, buffer, count, bytesRead, NULL); BOOL success = ReadFile((HANDLE)file, buffer, count, bytesRead, NULL);
return success ? 0 : GetLastError(); return success ? 0 : GetLastError();
@ -187,11 +192,11 @@ ReturnCode Platform_FileSeek(void* file, Int32 offset, Int32 seekType) {
DWORD pos; DWORD pos;
switch (seekType) { switch (seekType) {
case STREAM_SEEKFROM_BEGIN: case STREAM_SEEKFROM_BEGIN:
pos = SetFilePointer(file, offset, NULL, 0); break; pos = SetFilePointer(file, offset, NULL, FILE_BEGIN); break;
case STREAM_SEEKFROM_CURRENT: case STREAM_SEEKFROM_CURRENT:
pos = SetFilePointer(file, offset, NULL, 1); break; pos = SetFilePointer(file, offset, NULL, FILE_CURRENT); break;
case STREAM_SEEKFROM_END: case STREAM_SEEKFROM_END:
pos = SetFilePointer(file, offset, NULL, 2); break; pos = SetFilePointer(file, offset, NULL, FILE_END); break;
default: default:
ErrorHandler_Fail("Invalid SeekType provided when seeking file"); ErrorHandler_Fail("Invalid SeekType provided when seeking file");
} }