From e8332067d19485de7df4785434aa5ff6ac15be02 Mon Sep 17 00:00:00 2001
From: Chev <11602755+chev2@users.noreply.github.com>
Date: Sun, 10 Jan 2021 20:06:26 -0800
Subject: [PATCH] UI new tabs
Updated TSCB to have a pixel view. Added SARC, Yaz0 & RSTB tabs.
---
 botw-toolset/Control/TSCBAreaExpander.xaml    |  14 +
 botw-toolset/Control/TSCBAreaExpander.xaml.cs |  15 +
 botw-toolset/Control/TabRSTB.xaml             |  12 +
 botw-toolset/Control/TabRSTB.xaml.cs          |  15 +
 botw-toolset/Control/TabSARC.xaml             |  67 +++
 botw-toolset/Control/TabSARC.xaml.cs          | 168 +++++++
 botw-toolset/Control/TabTSCB.xaml             |  40 +-
 botw-toolset/Control/TabTSCB.xaml.cs          | 411 +++++++++++++++++-
 botw-toolset/Control/TabYaz0.xaml             |  27 ++
 botw-toolset/Control/TabYaz0.xaml.cs          | 167 +++++++
 botw-toolset/Dashboard.xaml                   |  30 +-
 botw-toolset/Dashboard.xaml.cs                |  10 +
 12 files changed, 942 insertions(+), 34 deletions(-)
 create mode 100644 botw-toolset/Control/TSCBAreaExpander.xaml
 create mode 100644 botw-toolset/Control/TSCBAreaExpander.xaml.cs
 create mode 100644 botw-toolset/Control/TabRSTB.xaml
 create mode 100644 botw-toolset/Control/TabRSTB.xaml.cs
 create mode 100644 botw-toolset/Control/TabSARC.xaml
 create mode 100644 botw-toolset/Control/TabSARC.xaml.cs
 create mode 100644 botw-toolset/Control/TabYaz0.xaml
 create mode 100644 botw-toolset/Control/TabYaz0.xaml.cs
diff --git a/botw-toolset/Control/TSCBAreaExpander.xaml b/botw-toolset/Control/TSCBAreaExpander.xaml
new file mode 100644
index 0000000..8fa3fd2
--- /dev/null
+++ b/botw-toolset/Control/TSCBAreaExpander.xaml
@@ -0,0 +1,14 @@
+
+    
+        
+            
+        
+    
+
diff --git a/botw-toolset/Control/TSCBAreaExpander.xaml.cs b/botw-toolset/Control/TSCBAreaExpander.xaml.cs
new file mode 100644
index 0000000..52ff792
--- /dev/null
+++ b/botw-toolset/Control/TSCBAreaExpander.xaml.cs
@@ -0,0 +1,15 @@
+using System.Windows.Controls;
+
+namespace BOTWToolset.Control
+{
+    /// 
+    /// Interaction logic for TSCBAreaExpander.xaml
+    /// 
+    public partial class TSCBAreaExpander : UserControl
+    {
+        public TSCBAreaExpander()
+        {
+            InitializeComponent();
+        }
+    }
+}
diff --git a/botw-toolset/Control/TabRSTB.xaml b/botw-toolset/Control/TabRSTB.xaml
new file mode 100644
index 0000000..42c3dd3
--- /dev/null
+++ b/botw-toolset/Control/TabRSTB.xaml
@@ -0,0 +1,12 @@
+
+    
+        
+    
+
diff --git a/botw-toolset/Control/TabRSTB.xaml.cs b/botw-toolset/Control/TabRSTB.xaml.cs
new file mode 100644
index 0000000..093fdec
--- /dev/null
+++ b/botw-toolset/Control/TabRSTB.xaml.cs
@@ -0,0 +1,15 @@
+using System.Windows.Controls;
+
+namespace BOTWToolset.Control
+{
+    /// 
+    /// Interaction logic for TabRSTB.xaml
+    /// 
+    public partial class TabRSTB : UserControl
+    {
+        public TabRSTB()
+        {
+            InitializeComponent();
+        }
+    }
+}
diff --git a/botw-toolset/Control/TabSARC.xaml b/botw-toolset/Control/TabSARC.xaml
new file mode 100644
index 0000000..f77c4f4
--- /dev/null
+++ b/botw-toolset/Control/TabSARC.xaml
@@ -0,0 +1,67 @@
+
+    
+        
+
+        
+        
+            
+                
+                    
+                    
+                        
+                            
+                        
+                    
+                
+                
+                    
+
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+
+                    
+
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+                    
+
+                    
+
+                    
+                    
+                    
+                
+            
+        
+    
+
diff --git a/botw-toolset/Control/TabSARC.xaml.cs b/botw-toolset/Control/TabSARC.xaml.cs
new file mode 100644
index 0000000..203b756
--- /dev/null
+++ b/botw-toolset/Control/TabSARC.xaml.cs
@@ -0,0 +1,168 @@
+using BOTWToolset.Debugging;
+using BOTWToolset.IO.SARC;
+using Microsoft.Win32;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace BOTWToolset.Control
+{
+    /// 
+    /// Interaction logic for TabSARC.xaml
+    /// 
+    public partial class TabSARC : UserControl
+    {
+        public static string fileLocation;
+        public static SARC currentSARC;
+
+        public TabSARC()
+        {
+            InitializeComponent();
+        }
+
+        public void SetEnabled(SARC s)
+        {
+            // SARC header
+            SARCMagic.Text = s.Magic;
+            SARCHeaderLength.Text = s.HeaderLength.ToString();
+            SARCIsBigEndian.Text = s.IsBigEndian ? "Yes" : "No";
+            SARCFileSize.Text = s.FileSize.ToString();
+            SARCDataOffset.Text = s.DataOffset.ToString();
+            SARCVersion.Text = s.Version.ToString();
+
+            // SFAT header
+            SFATMagic.Text = s.SFAT.Magic;
+            SFATHeaderLength.Text = s.SFAT.HeaderLength.ToString();
+            SFATNodeCount.Text = s.SFAT.NodeCount.ToString();
+            SFATHashKey.Text = s.SFAT.HashKey.ToString();
+
+            // SFNT header
+            SFNTMagic.Text = s.SFNT.Magic;
+            SFNTHeaderLength.Text = s.SFNT.HeaderLength.ToString();
+
+            // Show file count
+            FileCount.Content = $"SARC file count: {s.SFAT.NodeCount}";
+
+            for (int i = 0; i < s.SFNT.FileNames.Length; i++)
+            {
+                string file_name = s.SFNT.FileNames[i];
+                int file_size = s.Files[i].Length;
+
+                Grid grid = new Grid
+                {
+                    Height = 112
+                };
+
+                Label title = new Label
+                {
+                    Height = 44,
+                    Content = file_name,
+                    FontSize = 20,
+                    Margin = new Thickness(10, 10, 10, 0),
+                    VerticalAlignment = VerticalAlignment.Top,
+                    VerticalContentAlignment = VerticalAlignment.Center
+                };
+
+                Label desc = new Label
+                {
+                    Content = $"{file_size:n0} bytes",
+                    Margin = new Thickness(10, 59, 10, 10),
+                    FontSize = 16,
+                    VerticalContentAlignment = VerticalAlignment.Center
+                };
+
+                grid.Children.Add(title);
+                grid.Children.Add(desc);
+
+                FileDisplay.Children.Add(grid);
+            }
+        }
+
+        public void SetDisabled()
+        {
+            // SARC header
+            SARCMagic.Clear();
+            SARCHeaderLength.Clear();
+            SARCIsBigEndian.Clear();
+            SARCFileSize.Clear();
+            SARCDataOffset.Clear();
+            SARCVersion.Clear();
+
+            // SFAT header
+            SFATMagic.Clear();
+            SFATHeaderLength.Clear();
+            SFATNodeCount.Clear();
+            SFATHashKey.Clear();
+
+            // SFNT header
+            SFNTMagic.Clear();
+            SFNTHeaderLength.Clear();
+
+            // SARC Files
+            FileCount.Content = "";
+            FileDisplay.Children.Clear();
+        }
+
+        private void Menu_FileOpen(object sender, RoutedEventArgs e)
+        {
+            BOTWConsole.Log("Clicked File -> Open button");
+
+            var openFileDialog = new OpenFileDialog
+            {
+                InitialDirectory = @"C:\",
+                RestoreDirectory = true,
+                Title = "Select SARC file",
+                DefaultExt = "sarc",
+                Filter = "SARC files (*.arc;*.sarc;*.blarc;*.bgenv;*.genvb;*.pack;*.bars;*.stera)|*.arc;*.sarc;*.blarc;*.bgenv;*.genvb;*.pack;*.bars;*.stera|All files (*.*)|*.*",
+                CheckFileExists = true
+            };
+
+            if ((bool)openFileDialog.ShowDialog())
+            {
+                BOTWConsole.Log("Opening file");
+
+                SARC s = SARC.ReadFile(openFileDialog.FileName);
+
+                // Set the current file location to the chosen file's location
+                fileLocation = openFileDialog.FileName;
+
+                currentSARC = s;
+
+                // Display info in the tab
+                SetEnabled(s);
+
+                // Enable file edits
+                MenuFileClose.IsEnabled = true;
+                MenuFileSave.IsEnabled = true;
+                MenuFileSaveAs.IsEnabled = true;
+            }
+        }
+
+        private void Menu_FileClose(object sender, RoutedEventArgs e)
+        {
+            BOTWConsole.Log("Clicked File -> Close button");
+
+            fileLocation = null;
+
+            // Set the current TSCB info to nothing
+            currentSARC = null;
+
+            // Set the tab as disabled
+            SetDisabled();
+
+            // Disable file edits
+            MenuFileClose.IsEnabled = false;
+            MenuFileSave.IsEnabled = false;
+            MenuFileSaveAs.IsEnabled = false;
+        }
+
+        private void Menu_FileSave(object sender, RoutedEventArgs e)
+        {
+
+        }
+
+        private void Menu_FileSaveAs(object sender, RoutedEventArgs e)
+        {
+
+        }
+    }
+}
diff --git a/botw-toolset/Control/TabTSCB.xaml b/botw-toolset/Control/TabTSCB.xaml
index e19b97a..8f84b81 100644
--- a/botw-toolset/Control/TabTSCB.xaml
+++ b/botw-toolset/Control/TabTSCB.xaml
@@ -1,9 +1,9 @@
-
     
@@ -11,12 +11,16 @@
 
         
-        
+        
+            
+                
+            
+        
         
             
                 
@@ -36,10 +40,32 @@
                 
                 
                 
-                    
+                    
+                        
+                    
                 
             
         
         
+        
+            
+                
+                
+                
+                
+
+                
+                
+                
+                
+                
+                
+                
+                
+                
+                
+                
+            
+        
     
 
diff --git a/botw-toolset/Control/TabTSCB.xaml.cs b/botw-toolset/Control/TabTSCB.xaml.cs
index dbf6351..f25457d 100644
--- a/botw-toolset/Control/TabTSCB.xaml.cs
+++ b/botw-toolset/Control/TabTSCB.xaml.cs
@@ -1,9 +1,19 @@
 using BOTWToolset.Debugging;
+using BOTWToolset.IO;
+using BOTWToolset.IO.EXTM;
+using BOTWToolset.IO.SARC;
 using BOTWToolset.IO.TSCB;
+using BOTWToolset.IO.Yaz0;
 using Microsoft.Win32;
+using System;
+using System.IO;
+using System.Windows;
 using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
 
-namespace BOTWToolset
+namespace BOTWToolset.Control
 {
     /// 
     /// Control tab for TSCB management
@@ -11,26 +21,314 @@ namespace BOTWToolset
     public partial class TabTSCB : UserControl
     {
         public static string fileLocation;
-        public static TSCBInfo currentTSCBInfo;
+        public static TSCB currentTSCB;
+
+        public static WriteableBitmap writeableBitmap; //used for pixel-like display
 
         public TabTSCB()
         {
             InitializeComponent();
+
+            RenderOptions.SetBitmapScalingMode(PixelView, BitmapScalingMode.NearestNeighbor); // Nearest-neighbor sampling
+            //RenderOptions.SetEdgeMode(i, EdgeMode.Unspecified);
+
+            //Placeholder image
+            writeableBitmap = new WriteableBitmap(4, 4, 16, 16, PixelFormats.Rgb24, null);
+
+            PixelView.Source = writeableBitmap;
+            PixelView.Stretch = Stretch.Uniform;
+
+            PixelView.MouseLeftButtonDown += new MouseButtonEventHandler(PixelView_MouseLeft);
+            //PixelViewBorder.MouseWheel += new MouseWheelEventHandler(PixelViewBorder_MouseWheel);
         }
 
-        public void SetSidebarHeaderInfo(TSCBInfo t)
+        private void ClearBitmap()
+        {
+            PixelView.Source = null;
+            writeableBitmap = null;
+            GC.Collect();
+        }
+
+        private void DrawPixel(MouseEventArgs e)
+        {
+
+        }
+
+        private void ErasePixel(MouseEventArgs e)
+        {
+
+        }
+
+        private void PixelView_MouseLeft(object sender, MouseButtonEventArgs e)
+        {
+            DrawPixel(e);
+        }
+
+        public void PixelViewBorder_MouseWheel(object sender, MouseWheelEventArgs e)
+        {
+            Matrix m = PixelView.RenderTransform.Value;
+
+            if (e.Delta > 0)
+            {
+                m.ScaleAt(1.5f, 1.5f, e.GetPosition(PixelViewBorder).X, e.GetPosition(PixelViewBorder).Y);
+            }
+            else
+            {
+                m.ScaleAt(2 / 3, 2 / 3, e.GetPosition(PixelViewBorder).X, e.GetPosition(PixelViewBorder).Y);
+            }
+
+            PixelView.RenderTransform = new MatrixTransform(m);
+        }
+
+        private void PixelView_UpdateView(object sender, RoutedEventArgs e)
+        {
+            byte zoom = (byte)SliderZoomLevel.Value;
+
+            // Get the parent directory of the files directory
+            DirectoryInfo parent = Directory.GetParent(fileLocation);
+            string main_field_dir = Path.Combine(parent.FullName, "MainField");
+
+            Button sender_button = (Button)sender;
+
+            if (Directory.Exists(main_field_dir))
+            {
+                switch (sender_button.Name)
+                {
+                    case "PixelViewMATE":
+                        {
+                            string extension = ".mate.sstera";
+                            int img_size = (int)Math.Pow(2, 8 + zoom);
+
+                            ClearBitmap();
+                            writeableBitmap = new WriteableBitmap(img_size, img_size, 16, 16, PixelFormats.Rgb24, null);
+                            PixelView.Source = writeableBitmap;
+
+                            PixelView_SetMATE(main_field_dir, extension, zoom, 256);
+                        }
+                        break;
+                    case "PixelViewHGHT":
+                        {
+                            string extension = ".hght.sstera";
+                            int img_size = (int)Math.Pow(2, 8 + zoom);
+
+                            ClearBitmap();
+                            writeableBitmap = new WriteableBitmap(img_size, img_size, 16, 16, PixelFormats.Gray8, null);
+                            PixelView.Source = writeableBitmap;
+
+                            PixelView_SetHGHT(main_field_dir, extension, zoom, 256);
+                        }
+                        break;
+                    case "PixelViewGrassEXTM":
+                        {
+                            string extension = ".grass.extm.sstera";
+                            int img_size = (int)Math.Pow(2, 6 + zoom);
+
+                            ClearBitmap();
+                            writeableBitmap = new WriteableBitmap(img_size, img_size, 16, 16, PixelFormats.Rgb24, null);
+                            PixelView.Source = writeableBitmap;
+
+                            PixelView_SetGrassEXTM(main_field_dir, extension, zoom, 64);
+                        }
+                        break;
+                    case "PixelViewWaterEXTM":
+                        {
+                            string extension = ".water.extm.sstera";
+                            int img_size = (int)Math.Pow(2, 6 + zoom);
+
+                            ClearBitmap();
+                            writeableBitmap = new WriteableBitmap(img_size, img_size, 16, 16, PixelFormats.Rgb24, null);
+                            PixelView.Source = writeableBitmap;
+
+                            PixelView_SetWaterEXTM(main_field_dir, extension, zoom, 64);
+                        }
+                        break;
+                }
+            }
+        }
+
+        private void PixelView_SetMATE(string folder, string extension, byte zoom_level, int grid_size)
+        {
+            BOTWConsole.Log("Setting view to texture");
+            string[] ext_files = Directory.GetFiles(folder, $"5{zoom_level}*{extension}");
+
+            foreach (string ext_file in ext_files)
+            {
+                byte[] yaz0_bytes = File.ReadAllBytes(Path.Combine(folder, ext_file));
+                byte[] yaz0_decomp = Yaz0.Decompress(yaz0_bytes);
+                SARC s = SARC.FromBytes(new MemoryStream(yaz0_decomp));
+
+                for (int i = 0; i < s.Files.Length; i++)
+                {
+                    // Get grid index from filename
+                    string grid_index_str = s.SFNT.FileNames[i].Replace($"5{zoom_level}", "").Replace(extension.Replace(".sstera", ""), "").Replace(folder + "\\", "");
+                    int grid_index = int.Parse(grid_index_str, System.Globalization.NumberStyles.HexNumber);
+                    int[] xyoffs = GridConverter.ZCurveToXY(grid_index);
+                    MATE[] mats = MATE.FromBytes(s.Files[i]);
+
+                    for (int j = 0; j < mats.Length; j++)
+                    {
+                        MATE m = mats[j];
+
+                        int x = j % grid_size;
+                        int y = j / grid_size;
+
+                        // Draw pixel at the correct X, Y coordinate
+                        Int32Rect rect = new Int32Rect(x + (xyoffs[0] * grid_size), y + (xyoffs[1] * grid_size), 1, 1);
+                        // Get this water's color based off its material index/water type
+                        System.Drawing.Color c = GridColors.MaterialColors[m.Material0];
+                        // Use water type color + brightness adjust
+                        byte[] color = new byte[] { c.R, c.G, c.B };
+
+                        writeableBitmap.WritePixels(rect, color, writeableBitmap.BackBufferStride, 0);
+                    }
+                }
+            }
+        }
+
+        private void PixelView_SetHGHT(string folder, string extension, byte zoom_level, int grid_size)
+        {
+            BOTWConsole.Log("Setting view to heightmap");
+            string[] ext_files = Directory.GetFiles(folder, $"5{zoom_level}*{extension}");
+
+            foreach (string ext_file in ext_files)
+            {
+                byte[] yaz0_bytes = File.ReadAllBytes(Path.Combine(folder, ext_file));
+                byte[] yaz0_decomp = Yaz0.Decompress(yaz0_bytes);
+                SARC s = SARC.FromBytes(new MemoryStream(yaz0_decomp));
+
+                for (int i = 0; i < s.Files.Length; i++)
+                {
+                    // Get grid index from filename
+                    string grid_index_str = s.SFNT.FileNames[i].Replace($"5{zoom_level}", "").Replace(extension.Replace(".sstera", ""), "").Replace(folder + "\\", "");
+                    int grid_index = int.Parse(grid_index_str, System.Globalization.NumberStyles.HexNumber);
+                    int[] xyoffs = GridConverter.ZCurveToXY(grid_index);
+                    HGHT h = HGHT.FromBytes(s.Files[i]);
+
+                    for (int j = 0; j < h.Heights.Length; j++)
+                    {
+                        ushort height = h.Heights[j];
+
+                        int x = j % grid_size;
+                        int y = j / grid_size;
+
+                        // Draw pixel at the correct X, Y coordinate
+                        Int32Rect rect = new Int32Rect(x + (xyoffs[0] * grid_size), y + (xyoffs[1] * grid_size), 1, 1);
+                        // Brightness
+                        byte brightness = (byte)(height / 65535f * 255f);
+                        // Use water type color + brightness adjust
+                        byte[] color = new byte[] { brightness };
+
+                        writeableBitmap.WritePixels(rect, color, writeableBitmap.BackBufferStride, 0);
+                    }
+                }
+            }
+        }
+
+        private void PixelView_SetGrassEXTM(string folder, string extension, byte zoom_level, int grid_size)
+        {
+            BOTWConsole.Log("Setting view to grass");
+            string[] ext_files = Directory.GetFiles(folder, $"5{zoom_level}*{extension}");
+
+            foreach (string ext_file in ext_files)
+            {
+                byte[] yaz0_bytes = File.ReadAllBytes(Path.Combine(folder, ext_file));
+                byte[] yaz0_decomp = Yaz0.Decompress(yaz0_bytes);
+                SARC s = SARC.FromBytes(new MemoryStream(yaz0_decomp));
+
+                for (int i = 0; i < s.Files.Length; i++)
+                {
+                    // Get grid index from filename
+                    string grid_index_str = s.SFNT.FileNames[i].Replace($"5{zoom_level}", "").Replace(extension.Replace(".sstera", ""), "").Replace(folder + "\\", "");
+                    int grid_index = int.Parse(grid_index_str, System.Globalization.NumberStyles.HexNumber);
+                    int[] xyoffs = GridConverter.ZCurveToXY(grid_index);
+                    Grass[] grasses = Grass.FromBytes(s.Files[i]);
+
+                    for (int j = 0; j < grasses.Length; j++)
+                    {
+                        Grass g = grasses[j];
+
+                        int x = j % grid_size;
+                        int y = j / grid_size;
+
+                        // Draw pixel at the correct X, Y coordinate
+                        Int32Rect rect = new Int32Rect(x + (xyoffs[0] * grid_size), y + (xyoffs[1] * grid_size), 1, 1);
+                        // Brightness - clamp values so colors don't become completely muted
+                        float brightness = (g.Height / 255f);
+                        // Use water type color + brightness adjust
+                        byte[] color = new byte[] { (byte)(g.R * brightness), (byte)(g.G * brightness), (byte)(g.B * brightness) };
+
+                        writeableBitmap.WritePixels(rect, color, writeableBitmap.BackBufferStride, 0);
+                    }
+                }
+            }
+        }
+
+        private void PixelView_SetWaterEXTM(string folder, string extension, byte zoom_level, int grid_size)
+        {
+            BOTWConsole.Log("Setting view to water");
+            string[] ext_files = Directory.GetFiles(folder, $"5{zoom_level}*{extension}");
+
+            foreach (string ext_file in ext_files)
+            {
+                byte[] yaz0_bytes = File.ReadAllBytes(Path.Combine(folder, ext_file));
+                byte[] yaz0_decomp = Yaz0.Decompress(yaz0_bytes);
+                SARC s = SARC.FromBytes(new MemoryStream(yaz0_decomp));
+
+                for (int i = 0; i < s.Files.Length; i++)
+                {
+                    // Get grid index from filename
+                    string grid_index_str = s.SFNT.FileNames[i].Replace($"5{zoom_level}", "").Replace(extension.Replace(".sstera", ""), "").Replace(folder + "\\", "");
+                    int grid_index = int.Parse(grid_index_str, System.Globalization.NumberStyles.HexNumber);
+                    int[] xyoffs = GridConverter.ZCurveToXY(grid_index);
+                    Water[] waters = Water.FromBytes(s.Files[i]);
+
+                    for (int j = 0; j < waters.Length; j++)
+                    {
+                        Water w = waters[j];
+
+                        int x = j % grid_size;
+                        int y = j / grid_size;
+
+                        // Draw pixel at the correct X, Y coordinate
+                        Int32Rect rect = new Int32Rect(x + (xyoffs[0] * grid_size), y + (xyoffs[1] * grid_size), 1, 1);
+                        // Brightness - clamp values so colors don't become completely muted
+                        float brightness = (w.Height / 65535f).Clamp(0.2f, 0.8f);
+                        // Get this water's color based off its material index/water type
+                        System.Drawing.Color c = GridColors.WaterColors[w.MaterialIndex];
+                        // Use water type color + brightness adjust
+                        byte[] color = new byte[] { (byte)(c.R * brightness), (byte)(c.G * brightness), (byte)(c.B * brightness) };
+
+                        writeableBitmap.WritePixels(rect, color, writeableBitmap.BackBufferStride, 0);
+                    }
+                }
+            }
+        }
+
+        public void SetEnabled(TSCB t)
         {
             Signature.Text = t.Signature;
-            Version.Text = t.Version.ToString() + ".0.0.0"; // TODO: An actual version based off bytes should be done later
+            Version.Text = t.Version.ToString() + ".0.0.0";
             FileBaseOffset.Text = t.FileBaseOffset.ToString();
             WorldScale.Text = t.WorldScale.ToString();
             TerrainMaxHeight.Text = t.TerrainMaxHeight.ToString();
             MaterialInfoLength.Text = t.MaterialInfoLength.ToString();
             AreaArrayLength.Text = t.AreaArrayLength.ToString();
             TileSize.Text = t.TileSize.ToString();
+
+            foreach (var child in PixelViewTypes.Children)
+            {
+                if (child.GetType() == typeof(Button))
+                {
+                    ((Button)child).IsEnabled = true;
+                }
+                else if (child.GetType() == typeof(TextBox))
+                {
+                    ((TextBox)child).IsEnabled = true;
+                }
+            }
         }
 
-        public void ClearAllTabInfo()
+        public void SetDisabled()
         {
             // Clear sidebar header info
             Signature.Clear();
@@ -44,9 +342,25 @@ namespace BOTWToolset
 
             // Clear area display stack panel
             TSCBAreaViewer.Children.Clear();
+
+            // Clear pixel view
+            ClearBitmap();
+
+            // Disable controls
+            foreach (var child in PixelViewTypes.Children)
+            {
+                if (child.GetType() == typeof(Button))
+                {
+                    ((Button)child).IsEnabled = false;
+                }
+                else if (child.GetType() == typeof(TextBox))
+                {
+                    ((TextBox)child).IsEnabled = false;
+                }
+            }
         }
 
-        private void Menu_OpenFile(object sender, System.Windows.RoutedEventArgs e)
+        private void Menu_FileOpen(object sender, RoutedEventArgs e)
         {
             BOTWConsole.Log("Clicked File -> Open button");
 
@@ -62,41 +376,108 @@ namespace BOTWToolset
 
             if ((bool)openFileDialog.ShowDialog())
             {
+                // This is to ensure that opening a file when one is already open resets everything in the tab
+                SetDisabled();
+
                 BOTWConsole.Log("Opening file");
 
-                TSCBInfo t = TSCB.ReadFile(openFileDialog.FileName);
+                TSCB t = TSCB.ReadFile(openFileDialog.FileName);
 
                 // Set the current file location to the chosen file's location
                 fileLocation = openFileDialog.FileName;
 
                 // Set the current TCSBInfo to the new TSCBInfo
-                currentTSCBInfo = t;
+                currentTSCB = t;
 
-                // Set UI sidebar to have header info
-                SetSidebarHeaderInfo(t);
+                // Set UI sidebar to have header info, enable controls
+                SetEnabled(t);
 
                 // Allow the file to be saved
+                MenuFileClose.IsEnabled = true;
                 MenuFileSave.IsEnabled = true;
+
+                // Really laggy, creating 9,000+ controls isn't necessarily a fantastic idea
+                // Maybe having a filter would help?
+                /*oreach (var area in t.AreaInfo)
+                {
+                    Control.TSCBAreaExpander ae = new Control.TSCBAreaExpander();
+                    ae.AreaExpander.Header = $"({area.PositionX}, {area.PositionZ})";
+                    ae.AreaExpander.IsExpanded = false;
+
+                    TSCBAreaViewer.Children.Add(ae);
+                }*/
             }
         }
 
-        private void Menu_CloseFile(object sender, System.Windows.RoutedEventArgs e)
+        private void Menu_FileClose(object sender, RoutedEventArgs e)
         {
             BOTWConsole.Log("Clicked File -> Close button");
 
             // Set the current TSCB info to nothing
-            currentTSCBInfo = null;
+            currentTSCB = null;
 
-            // Clear sidebar header info
-            ClearAllTabInfo();
+            // Set the tab as disabled
+            SetDisabled();
 
             // Since there's no file open, don't allow saving
+            MenuFileClose.IsEnabled = false;
             MenuFileSave.IsEnabled = false;
         }
 
-        private void Menu_SaveFile(object sender, System.Windows.RoutedEventArgs e)
+        // TODO: split this into "save" and "save as"
+        private void Menu_FileSave(object sender, RoutedEventArgs e)
         {
             BOTWConsole.Log("Clicked File -> Save button");
+
+            SaveFileDialog saveFileDialog = new SaveFileDialog
+            {
+                InitialDirectory = @"C;\",
+                RestoreDirectory = true,
+                Title = "Save TSCB file",
+                DefaultExt = "tscb",
+                Filter = "TSCB files (*.tscb)|*.tscb"
+            };
+
+            if ((bool)saveFileDialog.ShowDialog())
+            {
+                File.WriteAllBytes(saveFileDialog.FileName, TSCB.GetBytes(currentTSCB));
+            }
+        }
+
+        private void OverrideKeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.Key == Key.Return)
+            {
+                TextBox textSender = (TextBox)sender;
+
+                if (float.TryParse(textSender.Text, out float overrideValue))
+                {
+                    // Clamp value between 0 and 1
+                    overrideValue = MathExt.Clamp(overrideValue, 0.0f, 1.0f);
+
+                    BOTWConsole.Log($"Overriding {textSender.Name} with value {overrideValue}");
+
+                    switch (textSender.Name)
+                    {
+                        case "OverrideMinTerrainHeight":
+                            foreach (var area in currentTSCB.AreaInfo)
+                                area.MinTerrainHeight = overrideValue;
+                            break;
+                        case "OverrideMaxTerrainHeight":
+                            foreach (var area in currentTSCB.AreaInfo)
+                                area.MaxTerrainHeight = overrideValue;
+                            break;
+                        case "OverrideMinWaterHeight":
+                            foreach (var area in currentTSCB.AreaInfo)
+                                area.MinWaterHeight = overrideValue;
+                            break;
+                        case "OverrideMaxWaterHeight":
+                            foreach (var area in currentTSCB.AreaInfo)
+                                area.MaxWaterHeight = overrideValue;
+                            break;
+                    }
+                }
+            }
         }
     }
 }
diff --git a/botw-toolset/Control/TabYaz0.xaml b/botw-toolset/Control/TabYaz0.xaml
new file mode 100644
index 0000000..7be2356
--- /dev/null
+++ b/botw-toolset/Control/TabYaz0.xaml
@@ -0,0 +1,27 @@
+
+    
+        
+
+        
+        
+            
+                
+                
+                
+                
+            
+        
+    
+
diff --git a/botw-toolset/Control/TabYaz0.xaml.cs b/botw-toolset/Control/TabYaz0.xaml.cs
new file mode 100644
index 0000000..28615c5
--- /dev/null
+++ b/botw-toolset/Control/TabYaz0.xaml.cs
@@ -0,0 +1,167 @@
+using BOTWToolset.Debugging;
+using BOTWToolset.Exceptions;
+using BOTWToolset.IO.Yaz0;
+using Microsoft.Win32;
+using System.IO;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace BOTWToolset.Control
+{
+    /// 
+    /// Interaction logic for TabYaz0.xaml
+    /// 
+    public partial class TabYaz0 : UserControl
+    {
+        public TabYaz0()
+        {
+            InitializeComponent();
+        }
+
+        private void Menu_FileDecode(object sender, RoutedEventArgs e)
+        {
+            var openFileDialog = new OpenFileDialog
+            {
+                InitialDirectory = Dashboard.UserDesktop,
+                RestoreDirectory = true,
+                Title = "Select Yaz0 file",
+                DefaultExt = "yaz0",
+                Filter = "All Files (*.*)|*.*",
+                CheckFileExists = true
+            };
+
+            if ((bool)openFileDialog.ShowDialog())
+            {
+                try
+                {
+                    byte[] decoded = Yaz0.Decompress(File.ReadAllBytes(openFileDialog.FileName));
+
+                    // If decoding at source is checked, don't open a file save picker
+                    if ((bool)Yaz0DecodeAtSource.IsChecked)
+                    {
+                        string decoded_filename = openFileDialog.FileName;
+                        string ext = Path.GetExtension(openFileDialog.FileName);
+
+                        // Replace extension when decoding
+                        if (ext.StartsWith(".s"))
+                        {
+                            ext = ext.Replace(".s", ".");
+                            decoded_filename = Path.ChangeExtension(decoded_filename, ext);
+                        }
+
+                        File.WriteAllBytes(decoded_filename, decoded);
+                    }
+                    else
+                    {
+                        var saveFileDialog = new SaveFileDialog
+                        {
+                            InitialDirectory = Dashboard.UserDesktop,
+                            RestoreDirectory = true,
+                            Title = "Save decoded Yaz0 file",
+                            Filter = "All Files (*.*)|*.*"
+                        };
+
+                        if ((bool)saveFileDialog.ShowDialog())
+                        {
+                            File.WriteAllBytes(saveFileDialog.FileName, decoded);
+                        }
+                    }
+
+                    BOTWConsole.LogStatus("Yaz0 file successfully decoded.");
+                }
+                catch (InvalidMagicException err)
+                {
+                    BOTWConsole.LogStatus(err.Message);
+                }
+            }
+        }
+
+        private void Menu_FileEncode(object sender, RoutedEventArgs e)
+        {
+            var openFileDialog = new OpenFileDialog
+            {
+                InitialDirectory = Dashboard.UserDesktop,
+                RestoreDirectory = true,
+                Title = "Select file to Yaz0-encode",
+                Filter = "All Files (*.*)|*.*",
+                CheckFileExists = true
+            };
+
+            if ((bool)openFileDialog.ShowDialog())
+            {
+                try
+                {
+                    byte[] encoded = Yaz0.Compress(File.ReadAllBytes(openFileDialog.FileName));
+
+                    // If decoding at source is checked, don't open a file save picker
+                    if ((bool)Yaz0DecodeAtSource.IsChecked)
+                    {
+                        // Replace extension when encoding
+                        string ext = Path.GetExtension(openFileDialog.FileName).Replace(".", ".s");
+                        string encoded_filename = Path.ChangeExtension(openFileDialog.FileName, ext);
+                        File.WriteAllBytes(encoded_filename, encoded);
+                    }
+                    else
+                    {
+                        var saveFileDialog = new SaveFileDialog
+                        {
+                            InitialDirectory = Dashboard.UserDesktop,
+                            RestoreDirectory = true,
+                            Title = "Save encoded Yaz0 file",
+                            Filter = "All Files (*.*)|*.*"
+                        };
+
+                        if ((bool)saveFileDialog.ShowDialog())
+                        {
+                            File.WriteAllBytes(saveFileDialog.FileName, encoded);
+                        }
+                    }
+
+                    BOTWConsole.LogStatus("Yaz0 file successfully encoded.");
+                }
+                catch (InvalidMagicException err)
+                {
+                    BOTWConsole.LogStatus(err.Message);
+                }
+            }
+        }
+
+        /*private void Menu_FileOpen(object sender, RoutedEventArgs e)
+        {
+            BOTWConsole.Log("Clicked File -> Open button");
+
+            var openFileDialog = new OpenFileDialog
+            {
+                InitialDirectory = @"C:\",
+                RestoreDirectory = true,
+                Title = "Select Yaz0 file",
+                DefaultExt = "yaz0",
+                Filter = "All Files (*.*)|*.*",
+                CheckFileExists = true
+            };
+
+            if ((bool)openFileDialog.ShowDialog())
+            {
+                // This is to ensure that opening a file when one is already open resets everything in the tab
+                SetDisabled();
+
+                BOTWConsole.Log("Opening file");
+
+                Yaz0 y = Yaz0.ReadFile(openFileDialog.FileName);
+
+                // Set the current file location to the chosen file's location
+                fileLocation = openFileDialog.FileName;
+
+                currentYaz0 = y;
+
+                // Set UI sidebar to have header info, enable controls
+                SetEnabled(y);
+
+                // Allow the file to be saved
+                MenuFileClose.IsEnabled = true;
+                MenuFileSave.IsEnabled = true;
+                MenuFileSaveAs.IsEnabled = true;
+            }
+        }*/
+    }
+}
diff --git a/botw-toolset/Dashboard.xaml b/botw-toolset/Dashboard.xaml
index 9f3624f..1805fdd 100644
--- a/botw-toolset/Dashboard.xaml
+++ b/botw-toolset/Dashboard.xaml
@@ -3,38 +3,38 @@
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:BOTWToolset"
+        xmlns:local="clr-namespace:BOTWToolset.Control"
         mc:Ignorable="d"
         Title="Breath of the Wild Toolset" Height="720" Width="1280" Icon="Resources/Icons/triforce.png" MinWidth="645" MinHeight="645">
     
-        
+        
             
                 
                     
 
                     
-                        
+                        
                             
                             
                             
                         
                     
                     
-                        
+                        
                             
                             
                             
                         
                     
                     
-                        
+                        
                             
                             
                             
                         
                     
                     
-                        
+                        
                             
                             
                             
@@ -42,14 +42,20 @@
                     
                 
             
-            
+            
                 
             
-            
-            
-            
+            
+                
+            
+            
+                
+            
+            
+                
+            
         
-        
-
+        
+        
     
 
diff --git a/botw-toolset/Dashboard.xaml.cs b/botw-toolset/Dashboard.xaml.cs
index ef308d0..a358012 100644
--- a/botw-toolset/Dashboard.xaml.cs
+++ b/botw-toolset/Dashboard.xaml.cs
@@ -1,4 +1,5 @@
 using BOTWToolset.Debugging;
+using System;
 using System.Windows;
 
 namespace BOTWToolset
@@ -9,6 +10,7 @@ namespace BOTWToolset
     public partial class Dashboard : Window
     {
         public const string VERSION = "1.0.0-pre-alpha";
+        public static string UserDesktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
 
         public Dashboard()
         {
@@ -18,5 +20,13 @@ namespace BOTWToolset
 
             LabelVersion.Content = $"Version v{VERSION}";
         }
+
+        private void TabSelect_TSCB(object sender, System.Windows.Input.MouseButtonEventArgs e) => tabItemTSCB.IsSelected = true;
+
+        private void TabSelect_Yaz0(object sender, System.Windows.Input.MouseButtonEventArgs e) => tabItemYaz0.IsSelected = true;
+
+        private void TabSelect_SARC(object sender, System.Windows.Input.MouseButtonEventArgs e) => tabItemSARC.IsSelected = true;
+
+        private void TabSelect_RSTB(object sender, System.Windows.Input.MouseButtonEventArgs e) => tabItemRSTB.IsSelected = true;
     }
 }