├── .gitignore ├── LICENSE ├── README.md ├── mfs_gui ├── App.config ├── FolderSelectDialog │ ├── FolderSelectDialog.cs │ └── Reflector.cs ├── Icons │ ├── BlueDisk.ico │ ├── BlueDisk.png │ ├── Filetypes │ │ ├── CARD.png │ │ ├── CRSD.png │ │ ├── File.png │ │ ├── GSBLK.png │ │ ├── GSEXP.png │ │ ├── MA2D1.png │ │ ├── MA3D1.png │ │ ├── OPT.png │ │ ├── PSDNS.png │ │ ├── PSMAS.png │ │ ├── PSPPM.png │ │ ├── PSSEA.png │ │ ├── TSANL.png │ │ ├── TSANM.png │ │ ├── TSBGA.png │ │ ├── TSBGL.png │ │ ├── TSTLL.png │ │ └── TSTLT.png │ ├── GreyDisk16.png │ └── Storage │ │ ├── 00.png │ │ ├── 01.png │ │ ├── 02.png │ │ ├── 03.png │ │ ├── 04.png │ │ ├── 05.png │ │ ├── 06.png │ │ ├── 07.png │ │ ├── 08.png │ │ ├── 09.png │ │ └── 10.png ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.resx ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── changelog.txt ├── license.txt ├── mfs_gui.csproj └── readme.txt ├── mfs_library ├── Leo │ ├── Leo.cs │ └── LeoDisk.cs ├── MA │ ├── MA2D1.cs │ └── Yay1.cs ├── MFS │ ├── MFS.cs │ ├── MFSDef.cs │ ├── MFSDisk.cs │ ├── MFSUtil_Directory.cs │ ├── MFSUtil_File.cs │ └── MFSUtil_Other.cs ├── Properties │ └── AssemblyInfo.cs ├── Util │ ├── SJISUtil.cs │ └── Util.cs └── mfs_library.csproj ├── mfs_manager.sln ├── mfs_manager ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── mfs_manager.csproj └── save_manager ├── App.config ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.resx ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings └── save_manager.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 LuigiBlood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 64DD MFS Manager 2 | 3 | Contains three projects: 4 | - **mfs_library**: the main library for disk and MFS access 5 | - **mfs_manager**: the command line utility project 6 | - **mfs_gui**: the user friendly utility 7 | - **save_manager**: basic RAW import/export of disk RAM area 8 | 9 | This software can be used to manage files in 64DD titles that uses MFS for save data. 10 | You can import, extract, copy, move and rename files. 11 | 12 | Supports *.ndd disk images (64DD Dump Tool), *.disk (MAME 64DD disk image format), and *.ram (Pure 64DD disk RAM Partition image). 13 | 14 | The only known titles to use MFS are: 15 | - Mario Artist Paint Studio 16 | - Mario Artist Talent Studio 17 | - Mario Artist Communication Kit 18 | - Mario Artist Polygon Studio 19 | - Japan Pro Golf Tour 64 (do not delete zero3MByte.dat) 20 | - F-Zero X Expansion Kit 21 | - Randnet Disk 22 | 23 | Watch out to make sure to use the proper directories for save management. 24 | -------------------------------------------------------------------------------- /mfs_gui/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /mfs_gui/FolderSelectDialog/FolderSelectDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | // ------------------------------------------------------------------ 5 | // Wraps System.Windows.Forms.OpenFileDialog to make it present 6 | // a vista-style dialog. 7 | // ------------------------------------------------------------------ 8 | 9 | namespace FolderSelect 10 | { 11 | /// 12 | /// Wraps System.Windows.Forms.OpenFileDialog to make it present 13 | /// a vista-style dialog. 14 | /// 15 | public class FolderSelectDialog 16 | { 17 | // Wrapped dialog 18 | System.Windows.Forms.OpenFileDialog ofd = null; 19 | 20 | /// 21 | /// Default constructor 22 | /// 23 | public FolderSelectDialog() 24 | { 25 | ofd = new System.Windows.Forms.OpenFileDialog(); 26 | 27 | ofd.Filter = "Folders|\n"; 28 | ofd.AddExtension = false; 29 | ofd.CheckFileExists = false; 30 | ofd.DereferenceLinks = true; 31 | ofd.Multiselect = false; 32 | } 33 | 34 | #region Properties 35 | 36 | /// 37 | /// Gets/Sets the initial folder to be selected. A null value selects the current directory. 38 | /// 39 | public string InitialDirectory 40 | { 41 | get { return ofd.InitialDirectory; } 42 | set { ofd.InitialDirectory = value == null || value.Length == 0 ? Environment.CurrentDirectory : value; } 43 | } 44 | 45 | /// 46 | /// Gets/Sets the title to show in the dialog 47 | /// 48 | public string Title 49 | { 50 | get { return ofd.Title; } 51 | set { ofd.Title = value == null ? "Select a folder" : value; } 52 | } 53 | 54 | /// 55 | /// Gets the selected folder 56 | /// 57 | public string FileName 58 | { 59 | get { return ofd.FileName; } 60 | } 61 | 62 | #endregion 63 | 64 | #region Methods 65 | 66 | /// 67 | /// Shows the dialog 68 | /// 69 | /// True if the user presses OK else false 70 | public bool ShowDialog() 71 | { 72 | return ShowDialog(IntPtr.Zero); 73 | } 74 | 75 | /// 76 | /// Shows the dialog 77 | /// 78 | /// Handle of the control to be parent 79 | /// True if the user presses OK else false 80 | public bool ShowDialog(IntPtr hWndOwner) 81 | { 82 | bool flag = false; 83 | 84 | if (Environment.OSVersion.Version.Major >= 6) 85 | { 86 | var r = new Reflector("System.Windows.Forms"); 87 | 88 | uint num = 0; 89 | Type typeIFileDialog = r.GetType("FileDialogNative.IFileDialog"); 90 | object dialog = r.Call(ofd, "CreateVistaDialog"); 91 | r.Call(ofd, "OnBeforeVistaDialog", dialog); 92 | 93 | uint options = (uint)r.CallAs(typeof(System.Windows.Forms.FileDialog), ofd, "GetOptions"); 94 | options |= (uint)r.GetEnum("FileDialogNative.FOS", "FOS_PICKFOLDERS"); 95 | r.CallAs(typeIFileDialog, dialog, "SetOptions", options); 96 | 97 | object pfde = r.New("FileDialog.VistaDialogEvents", ofd); 98 | object[] parameters = new object[] { pfde, num }; 99 | r.CallAs2(typeIFileDialog, dialog, "Advise", parameters); 100 | num = (uint)parameters[1]; 101 | try 102 | { 103 | int num2 = (int)r.CallAs(typeIFileDialog, dialog, "Show", hWndOwner); 104 | flag = 0 == num2; 105 | } 106 | finally 107 | { 108 | r.CallAs(typeIFileDialog, dialog, "Unadvise", num); 109 | GC.KeepAlive(pfde); 110 | } 111 | } 112 | else 113 | { 114 | var fbd = new FolderBrowserDialog(); 115 | fbd.Description = this.Title; 116 | fbd.SelectedPath = this.InitialDirectory; 117 | fbd.ShowNewFolderButton = false; 118 | if (fbd.ShowDialog(new WindowWrapper(hWndOwner)) != DialogResult.OK) return false; 119 | ofd.FileName = fbd.SelectedPath; 120 | flag = true; 121 | } 122 | 123 | return flag; 124 | } 125 | 126 | #endregion 127 | } 128 | 129 | /// 130 | /// Creates IWin32Window around an IntPtr 131 | /// 132 | public class WindowWrapper : System.Windows.Forms.IWin32Window 133 | { 134 | /// 135 | /// Constructor 136 | /// 137 | /// Handle to wrap 138 | public WindowWrapper(IntPtr handle) 139 | { 140 | _hwnd = handle; 141 | } 142 | 143 | /// 144 | /// Original ptr 145 | /// 146 | public IntPtr Handle 147 | { 148 | get { return _hwnd; } 149 | } 150 | 151 | private IntPtr _hwnd; 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /mfs_gui/FolderSelectDialog/Reflector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace FolderSelect 5 | { 6 | /// 7 | /// This class is from the Front-End for Dosbox and is used to present a 'vista' dialog box to select folders. 8 | /// Being able to use a vista style dialog box to select folders is much better then using the shell folder browser. 9 | /// http://code.google.com/p/fed/ 10 | /// 11 | /// Example: 12 | /// var r = new Reflector("System.Windows.Forms"); 13 | /// 14 | public class Reflector 15 | { 16 | #region variables 17 | 18 | string m_ns; 19 | Assembly m_asmb; 20 | 21 | #endregion 22 | 23 | #region Constructors 24 | 25 | /// 26 | /// Constructor 27 | /// 28 | /// The namespace containing types to be used 29 | public Reflector(string ns) 30 | : this(ns, ns) 31 | { } 32 | 33 | /// 34 | /// Constructor 35 | /// 36 | /// A specific assembly name (used if the assembly name does not tie exactly with the namespace) 37 | /// The namespace containing types to be used 38 | public Reflector(string an, string ns) 39 | { 40 | m_ns = ns; 41 | m_asmb = null; 42 | foreach (AssemblyName aN in Assembly.GetExecutingAssembly().GetReferencedAssemblies()) 43 | { 44 | if (aN.FullName.StartsWith(an)) 45 | { 46 | m_asmb = Assembly.Load(aN); 47 | break; 48 | } 49 | } 50 | } 51 | 52 | #endregion 53 | 54 | #region Methods 55 | 56 | /// 57 | /// Return a Type instance for a type 'typeName' 58 | /// 59 | /// The name of the type 60 | /// A type instance 61 | public Type GetType(string typeName) 62 | { 63 | Type type = null; 64 | string[] names = typeName.Split('.'); 65 | 66 | if (names.Length > 0) 67 | type = m_asmb.GetType(m_ns + "." + names[0]); 68 | 69 | for (int i = 1; i < names.Length; ++i) { 70 | type = type.GetNestedType(names[i], BindingFlags.NonPublic); 71 | } 72 | return type; 73 | } 74 | 75 | /// 76 | /// Create a new object of a named type passing along any params 77 | /// 78 | /// The name of the type to create 79 | /// 80 | /// An instantiated type 81 | public object New(string name, params object[] parameters) 82 | { 83 | Type type = GetType(name); 84 | 85 | ConstructorInfo[] ctorInfos = type.GetConstructors(); 86 | foreach (ConstructorInfo ci in ctorInfos) { 87 | try { 88 | return ci.Invoke(parameters); 89 | } catch { } 90 | } 91 | 92 | return null; 93 | } 94 | 95 | /// 96 | /// Calls method 'func' on object 'obj' passing parameters 'parameters' 97 | /// 98 | /// The object on which to excute function 'func' 99 | /// The function to execute 100 | /// The parameters to pass to function 'func' 101 | /// The result of the function invocation 102 | public object Call(object obj, string func, params object[] parameters) 103 | { 104 | return Call2(obj, func, parameters); 105 | } 106 | 107 | /// 108 | /// Calls method 'func' on object 'obj' passing parameters 'parameters' 109 | /// 110 | /// The object on which to excute function 'func' 111 | /// The function to execute 112 | /// The parameters to pass to function 'func' 113 | /// The result of the function invocation 114 | public object Call2(object obj, string func, object[] parameters) 115 | { 116 | return CallAs2(obj.GetType(), obj, func, parameters); 117 | } 118 | 119 | /// 120 | /// Calls method 'func' on object 'obj' which is of type 'type' passing parameters 'parameters' 121 | /// 122 | /// The type of 'obj' 123 | /// The object on which to excute function 'func' 124 | /// The function to execute 125 | /// The parameters to pass to function 'func' 126 | /// The result of the function invocation 127 | public object CallAs(Type type, object obj, string func, params object[] parameters) 128 | { 129 | return CallAs2(type, obj, func, parameters); 130 | } 131 | 132 | /// 133 | /// Calls method 'func' on object 'obj' which is of type 'type' passing parameters 'parameters' 134 | /// 135 | /// The type of 'obj' 136 | /// The object on which to excute function 'func' 137 | /// The function to execute 138 | /// The parameters to pass to function 'func' 139 | /// The result of the function invocation 140 | public object CallAs2(Type type, object obj, string func, object[] parameters) { 141 | MethodInfo methInfo = type.GetMethod(func, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 142 | return methInfo.Invoke(obj, parameters); 143 | } 144 | 145 | /// 146 | /// Returns the value of property 'prop' of object 'obj' 147 | /// 148 | /// The object containing 'prop' 149 | /// The property name 150 | /// The property value 151 | public object Get(object obj, string prop) 152 | { 153 | return GetAs(obj.GetType(), obj, prop); 154 | } 155 | 156 | /// 157 | /// Returns the value of property 'prop' of object 'obj' which has type 'type' 158 | /// 159 | /// The type of 'obj' 160 | /// The object containing 'prop' 161 | /// The property name 162 | /// The property value 163 | public object GetAs(Type type, object obj, string prop) { 164 | PropertyInfo propInfo = type.GetProperty(prop, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 165 | return propInfo.GetValue(obj, null); 166 | } 167 | 168 | /// 169 | /// Returns an enum value 170 | /// 171 | /// The name of enum type 172 | /// The name of the value 173 | /// The enum value 174 | public object GetEnum(string typeName, string name) { 175 | Type type = GetType(typeName); 176 | FieldInfo fieldInfo = type.GetField(name); 177 | return fieldInfo.GetValue(null); 178 | } 179 | 180 | #endregion 181 | 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /mfs_gui/Icons/BlueDisk.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/BlueDisk.ico -------------------------------------------------------------------------------- /mfs_gui/Icons/BlueDisk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/BlueDisk.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/CARD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/CARD.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/CRSD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/CRSD.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/File.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/File.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/GSBLK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/GSBLK.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/GSEXP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/GSEXP.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/MA2D1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/MA2D1.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/MA3D1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/MA3D1.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/OPT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/OPT.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/PSDNS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/PSDNS.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/PSMAS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/PSMAS.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/PSPPM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/PSPPM.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/PSSEA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/PSSEA.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/TSANL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/TSANL.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/TSANM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/TSANM.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/TSBGA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/TSBGA.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/TSBGL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/TSBGL.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/TSTLL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/TSTLL.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Filetypes/TSTLT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Filetypes/TSTLT.png -------------------------------------------------------------------------------- /mfs_gui/Icons/GreyDisk16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/GreyDisk16.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/00.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/01.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/02.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/03.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/04.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/05.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/06.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/07.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/08.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/09.png -------------------------------------------------------------------------------- /mfs_gui/Icons/Storage/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuigiBlood/mfs_manager/daa58a829df986d2a77fabfc148e60504d771798/mfs_gui/Icons/Storage/10.png -------------------------------------------------------------------------------- /mfs_gui/MainForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | using FolderSelect; 12 | using mfs_library; 13 | using static System.Net.WebRequestMethods; 14 | 15 | namespace mfs_gui 16 | { 17 | public partial class MainForm : Form 18 | { 19 | enum cliptype 20 | { 21 | Copy, Cut 22 | } 23 | 24 | MFSDirectory current_dir; 25 | MFSFile[] clipboardfiles; 26 | cliptype clipboardtype; 27 | bool changed; 28 | 29 | public MainForm() 30 | { 31 | InitializeComponent(); 32 | } 33 | 34 | private void openToolStripMenuItem_Click(object sender, EventArgs e) 35 | { 36 | OpenFileDialog ofs = new OpenFileDialog(); 37 | ofs.Filter = "All Supported Files (*.ndd, *.ndr, *.ram, *.n64, *.z64, *.disk)|*.ndd;*.ndr;*.ram;*.n64;*.z64;*.disk|64DD RAM Area Image (*.ram)|*.ram|64DD Disk Image (*.ndd, *.ndr, *.disk)|*.ndd;*.ndr;*.disk|N64 Cartridge Port Image (*.n64, *.z64)|*.n64;*.z64|All files|*.*"; 38 | ofs.Title = "Open Disk Image..."; 39 | ofs.Multiselect = false; 40 | if (ofs.ShowDialog() == DialogResult.OK) 41 | { 42 | if (Program.LoadDisk(ofs.FileName)) 43 | { 44 | treeViewMFS.Nodes.Clear(); 45 | listViewMFS.Items.Clear(); 46 | 47 | current_dir = null; 48 | clipboardfiles = null; 49 | 50 | TreeNode node; 51 | if (Program.GetDirectoryNode(out node)) 52 | { 53 | treeViewMFS.Nodes.Add(node); 54 | treeViewMFS.Nodes[0].Expand(); 55 | treeViewMFS.SelectedNode = treeViewMFS.Nodes[0]; 56 | } 57 | } 58 | else 59 | { 60 | MessageBox.Show("Could not load " + ofs.SafeFileName, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 61 | } 62 | UpdateFormText(); 63 | UpdateStatusBar(); 64 | } 65 | } 66 | 67 | private void saveToolStripMenuItem_Click(object sender, EventArgs e) 68 | { 69 | if (Program.IsDiskLoaded()) 70 | { 71 | Program.SaveDisk(); 72 | changed = false; 73 | } 74 | UpdateFormText(); 75 | } 76 | 77 | private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) 78 | { 79 | if (!Program.IsDiskLoaded()) 80 | return; 81 | 82 | SaveFileDialog sfd = new SaveFileDialog(); 83 | sfd.Title = "Save as..."; 84 | if (sfd.ShowDialog() == DialogResult.OK) 85 | { 86 | Program.SaveDisk(sfd.FileName); 87 | changed = false; 88 | } 89 | UpdateFormText(); 90 | } 91 | 92 | private void exitToolStripMenuItem_Click(object sender, EventArgs e) 93 | { 94 | Application.Exit(); 95 | } 96 | 97 | private void aboutToolStripMenuItem_Click(object sender, EventArgs e) 98 | { 99 | MessageBox.Show("64DD MFS Manager (GUI) " + Application.ProductVersion + " by LuigiBlood", "About...", MessageBoxButtons.OK, MessageBoxIcon.Information); 100 | } 101 | 102 | private void MainForm_FormClosing(object sender, FormClosingEventArgs e) 103 | { 104 | if (changed && MessageBox.Show("Are you sure you want to exit 64DD MFS Manager?\nYour changes will be lost.", "Exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) 105 | e.Cancel = true; 106 | } 107 | 108 | private void treeView_AfterSelect(object sender, TreeViewEventArgs e) 109 | { 110 | current_dir = (MFSDirectory)e.Node.Tag; 111 | UpdateTreeView(current_dir); 112 | } 113 | 114 | private void listView_DragEnter(object sender, DragEventArgs e) 115 | { 116 | e.Effect = DragDropEffects.None; 117 | if (current_dir != null) 118 | e.Effect = DragDropEffects.Copy; 119 | } 120 | 121 | private void listView_DragDrop(object sender, DragEventArgs e) 122 | { 123 | string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); 124 | Program.AddFilesToDirectory(current_dir, files); 125 | UpdateTreeView(current_dir); 126 | changed = true; 127 | UpdateFormText(); 128 | } 129 | 130 | private void contextMenuStripFile_Opening(object sender, CancelEventArgs e) 131 | { 132 | foreach (ToolStripItem test in contextMenuStripFile.Items) 133 | { 134 | test.Enabled = false; 135 | } 136 | if (listViewMFS.SelectedItems.Count > 0) 137 | { 138 | extractToolStripMenuItem.Enabled = true; 139 | cutToolStripMenuItem.Enabled = true; 140 | copyToolStripMenuItem.Enabled = true; 141 | deleteToolStripMenuItem.Enabled = true; 142 | if (listViewMFS.SelectedItems.Count == 1) 143 | renameToolStripMenuItem.Enabled = true; 144 | 145 | List files = new List(); 146 | foreach (ListViewItem item in listViewMFS.SelectedItems) 147 | { 148 | files.Add(((MFSFile)item.Tag).GetEntryName()); 149 | } 150 | if (Program.CanExportConvertFiles(files.ToArray())) 151 | { 152 | convertFilesToolStripMenuItem.Enabled = true; 153 | } 154 | 155 | } 156 | if (current_dir != null) 157 | importToolStripMenuItem.Enabled = true; 158 | if (clipboardfiles != null && clipboardfiles.Length > 0) 159 | pasteToolStripMenuItem.Enabled = true; 160 | } 161 | 162 | private void importToolStripMenuItem_Click(object sender, EventArgs e) 163 | { 164 | OpenFileDialog ofs = new OpenFileDialog(); 165 | ofs.Filter = "All files|*.*"; 166 | ofs.Title = "Import Files..."; 167 | ofs.Multiselect = true; 168 | 169 | if (ofs.ShowDialog() == DialogResult.OK) 170 | { 171 | string[] files = ofs.FileNames; 172 | Program.AddFilesToDirectory(current_dir, files); 173 | UpdateTreeView(current_dir); 174 | changed = true; 175 | UpdateFormText(); 176 | } 177 | } 178 | 179 | private void extractToolStripMenuItem_Click(object sender, EventArgs e) 180 | { 181 | if (listViewMFS.SelectedItems.Count > 1) 182 | { 183 | //More than one file 184 | FolderSelectDialog fsd = new FolderSelectDialog(); 185 | fsd.Title = "Export Files..."; 186 | if (fsd.ShowDialog()) 187 | { 188 | List files = new List(); 189 | foreach (ListViewItem item in listViewMFS.SelectedItems) 190 | { 191 | files.Add((MFSFile)item.Tag); 192 | } 193 | if (Program.SaveFiles(files.ToArray(), fsd.FileName)) 194 | MessageBox.Show("Files are extracted successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 195 | } 196 | } 197 | else if (listViewMFS.SelectedItems.Count == 1) 198 | { 199 | //Only one file 200 | MFSFile file = (MFSFile)listViewMFS.SelectedItems[0].Tag; 201 | SaveFileDialog sfd = new SaveFileDialog(); 202 | sfd.Title = "Export File..."; 203 | sfd.FileName = file.Name + (file.Ext != "" ? "." + file.Ext : ""); 204 | if (sfd.ShowDialog() == DialogResult.OK) 205 | { 206 | if (Program.SaveFile(file, sfd.FileName)) 207 | MessageBox.Show("File is extracted successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 208 | } 209 | } 210 | } 211 | 212 | private void convertFilesToolStripMenuItem_Click(object sender, EventArgs e) 213 | { 214 | if (listViewMFS.SelectedItems.Count > 1) 215 | { 216 | //More than one file 217 | FolderSelectDialog fsd = new FolderSelectDialog(); 218 | fsd.Title = "Convert File(s)..."; 219 | if (fsd.ShowDialog()) 220 | { 221 | List files = new List(); 222 | foreach (ListViewItem item in listViewMFS.SelectedItems) 223 | { 224 | files.Add((MFSFile)item.Tag); 225 | } 226 | if (Program.ExportConvertFiles(files.ToArray(), fsd.FileName)) 227 | MessageBox.Show("Files are converted successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 228 | } 229 | } 230 | else if (listViewMFS.SelectedItems.Count == 1) 231 | { 232 | //Only one file 233 | MFSFile file = (MFSFile)listViewMFS.SelectedItems[0].Tag; 234 | if (!Program.CanExportConvertFile(file.GetEntryName())) 235 | { 236 | return; 237 | } 238 | SaveFileDialog sfd = new SaveFileDialog(); 239 | sfd.Title = "Convert File..."; 240 | sfd.FileName = file.GetEntryName() + ".png"; 241 | sfd.Filter = "Image File (*.png)|*.png"; 242 | if (sfd.ShowDialog() == DialogResult.OK) 243 | { 244 | if (Program.ExportConvertFile(file, sfd.FileName)) 245 | MessageBox.Show("File is converted successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 246 | } 247 | } 248 | } 249 | 250 | private void deleteToolStripMenuItem_Click(object sender, EventArgs e) 251 | { 252 | if (listViewMFS.SelectedItems.Count > 0) 253 | { 254 | List files = new List(); 255 | foreach (ListViewItem item in listViewMFS.SelectedItems) 256 | { 257 | files.Add((MFSFile)item.Tag); 258 | } 259 | Program.DeleteFiles(files.ToArray()); 260 | UpdateTreeView(current_dir); 261 | changed = true; 262 | UpdateFormText(); 263 | } 264 | } 265 | 266 | private void cutToolStripMenuItem_Click(object sender, EventArgs e) 267 | { 268 | List files = new List(); 269 | foreach (ListViewItem item in listViewMFS.SelectedItems) 270 | { 271 | files.Add((MFSFile)item.Tag); 272 | } 273 | 274 | clipboardfiles = files.ToArray(); 275 | clipboardtype = cliptype.Cut; 276 | } 277 | 278 | private void copyToolStripMenuItem_Click(object sender, EventArgs e) 279 | { 280 | List files = new List(); 281 | foreach (ListViewItem item in listViewMFS.SelectedItems) 282 | { 283 | files.Add((MFSFile)item.Tag); 284 | } 285 | 286 | clipboardfiles = files.ToArray(); 287 | clipboardtype = cliptype.Copy; 288 | } 289 | 290 | private void pasteToolStripMenuItem_Click(object sender, EventArgs e) 291 | { 292 | if (clipboardtype == cliptype.Copy) 293 | Program.CopyFiles(clipboardfiles, current_dir.DirectoryID); 294 | else 295 | Program.MoveFiles(clipboardfiles, current_dir.DirectoryID); 296 | UpdateTreeView(current_dir); 297 | changed = true; 298 | UpdateFormText(); 299 | } 300 | 301 | private void renameToolStripMenuItem_Click(object sender, EventArgs e) 302 | { 303 | if (listViewMFS.SelectedItems.Count == 1) 304 | { 305 | listViewMFS.SelectedItems[0].BeginEdit(); 306 | } 307 | } 308 | 309 | private void listView_AfterLabelEdit(object sender, LabelEditEventArgs e) 310 | { 311 | MFSFile file = (MFSFile)listViewMFS.Items[e.Item].Tag; 312 | if (e.Label != null) 313 | { 314 | string _name = Path.GetFileNameWithoutExtension(e.Label); 315 | string _ext = Path.GetExtension(e.Label); 316 | if (_ext.StartsWith(".")) 317 | _ext = _ext.Substring(1); 318 | 319 | if (file.Ext != _ext) 320 | { 321 | if (MessageBox.Show("Are you sure to change the extension of the file? It may not work as intended.", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No) 322 | return; 323 | changed = true; 324 | } 325 | 326 | if (file.Name != _name) 327 | changed = true; 328 | 329 | file.Ext = _ext; 330 | file.Name = _name; 331 | } 332 | UpdateTreeView(current_dir); 333 | UpdateFormText(); 334 | } 335 | 336 | //Other 337 | private void UpdateTreeView(MFSDirectory dir) 338 | { 339 | ListViewItem[] items; 340 | listViewMFS.Items.Clear(); 341 | if (Program.GetAllFilesFromDirectory(dir, out items)) 342 | { 343 | listViewMFS.Items.AddRange(items); 344 | } 345 | UpdateStatusBar(); 346 | } 347 | 348 | private void UpdateStatusBar() 349 | { 350 | statusStrip1.Items.Clear(); 351 | if (Program.IsDiskLoaded()) 352 | { 353 | statusStrip1.Items.Add("Free Space: " + Math.Floor((Program.GetCapacitySize() - Program.GetUsedSpaceSize()) / 100000f) / 10f + " MB"); 354 | } 355 | } 356 | 357 | private void UpdateFormText() 358 | { 359 | if (Program.IsDiskLoaded()) 360 | { 361 | this.Text = "64DD MFS Manager - " + (changed ? "*" : "") + "[" + Program.GetDiskFilename() + "]"; 362 | } 363 | else 364 | { 365 | this.Text = "64DD MFS Manager"; 366 | } 367 | } 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /mfs_gui/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | using System.Windows.Forms; 7 | using mfs_library; 8 | using System.Drawing; 9 | 10 | namespace mfs_gui 11 | { 12 | static class Program 13 | { 14 | static MainForm mainform; 15 | static string loadedfilepath; 16 | static MFSDisk disk; 17 | 18 | static char[] symbolsContainer = { '♦', '■', '●', '♥', '♠', '♣', '▼', '♪', '▲', '★' }; 19 | 20 | [STAThread] 21 | static void Main() 22 | { 23 | Application.EnableVisualStyles(); 24 | Application.SetCompatibleTextRenderingDefault(false); 25 | mainform = new MainForm(); 26 | Application.Run(mainform); 27 | } 28 | 29 | public static bool IsDiskLoaded() 30 | { 31 | if (disk == null) return false; 32 | return (disk.Disk.RAMFileSystem == LeoDisk.FileSystem.MFS); 33 | } 34 | 35 | public static string GetDiskFilename() 36 | { 37 | return loadedfilepath; 38 | } 39 | 40 | public static bool LoadDisk(string filepath) 41 | { 42 | MFSDisk disk_temp = new MFSDisk(filepath); 43 | if (disk_temp.Disk.RAMFileSystem == LeoDisk.FileSystem.MFS) 44 | { 45 | loadedfilepath = filepath; 46 | disk = disk_temp; 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | public static bool SaveDisk(string filepath = "") 53 | { 54 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 55 | return false; 56 | 57 | if (filepath == "") 58 | filepath = loadedfilepath; 59 | 60 | disk.Save(filepath); 61 | 62 | loadedfilepath = filepath; 63 | return true; 64 | } 65 | 66 | public static bool GetDirectoryNode(out TreeNode nodes) 67 | { 68 | nodes = new TreeNode(); 69 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 70 | return false; 71 | 72 | nodes = GetDirectoryTreeNode(MFSRAMUtil.GetDirectoryFromID(disk, 0)); 73 | nodes.ImageIndex = 0; 74 | nodes.SelectedImageIndex = 0; 75 | return true; 76 | } 77 | 78 | static TreeNode GetDirectoryTreeNode(MFSDirectory pdir) 79 | { 80 | TreeNode node = new TreeNode(pdir.Name); 81 | node.Tag = pdir; 82 | node.ImageIndex = GetContainerColor(pdir.Name); 83 | node.SelectedImageIndex = node.ImageIndex; 84 | foreach (MFSDirectory dir in MFSRAMUtil.GetAllDirectoriesFromDirID(disk, pdir.DirectoryID)) 85 | { 86 | node.Nodes.Add(GetDirectoryTreeNode(dir)); 87 | } 88 | return node; 89 | } 90 | 91 | static int GetContainerColor(string name) 92 | { 93 | foreach (char c in name) 94 | { 95 | for (int i = 0; i < symbolsContainer.Length; i++) 96 | if (c == symbolsContainer[i]) return i + 2; 97 | } 98 | return 1; 99 | } 100 | 101 | public static bool GetAllFilesFromDirectory(MFSDirectory dir, out ListViewItem[] items) 102 | { 103 | List list = new List(); 104 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 105 | { 106 | items = null; 107 | return false; 108 | } 109 | 110 | foreach (MFSFile file in MFSRAMUtil.GetAllFilesFromDirID(disk, dir.DirectoryID)) 111 | { 112 | ListViewItem item = new ListViewItem(file.Name + (file.Ext != "" ? "." + file.Ext : "")); 113 | item.Tag = file; 114 | item.ImageIndex = mainform.imageListLarge.Images.IndexOfKey(file.Ext); 115 | if (item.ImageIndex == -1) 116 | item.ImageIndex = 0; 117 | list.Add(item); 118 | } 119 | 120 | items = list.ToArray(); 121 | return true; 122 | } 123 | 124 | public static bool AddFileToDirectory(MFSDirectory dir, string filepath) 125 | { 126 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 127 | { 128 | return false; 129 | } 130 | 131 | bool error; 132 | if (!CanImportConvertFile(filepath)) 133 | { 134 | byte[] filedata = File.ReadAllBytes(filepath); 135 | error = MFSRAMUtil.WriteFile(disk, filedata, Path.GetFileName(filepath), dir.DirectoryID); 136 | } 137 | else 138 | { 139 | byte[] filedata; 140 | string filename; 141 | 142 | if (ImportConvertFile(filepath, out filedata, out filename)) 143 | { 144 | error = MFSRAMUtil.WriteFile(disk, filedata, filename, dir.DirectoryID); 145 | } 146 | else 147 | { 148 | error = false; 149 | } 150 | } 151 | 152 | return error; 153 | } 154 | 155 | public static bool AddFilesToDirectory(MFSDirectory dir, string[] filepaths) 156 | { 157 | foreach (string file in filepaths) 158 | { 159 | if (Program.AddFileToDirectory(dir, file) == false) 160 | { 161 | MessageBox.Show("Could not import " + Path.GetFileName(file) + "\nCancelling file import.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 162 | break; 163 | } 164 | } 165 | return true; 166 | } 167 | 168 | public static bool ImportConvertFile(string filepath, out byte[] bytes, out string filename) 169 | { 170 | if (!CanImportConvertFile(filepath)) 171 | { 172 | bytes = null; 173 | filename = null; 174 | return false; 175 | } 176 | 177 | Bitmap input = new Bitmap(filepath); 178 | 179 | bytes = mfs_library.MA.MA2D1.ConvertToMA2D1(input); 180 | filename = Path.GetFileNameWithoutExtension(filepath) + ".MA2D1"; 181 | 182 | input.Dispose(); 183 | 184 | return true; 185 | } 186 | 187 | public static bool CanImportConvertFile(string filepath) 188 | { 189 | switch (Path.GetExtension(filepath).ToLower()) 190 | { 191 | case ".png": 192 | case ".bmp": 193 | case ".jpg": 194 | case ".jpeg": 195 | return true; 196 | } 197 | return false; 198 | } 199 | 200 | public static bool CanImportConvertFiles(string[] filepaths) 201 | { 202 | foreach (string s in filepaths) 203 | { 204 | switch (Path.GetExtension(s).ToLower()) 205 | { 206 | case ".png": 207 | case ".bmp": 208 | case ".jpg": 209 | case ".jpeg": 210 | return true; 211 | } 212 | } 213 | return false; 214 | } 215 | 216 | public static bool ExportConvertFile(MFSFile file, string filepath) 217 | { 218 | if (!CanExportConvertFile(file.GetEntryName())) return false; 219 | 220 | var input = LoadFileData(file); 221 | 222 | Bitmap output = mfs_library.MA.MA2D1.ConvertToBitmap(input); 223 | output.Save(filepath); 224 | output.Dispose(); 225 | return true; 226 | } 227 | 228 | public static bool ExportConvertFiles(MFSFile[] file, string folderpath) 229 | { 230 | foreach (MFSFile f in file) 231 | { 232 | ExportConvertFile(f, folderpath + "\\" + f.GetEntryName() + ".png"); 233 | } 234 | return true; 235 | } 236 | 237 | public static bool CanExportConvertFile(string filepath) 238 | { 239 | switch (Path.GetExtension(filepath).ToLower()) 240 | { 241 | case ".ma2d1": 242 | return true; 243 | } 244 | return false; 245 | } 246 | 247 | public static bool CanExportConvertFiles(string[] filepaths) 248 | { 249 | foreach (string s in filepaths) 250 | { 251 | switch (Path.GetExtension(s).ToLower()) 252 | { 253 | case ".ma2d1": 254 | return true; 255 | } 256 | } 257 | return false; 258 | } 259 | 260 | public static byte[] LoadFileData(MFSFile file) 261 | { 262 | return MFSRAMUtil.ReadFile(disk, file); 263 | } 264 | 265 | public static bool SaveFiles(MFSFile[] files, string folderpath) 266 | { 267 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 268 | { 269 | return false; 270 | } 271 | 272 | foreach (MFSFile file in files) 273 | { 274 | byte[] filedata = MFSRAMUtil.ReadFile(disk, file); 275 | FileStream fileout = new FileStream(folderpath + "\\" + file.Name + (file.Ext != "" ? "." + file.Ext : ""), FileMode.Create); 276 | fileout.Write(filedata, 0, filedata.Length); 277 | fileout.Close(); 278 | } 279 | return true; 280 | } 281 | 282 | public static bool DeleteFiles(MFSFile[] files) 283 | { 284 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 285 | { 286 | return false; 287 | } 288 | 289 | foreach (MFSFile file in files) 290 | { 291 | MFSRAMUtil.DeleteFile(disk, file); 292 | } 293 | return true; 294 | } 295 | 296 | public static bool SaveFile(MFSFile file, string filepath) 297 | { 298 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 299 | { 300 | return false; 301 | } 302 | 303 | byte[] filedata = MFSRAMUtil.ReadFile(disk, file); 304 | FileStream fileout = new FileStream(filepath, FileMode.Create); 305 | fileout.Write(filedata, 0, filedata.Length); 306 | fileout.Close(); 307 | 308 | return true; 309 | } 310 | 311 | public static bool CopyFiles(MFSFile[] files, ushort dir) 312 | { 313 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 314 | { 315 | return false; 316 | } 317 | 318 | foreach (MFSFile file in files) 319 | { 320 | MFSRAMUtil.WriteFile(disk, MFSRAMUtil.ReadFile(disk, file), file.Name + (file.Ext != "" ? "." + file.Ext : ""), dir); 321 | } 322 | 323 | return true; 324 | } 325 | 326 | public static bool MoveFiles(MFSFile[] files, ushort dir) 327 | { 328 | if (disk == null || disk.Disk.Format == LeoDisk.DiskFormat.Invalid) 329 | { 330 | return false; 331 | } 332 | 333 | foreach (MFSFile file in files) 334 | { 335 | MFSRAMUtil.MoveFile(disk, MFSRAMUtil.GetFullPath(disk, file), MFSRAMUtil.GetFullPath(disk, MFSRAMUtil.GetDirectoryFromID(disk, dir))); 336 | } 337 | 338 | return true; 339 | } 340 | 341 | public static int GetCapacitySize() 342 | { 343 | return MFSRAMUtil.GetCapacitySize(disk); 344 | } 345 | 346 | public static int GetUsedSpaceSize() 347 | { 348 | return MFSRAMUtil.GetTotalUsedSize(disk); 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /mfs_gui/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Les informations générales relatives à un assembly dépendent de 6 | // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations 7 | // associées à un assembly. 8 | [assembly: AssemblyTitle("64DD MFS Manager (GUI)")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("64DD MFS Manager (GUI)")] 13 | [assembly: AssemblyCopyright("Copyright © LuigiBlood 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly 18 | // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de 19 | // COM, affectez la valeur true à l'attribut ComVisible sur ce type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM 23 | [assembly: Guid("48ec8983-43d6-4686-b33d-fd46fec8ce8d")] 24 | 25 | // Les informations de version pour un assembly se composent des quatre valeurs suivantes : 26 | // 27 | // Version principale 28 | // Version secondaire 29 | // Numéro de build 30 | // Révision 31 | // 32 | // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut 33 | // en utilisant '*', comme indiqué ci-dessous : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.3.0.0")] 36 | [assembly: AssemblyFileVersion("1.3.0.0")] 37 | -------------------------------------------------------------------------------- /mfs_gui/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Ce code a été généré par un outil. 4 | // Version du runtime :4.0.30319.42000 5 | // 6 | // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si 7 | // le code est régénéré. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace mfs_gui.Properties 12 | { 13 | 14 | 15 | /// 16 | /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. 17 | /// 18 | // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder 19 | // à l'aide d'un outil, tel que ResGen ou Visual Studio. 20 | // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen 21 | // avec l'option /str ou régénérez votre projet VS. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("mfs_gui.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Remplace la propriété CurrentUICulture du thread actuel pour toutes 56 | /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /mfs_gui/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /mfs_gui/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace mfs_gui.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mfs_gui/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /mfs_gui/changelog.txt: -------------------------------------------------------------------------------- 1 | 64DD MFS Manager 2 | 3 | Changelog: 4 | v1.3.0.0: 5 | - mfs_library: Refactored a lot of classes and other processes 6 | - mfs_library: Add MA2D1 Image conversion 7 | - mfs_manager: Add check for MFS Filesystem 8 | - mfs_gui: Do not unload/reload when you try to load another disk 9 | - mfs_gui: Add automatic image import conversion 10 | - mfs_gui: Add image export conversion 11 | - save_manager: Added 12 | 13 | v1.2.1.1: 14 | - mfs_library: Fix off-by-one error that prevents reading the last LBA of the disk 15 | 16 | v1.2.1.0: 17 | - mfs_library: Create library using mfs_manager functions and refactoring. 18 | - mfs_library: Seperate Disk management from MFSDisk class into its own class (LeoDisk). 19 | - mfs_library: Fix offset calculation with MAME Disk format access (Critical) 20 | - mfs_manager: Process extension change based on filename and extension. 21 | 22 | v1.2.0.0: 23 | - Support MAME format disk images (used by Ares for saves) 24 | - Rudimentary code to keep track of changes for saving and also to only provide a prompt when exiting if the changes were not saved. 25 | 26 | v0.0.3.1: 27 | - Add forgotten N64 Cartridge Port support to the File Loading window 28 | 29 | v0.0.3.0: 30 | - Fixed locked Drag & Drop import after loading a disk. 31 | - Added Import option into the right click menu. 32 | - Show Free Space size on status bar. 33 | - Warn when files cannot be imported. 34 | 35 | v0.0.2.0: 36 | - Cartridge Port ROM support 37 | 38 | v0.0.1.0: 39 | - Initial release 40 | -------------------------------------------------------------------------------- /mfs_gui/license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | mfs_manager 4 | Copyright (c) 2019 LuigiBlood 5 | 6 | Mr-Peeps-Compressor 7 | Copyright (c) 2017 Dan McCarthy 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | -------------------------------------------------------------------------------- /mfs_gui/mfs_gui.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {48EC8983-43D6-4686-B33D-FD46FEC8CE8D} 8 | WinExe 9 | mfs_gui 10 | mfs_gui 11 | v4.6.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | Icons\BlueDisk.ico 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Form 60 | 61 | 62 | MainForm.cs 63 | 64 | 65 | 66 | 67 | MainForm.cs 68 | 69 | 70 | ResXFileCodeGenerator 71 | Resources.Designer.cs 72 | Designer 73 | 74 | 75 | True 76 | Resources.resx 77 | 78 | 79 | SettingsSingleFileGenerator 80 | Settings.Designer.cs 81 | 82 | 83 | True 84 | Settings.settings 85 | True 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | Always 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | Always 128 | 129 | 130 | Always 131 | 132 | 133 | 134 | 135 | False 136 | Microsoft .NET Framework 4.6.1 %28x86 et x64%29 137 | true 138 | 139 | 140 | False 141 | .NET Framework 3.5 SP1 142 | false 143 | 144 | 145 | 146 | 147 | {8d1cf3e5-ba0f-4790-ba0a-c52b79302da3} 148 | mfs_library 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /mfs_gui/readme.txt: -------------------------------------------------------------------------------- 1 | 64DD MFS Manager v1.3.0.0 2 | 3 | This contains the following: 4 | - mfs_library: the main library for disk and MFS access 5 | - mfs_manager: the command line utility project 6 | - mfs_gui: the user friendly utility 7 | - save_manager: basic RAW import/export of disk RAM area 8 | 9 | 10 | This software can be used to manage files in 64DD titles that uses MFS for save data. You can import, extract, copy, move and rename files. 11 | Supports *.ndd disk images (64DD Dump Tool), *.disk (MAME 64DD disk image format), and *.ram (Pure 64DD disk RAM Partition image). 12 | 13 | Use at your own risk. 14 | 15 | The only known titles to use MFS are: 16 | - Mario Artist Paint Studio 17 | - Mario Artist Talent Studio 18 | - Mario Artist Communication Kit 19 | - Mario Artist Polygon Studio 20 | - Japan Pro Golf Tour 64 (do not delete zero3MByte.dat) 21 | - F-Zero X Expansion Kit 22 | - Randnet Disk 23 | 24 | Watch out to make sure to use the proper directories for save management. 25 | 26 | 27 | Important Notices: 28 | - When you import BMP, PNG, or JPG files, it is automatically converted and resized to 216x202 MA2D1 files (2D picture format for Mario Artist). 29 | -------------------------------------------------------------------------------- /mfs_library/Leo/Leo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace mfs_library 8 | { 9 | public static class Leo 10 | { 11 | public const int MAX_LBA = 0x10DB; 12 | public const int SIZE_LBA = MAX_LBA + 1; 13 | public const int SYSTEM_LBAS = 24; 14 | public const int DISKID_LBA = 14; 15 | 16 | public const int SECTORS_PER_BLOCK = 85; 17 | 18 | public const int BLOCKS_PER_TRACK = 2; 19 | 20 | public const int DISK_SIZE_MAME = 0x435B0C0; 21 | public const int DISK_SIZE_SDK = 0x3DEC800; 22 | 23 | /* LBA to Disk System Data (Retail) */ 24 | public static readonly int[] LBA_SYS_PROD = { 0, 1, 8, 9 }; 25 | 26 | /* LBA to Disk System Data (Development) */ 27 | public static readonly int[] LBA_SYS_DEV = { 2, 3, 10, 11 }; 28 | 29 | /* Sector Size in bytes [zone] */ 30 | public static readonly int[] SECTOR_SIZE = { 232, 216, 208, 192, 176, 160, 144, 128, 112 }; 31 | 32 | /* Block Size in bytes [zone] */ 33 | public static readonly int[] BLOCK_SIZE = { 0x4D08, 0x47B8, 0x4510, 0x3FC0, 0x3A70, 0x3520, 0x2FD0, 0x2A80, 0x2530 }; 34 | 35 | /* Outer Track per Zone [zone] */ 36 | static readonly int[] PZONE_TRACK = { 0x000, 0x09E, 0x13C, 0x1D1, 0x266, 0x2FB, 0x390, 0x425, 0x497 }; 37 | 38 | /* Start Track of Zone [pzone] */ 39 | static readonly int[] TRACK_START_ZONE_TBL = 40 | {0x000, 0x09E, 0x13C, 0x1D1, 0x266, 0x2FB, 0x390, 0x425, 41 | 0x091, 0x12F, 0x1C4, 0x259, 0x2EE, 0x383, 0x418, 0x48A}; 42 | 43 | /* LBA to VZone [type,vzone] */ 44 | static readonly int[,] VZONE_LBA_TBL = { 45 | {0x0124, 0x0248, 0x035A, 0x047E, 0x05A2, 0x06B4, 0x07C6, 0x08D8, 0x09EA, 0x0AB6, 0x0B82, 0x0C94, 0x0DA6, 0x0EB8, 0x0FCA, 0x10DC}, 46 | {0x0124, 0x0248, 0x035A, 0x046C, 0x057E, 0x06A2, 0x07C6, 0x08D8, 0x09EA, 0x0AFC, 0x0BC8, 0x0C94, 0x0DA6, 0x0EB8, 0x0FCA, 0x10DC}, 47 | {0x0124, 0x0248, 0x035A, 0x046C, 0x057E, 0x0690, 0x07A2, 0x08C6, 0x09EA, 0x0AFC, 0x0C0E, 0x0CDA, 0x0DA6, 0x0EB8, 0x0FCA, 0x10DC}, 48 | {0x0124, 0x0248, 0x035A, 0x046C, 0x057E, 0x0690, 0x07A2, 0x08B4, 0x09C6, 0x0AEA, 0x0C0E, 0x0D20, 0x0DEC, 0x0EB8, 0x0FCA, 0x10DC}, 49 | {0x0124, 0x0248, 0x035A, 0x046C, 0x057E, 0x0690, 0x07A2, 0x08B4, 0x09C6, 0x0AD8, 0x0BEA, 0x0D0E, 0x0E32, 0x0EFE, 0x0FCA, 0x10DC}, 50 | {0x0124, 0x0248, 0x035A, 0x046C, 0x057E, 0x0690, 0x07A2, 0x086E, 0x0980, 0x0A92, 0x0BA4, 0x0CB6, 0x0DC8, 0x0EEC, 0x1010, 0x10DC}, 51 | {0x0124, 0x0248, 0x035A, 0x046C, 0x057E, 0x0690, 0x07A2, 0x086E, 0x093A, 0x0A4C, 0x0B5E, 0x0C70, 0x0D82, 0x0E94, 0x0FB8, 0x10DC} 52 | }; 53 | 54 | /* VZone to PZone [type,vzone] */ 55 | static readonly int[,] VZONE_PZONE_TBL = { 56 | {0x0, 0x1, 0x2, 0x9, 0x8, 0x3, 0x4, 0x5, 0x6, 0x7, 0xF, 0xE, 0xD, 0xC, 0xB, 0xA}, 57 | {0x0, 0x1, 0x2, 0x3, 0xA, 0x9, 0x8, 0x4, 0x5, 0x6, 0x7, 0xF, 0xE, 0xD, 0xC, 0xB}, 58 | {0x0, 0x1, 0x2, 0x3, 0x4, 0xB, 0xA, 0x9, 0x8, 0x5, 0x6, 0x7, 0xF, 0xE, 0xD, 0xC}, 59 | {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xC, 0xB, 0xA, 0x9, 0x8, 0x6, 0x7, 0xF, 0xE, 0xD}, 60 | {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8, 0x7, 0xF, 0xE}, 61 | {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xE, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8, 0xF}, 62 | {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xF, 0xE, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8} 63 | }; 64 | 65 | /* LBA Start RAM Area [type] */ 66 | public static readonly short[] RamStartLBA = { 0x5A2, 0x7C6, 0x9EA, 0xC0E, 0xE32, 0x1010, 0x10DC }; 67 | 68 | /* RAM Area Total Sizes [type] */ 69 | public static readonly int[] RamSize = { 0x24A9DC0, 0x1C226C0, 0x1450F00, 0xD35680, 0x6CFD40, 0x1DA240, 0x0 }; 70 | 71 | /* LBA To Virtual Zone */ 72 | public static int LBAToVZone(int lba, int disktype) 73 | { 74 | for (int vzone = 0; vzone < 16; vzone++) 75 | { 76 | if (lba < VZONE_LBA_TBL[disktype,vzone]) 77 | { 78 | return vzone; 79 | } 80 | } 81 | return -1; 82 | } 83 | 84 | /* Virtual Zone to Physical Zone */ 85 | public static int VZoneToPZone(int vzone, int disktype) { return VZONE_PZONE_TBL[disktype, vzone]; } 86 | 87 | /* Calculate byte size from LBA x to LBA x+y */ 88 | public static int LBAToByte(int disktype, int startlba, int nlbas) 89 | { 90 | if (disktype < 0 && disktype > 7) throw new ArgumentOutOfRangeException("Disk Type out of range", disktype.ToString()); 91 | if (startlba < 0 && startlba > Leo.MAX_LBA) throw new ArgumentOutOfRangeException("LBA Start out of range", startlba.ToString()); 92 | if (nlbas < 0 && nlbas > Leo.MAX_LBA) throw new ArgumentOutOfRangeException("LBA Amount out of range", nlbas.ToString()); 93 | if ((startlba + nlbas) < 0 && (startlba + nlbas) > Leo.MAX_LBA) throw new ArgumentOutOfRangeException("LBA Start out of range", (startlba + nlbas).ToString()); 94 | 95 | int totalbytes = 0; 96 | bool init_flag = true; 97 | int vzone = 1; 98 | int pzone = 0; 99 | int lba = startlba; 100 | int lba_count = nlbas; 101 | int blkbytes = 0; 102 | if (nlbas != 0) 103 | { 104 | for (; lba_count != 0; lba_count--) 105 | { 106 | if ((init_flag) || (VZONE_LBA_TBL[disktype, vzone] == lba)) 107 | { 108 | vzone = LBAToVZone(lba, disktype); 109 | pzone = VZoneToPZone(vzone, disktype); 110 | if (7 < pzone) 111 | { 112 | pzone -= 7; 113 | } 114 | blkbytes = BLOCK_SIZE[pzone]; 115 | } 116 | totalbytes += blkbytes; 117 | lba++; 118 | init_flag = false; 119 | if ((lba_count > 1) && (lba > MAX_LBA)) 120 | { 121 | return -1; 122 | } 123 | } 124 | } 125 | return totalbytes; 126 | } 127 | 128 | /* MAME Disk Format Offsets for each zone */ 129 | static readonly int[] MAMEOffsetTable = 130 | {0x0,0x5F15E0,0xB79D00,0x10801A0,0x1523720,0x1963D80,0x1D414C0,0x20BBCE0, 131 | 0x23196E0,0x28A1E00,0x2DF5DC0,0x3299340,0x36D99A0,0x3AB70E0,0x3E31900,0x4149200}; 132 | 133 | public static int LBAToMAMEOffset(int lba, byte[] sysData) 134 | { 135 | if (lba < 0 && lba > Leo.MAX_LBA) throw new ArgumentOutOfRangeException("LBA out of range", lba.ToString()); 136 | 137 | int head, track, block; 138 | LBAToPhys(lba, sysData, out head, out track, out block); 139 | return PhysToMAMEOffset(head, track, block, 0); 140 | } 141 | 142 | public static int PhysToMAMEOffset(int head, int track, int block, int sector) 143 | { 144 | int pzone = Array.FindIndex(PZONE_TRACK, x => track < x) - 1; 145 | 146 | int trackRelative = track - PZONE_TRACK[pzone]; 147 | 148 | int offsetCalc = MAMEOffsetTable[pzone + (head * 8)]; 149 | offsetCalc += BLOCK_SIZE[pzone + head] * 2 * trackRelative; 150 | offsetCalc += block * BLOCK_SIZE[pzone + head]; 151 | offsetCalc += sector * SECTOR_SIZE[pzone + head]; 152 | 153 | if (offsetCalc >= DISK_SIZE_MAME) throw new ArgumentOutOfRangeException("Offset Result is out of bounds.", "0x" + offsetCalc.ToString("X")); 154 | 155 | return offsetCalc; 156 | } 157 | 158 | public static void LBAToPhys(int lba, byte[] sysData, out int head, out int track, out int block) 159 | { 160 | if (lba < 0 && lba > Leo.MAX_LBA) throw new ArgumentOutOfRangeException("LBA out of range", lba.ToString()); 161 | 162 | //Get Disk Type 163 | int diskType = sysData[0x05] & 0x0F; 164 | 165 | //Get Block 166 | if (((lba & 3) == 0) || ((lba & 3) == 3)) 167 | block = 0; 168 | else 169 | block = 1; 170 | 171 | //Calculate Head & Track 172 | int VZone = LBAToVZone(lba, diskType); 173 | int PZone = VZoneToPZone(VZone, diskType); 174 | 175 | //Get Head 176 | head = (7 < PZone) ? 1 : 0; 177 | 178 | int calcPZone = PZone - (head * 7); 179 | 180 | int calcTrack = (lba - ((VZone == 0) ? 0 : VZONE_LBA_TBL[diskType, VZone - 1])) / BLOCKS_PER_TRACK; 181 | 182 | //int zoneTrackStart = PZONE_TRACK[calcPZone - head]; 183 | int zoneTrackStart = TRACK_START_ZONE_TBL[PZone]; 184 | if (head == 1) 185 | { 186 | calcTrack = -calcTrack; 187 | zoneTrackStart = PZONE_TRACK[calcPZone - head]; 188 | } 189 | calcTrack += TRACK_START_ZONE_TBL[PZone]; 190 | 191 | int calcDefectOffset = (PZone == 0) ? 0 : sysData[8 + PZone - 1]; 192 | int calcDefectAmount = sysData[8 + PZone] - calcDefectOffset; 193 | 194 | while ((calcDefectAmount > 0) && ((sysData[0x20 + calcDefectOffset] + zoneTrackStart) <= calcTrack)) 195 | { 196 | calcTrack++; 197 | calcDefectOffset++; 198 | calcDefectAmount--; 199 | } 200 | 201 | track = calcTrack; 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /mfs_library/Leo/LeoDisk.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SqlServer.Server; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Security.Cryptography; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using static mfs_library.MFS; 10 | using static System.Net.Mime.MediaTypeNames; 11 | 12 | namespace mfs_library 13 | { 14 | public class LeoDisk 15 | { 16 | public enum DiskFormat 17 | { 18 | SDK, 19 | D64, 20 | RAM, 21 | N64, 22 | MAME, 23 | Invalid 24 | } 25 | 26 | public enum FileSystem 27 | { 28 | MFS, 29 | ATNFS, 30 | Invalid 31 | } 32 | 33 | public string Filename; 34 | public DiskFormat Format; 35 | public FileSystem RAMFileSystem; 36 | public int OffsetToRamArea; 37 | public int OffsetToSysData; 38 | public int DiskType; 39 | public byte[] Data; 40 | 41 | public LeoDisk(string filepath) 42 | { 43 | Load(filepath); 44 | } 45 | 46 | void Load(string filepath) 47 | { 48 | //Assume file is bad first 49 | Format = DiskFormat.Invalid; 50 | RAMFileSystem = FileSystem.Invalid; 51 | OffsetToSysData = -1; 52 | OffsetToRamArea = -1; 53 | if (!File.Exists(filepath)) 54 | { 55 | return; 56 | } 57 | 58 | FileStream file = new FileStream(filepath, FileMode.Open); 59 | 60 | if (file.Length > Leo.RamSize[0]) 61 | { 62 | //Perform System Area heuristics if the file size is MAME or SDK 63 | bool correctSysData = false; 64 | byte[] sysData = new byte[Leo.SECTOR_SIZE[0]]; 65 | if ((file.Length == Leo.DISK_SIZE_MAME) || (file.Length == Leo.DISK_SIZE_SDK)) 66 | { 67 | //Check each System Data Block 68 | 69 | //Check Retail SysData 70 | foreach (int lba in Leo.LBA_SYS_PROD) 71 | { 72 | OffsetToSysData = Leo.BLOCK_SIZE[0] * lba; 73 | file.Seek(OffsetToSysData, SeekOrigin.Begin); 74 | file.Read(sysData, 0, sysData.Length); 75 | 76 | bool isEqual = true; 77 | for (int i = 1; i < Leo.SECTORS_PER_BLOCK; i++) 78 | { 79 | byte[] sysDataCompare = new byte[sysData.Length]; 80 | file.Read(sysDataCompare, 0, sysDataCompare.Length); 81 | 82 | //Compare Bytes 83 | for (int j = 0; j < sysDataCompare.Length; j++) 84 | { 85 | if (sysDataCompare[j] != sysData[j]) 86 | { 87 | isEqual = false; 88 | break; 89 | } 90 | } 91 | //If not equal then don't bother doing more 92 | if (!isEqual) break; 93 | } 94 | 95 | correctSysData = isEqual; 96 | 97 | //If SysData is found then it's fine don't bother checking the rest 98 | if (correctSysData) break; 99 | } 100 | 101 | //Check Dev SysData if not found 102 | if (!correctSysData) 103 | { 104 | sysData = new byte[Leo.SECTOR_SIZE[3]]; 105 | foreach (int lba in Leo.LBA_SYS_DEV) 106 | { 107 | OffsetToSysData = Leo.BLOCK_SIZE[0] * lba; 108 | file.Seek(OffsetToSysData, SeekOrigin.Begin); 109 | file.Read(sysData, 0, sysData.Length); 110 | 111 | bool isEqual = true; 112 | for (int i = 1; i < Leo.SECTORS_PER_BLOCK; i++) 113 | { 114 | byte[] sysDataCompare = new byte[sysData.Length]; 115 | file.Read(sysDataCompare, 0, sysDataCompare.Length); 116 | 117 | //Compare Bytes 118 | for (int j = 0; j < sysDataCompare.Length; j++) 119 | { 120 | if (sysDataCompare[j] != sysData[j]) 121 | { 122 | isEqual = false; 123 | break; 124 | } 125 | } 126 | //If not equal then don't bother doing more 127 | if (!isEqual) break; 128 | } 129 | 130 | correctSysData = isEqual; 131 | 132 | //If SysData is found then it's fine don't bother checking the rest 133 | if (correctSysData) break; 134 | } 135 | } 136 | } 137 | 138 | if (file.Length == Leo.DISK_SIZE_MAME) 139 | { 140 | /* --- Check if it's MAME Format --- */ 141 | 142 | //if SysData found 143 | if (correctSysData) 144 | { 145 | DiskType = sysData[0x5] & 0xF; 146 | Format = DiskFormat.MAME; 147 | OffsetToRamArea = 0; 148 | //Data is good. 149 | } 150 | } 151 | else if (file.Length == Leo.DISK_SIZE_SDK) 152 | { 153 | /* --- Check if it's SDK Format --- */ 154 | 155 | //if SysData found 156 | if (correctSysData) 157 | { 158 | DiskType = sysData[0x5] & 0xF; 159 | Format = DiskFormat.SDK; 160 | OffsetToRamArea = Leo.LBAToByte(DiskType, 0, Leo.RamStartLBA[DiskType]); 161 | //Data is good. 162 | } 163 | } 164 | else 165 | { 166 | /* --- Check if it's N64 CART Format --- */ 167 | 168 | //SHA256 check if N64 Cartridge Port bootloader 169 | byte[] headerTest = new byte[0xFC0]; 170 | file.Seek(0x40, SeekOrigin.Begin); 171 | file.Read(headerTest, 0, headerTest.Length); 172 | 173 | SHA256 hashHeader = SHA256.Create(); 174 | hashHeader.ComputeHash(headerTest); 175 | 176 | string hashHeaderStr = ""; 177 | foreach (byte b in hashHeader.Hash) 178 | hashHeaderStr += b.ToString("x2"); 179 | 180 | Console.WriteLine(hashHeaderStr); 181 | 182 | int offsetStart = 0; 183 | 184 | //SHA256 = 53c0088fb777870d0af32f0251e964030e2e8b72e830c26042fd191169508c05 185 | if (hashHeaderStr == "53c0088fb777870d0af32f0251e964030e2e8b72e830c26042fd191169508c05") 186 | { 187 | offsetStart = 0x738C0 - 0x10E8; //Start of User LBA 0 (24 w/ System Area) 188 | 189 | file.Seek(0x1000, SeekOrigin.Begin); 190 | file.Read(sysData, 0, sysData.Length); 191 | 192 | DiskType = sysData[0x5] & 0xF; 193 | Format = DiskFormat.N64; 194 | OffsetToRamArea = Leo.LBAToByte(DiskType, 0, Leo.RamStartLBA[DiskType]) - offsetStart; 195 | OffsetToSysData = 0x1000; 196 | //Data is good. 197 | } 198 | } 199 | } 200 | else 201 | { 202 | /* --- Check if it's RAM Format --- */ 203 | if (Array.Exists(Leo.RamSize, x => x == file.Length)) 204 | { 205 | DiskType = Array.FindIndex(Leo.RamSize, x => x == file.Length); 206 | Format = DiskFormat.RAM; 207 | OffsetToRamArea = 0; 208 | //Data is good. 209 | } 210 | } 211 | 212 | if (Format != DiskFormat.Invalid) 213 | { 214 | //Copy full file 215 | Data = new byte[file.Length]; 216 | file.Seek(0, SeekOrigin.Begin); 217 | file.Read(Data, 0, Data.Length); 218 | //Disk is considered loaded here. 219 | } 220 | 221 | file.Close(); 222 | 223 | /* Check RAM FileSystem */ 224 | if (Format != DiskFormat.Invalid) 225 | { 226 | //Only check if RAM Area exists (Disk Type 6 has no RAM area) 227 | if (DiskType < 6) 228 | { 229 | //MultiFileSystem 230 | byte[] test = new byte[MFS.RAM_ID.Length]; 231 | byte[] firstRAM = ReadLBA(Leo.RamStartLBA[DiskType]); 232 | Array.Copy(firstRAM, test, test.Length); 233 | 234 | //See if equal to RAM_ID, and if so, it is found. 235 | if (Encoding.ASCII.GetString(test).Equals(MFS.RAM_ID)) 236 | { 237 | RAMFileSystem = FileSystem.MFS; 238 | } 239 | } 240 | } 241 | 242 | Filename = filepath; 243 | } 244 | 245 | public void Save(string filepath) 246 | { 247 | FileStream file = new FileStream(filepath, FileMode.Create); 248 | file.Write(Data, 0, Data.Length); 249 | file.Close(); 250 | } 251 | 252 | public byte[] ReadLBA(int lba) 253 | { 254 | //Do not read anywhere before RAM Area 255 | if (lba < Leo.RamStartLBA[DiskType]) return null; 256 | if (lba > Leo.MAX_LBA) return null; 257 | 258 | //Read Block 259 | byte[] output = new byte[Leo.LBAToByte(DiskType, lba, 1)]; 260 | if (Format == DiskFormat.MAME) 261 | { 262 | int sourceOffset = Leo.LBAToMAMEOffset(lba, GetSystemData()); 263 | Array.Copy(Data, sourceOffset, output, 0, output.Length); 264 | } 265 | else 266 | { 267 | if (OffsetToRamArea < 0) return null; 268 | int sourceOffset = Leo.LBAToByte(DiskType, Leo.RamStartLBA[DiskType], lba - Leo.RamStartLBA[DiskType]) + OffsetToRamArea; 269 | Array.Copy(Data, sourceOffset, output, 0, output.Length); 270 | } 271 | 272 | return output; 273 | } 274 | 275 | public void WriteLBA(int lba, byte[] data) 276 | { 277 | //Do not write anywhere before RAM Area 278 | if (lba < Leo.RamStartLBA[DiskType]) return; 279 | if (lba > Leo.MAX_LBA) return; 280 | 281 | //Check if data block is exact size of the expected LBA block size 282 | int blockSize = Leo.LBAToByte(DiskType, lba, 1); 283 | if (data.Length != blockSize) return; 284 | 285 | //Write Block 286 | if (Format == DiskFormat.MAME) 287 | { 288 | int destOffset = Leo.LBAToMAMEOffset(lba, GetSystemData()); 289 | Array.Copy(data, 0, Data, destOffset, blockSize); 290 | } 291 | else 292 | { 293 | if (OffsetToRamArea < 0) return; 294 | int destOffset = Leo.LBAToByte(DiskType, Leo.RamStartLBA[DiskType], lba - Leo.RamStartLBA[DiskType]) + OffsetToRamArea; 295 | Array.Copy(data, 0, Data, destOffset, blockSize); 296 | } 297 | } 298 | 299 | public byte[] GetSystemData() 300 | { 301 | if (OffsetToSysData < 0) return null; 302 | 303 | byte[] sysData = new byte[Leo.SECTOR_SIZE[0]]; 304 | 305 | Array.Copy(Data, OffsetToSysData, sysData, 0, sysData.Length); 306 | 307 | return sysData; 308 | } 309 | 310 | public byte[] GetRAMAreaArray() 311 | { 312 | if (OffsetToRamArea < 0) return null; 313 | 314 | List array = new List(); 315 | 316 | for (int lba = Leo.RamStartLBA[DiskType]; lba <= Leo.MAX_LBA; lba++) 317 | { 318 | array.AddRange(ReadLBA(lba)); 319 | } 320 | 321 | return array.ToArray(); 322 | } 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /mfs_library/MA/MA2D1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Drawing; 7 | using System.Security.Cryptography; 8 | using System.IO; 9 | 10 | namespace mfs_library.MA 11 | { 12 | public static class MA2D1 13 | { 14 | public static Bitmap ConvertToBitmap(byte[] bytes) 15 | { 16 | byte[] headerf = new byte[16]; 17 | Array.Copy(bytes, 0x480, headerf, 0, headerf.Length); 18 | string header = Encoding.ASCII.GetString(headerf); 19 | 20 | Bitmap bitmap = null; 21 | if (header.StartsWith("RGBA")) 22 | { 23 | //RAW 24 | int width = Convert.ToInt32(header.Substring(4, 3)); 25 | int height = Convert.ToInt32(header.Substring(7, 3)); 26 | int size = Convert.ToInt32(header.Substring(10, 6)); 27 | 28 | bitmap = new Bitmap(width, height); 29 | 30 | for (int y = 0; y < height; y++) 31 | { 32 | for (int x = 0; x < width; x++) 33 | { 34 | ushort color = (ushort)((bytes[0x490 + (y * width * 2) + (x * 2)] << 8) + bytes[0x490 + (y * width * 2) + (x * 2) + 1]); 35 | bitmap.SetPixel(x, y, RGBA16ToColor(color)); 36 | } 37 | } 38 | } 39 | else if (header.StartsWith("NCMP")) 40 | { 41 | //Yay1 42 | 43 | MemoryStream str = new MemoryStream(bytes); 44 | str.Seek(0x490, SeekOrigin.Begin); 45 | byte[] decomp = Yay1.Decompress(str); 46 | 47 | int width = Convert.ToInt32(header.Substring(4, 3)); 48 | int height = Convert.ToInt32(header.Substring(7, 3)); 49 | int size = Convert.ToInt32(header.Substring(10, 6)); 50 | 51 | bitmap = new Bitmap(width, height); 52 | 53 | for (int y = 0; y < height; y++) 54 | { 55 | for (int x = 0; x < width; x++) 56 | { 57 | ushort color = (ushort)((decomp[(y * width * 2) + (x * 2)] << 8) + decomp[(y * width * 2) + (x * 2) + 1]); 58 | bitmap.SetPixel(x, y, RGBA16ToColor(color)); 59 | } 60 | } 61 | } 62 | 63 | return bitmap; 64 | } 65 | 66 | public static byte[] ConvertToMA2D1(Bitmap bitmap) 67 | { 68 | byte[] thumbnail = ConvertToRGBA16(bitmap, 24, 24, true); 69 | byte[] image = ConvertToRGBA16(bitmap, 216, 202); 70 | string header = "RGBA216202" + image.Length.ToString("D6"); 71 | 72 | List data = new List(); 73 | data.AddRange(thumbnail); 74 | data.AddRange(Encoding.ASCII.GetBytes(header)); 75 | data.AddRange(image); 76 | 77 | return data.ToArray(); 78 | } 79 | 80 | static byte[] ConvertToRGBA16(Bitmap bitmap, int width, int height, bool forcealpha = false) 81 | { 82 | Bitmap output = new Bitmap(width, height); 83 | using (Graphics g = Graphics.FromImage(output)) 84 | { 85 | g.DrawImage(bitmap, 0, 0, width, height); 86 | } 87 | 88 | List bytes = new List(); 89 | for (int y = 0; y < height; y++) 90 | { 91 | for (int x = 0; x < width; x++) 92 | { 93 | ushort color = ColorToRGBA16(output.GetPixel(x, y), forcealpha); 94 | bytes.Add((byte)(color >> 8)); 95 | bytes.Add((byte)(color >> 0)); 96 | } 97 | } 98 | 99 | return bytes.ToArray(); 100 | } 101 | 102 | static ushort ColorToRGBA16(Color color, bool forcealpha = false) 103 | { 104 | uint r = (uint)((color.R / 255f) * 31f) & 0x1F; 105 | uint g = (uint)((color.G / 255f) * 31f) & 0x1F; 106 | uint b = (uint)((color.B / 255f) * 31f) & 0x1F; 107 | uint a = (uint)((color.A / 255f) * 1f) & 1; 108 | if (forcealpha) a = 1; 109 | 110 | ushort output = (ushort)((r << 11) + (g << 6) + (b << 1) + a); 111 | 112 | return output; 113 | } 114 | 115 | static Color RGBA16ToColor(ushort rgba) 116 | { 117 | int r = (int)((((rgba >> 11) & 0x1F) / 31f) * 255f); 118 | int g = (int)((((rgba >> 6) & 0x1F) / 31f) * 255f); 119 | int b = (int)((((rgba >> 1) & 0x1F) / 31f) * 255f); 120 | int a = (rgba & 1) * 255; 121 | 122 | Color output = Color.FromArgb(a, r, g, b); 123 | return output; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /mfs_library/MA/Yay1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.IO; 7 | using System.Collections; 8 | 9 | namespace mfs_library.MA 10 | { 11 | public static class Yay1 12 | { 13 | public static byte[] Decompress(Stream data) 14 | { 15 | // This is reusing the code from 16 | // https://github.com/Daniel-McCarthy/Mr-Peeps-Compressor/blob/master/PeepsCompress/PeepsCompress/Algorithm%20Classes/YAY0.cs 17 | // By Daniel McCarthy, under the MIT License 18 | int beginningOffset = (int)data.Position; 19 | 20 | byte[] temp = new byte[4]; 21 | data.Read(temp, 0, 4); 22 | string header = Encoding.ASCII.GetString(temp).ToLower(); 23 | 24 | if (header != "yay0" && header != "yay1") 25 | return null; 26 | 27 | List output = new List(); 28 | 29 | data.Read(temp, 0, 4); Array.Reverse(temp, 0, 4); 30 | int outputLength = BitConverter.ToInt32(temp, 0); 31 | data.Read(temp, 0, 4); Array.Reverse(temp, 0, 4); 32 | int compOffset = BitConverter.ToInt32(temp, 0) + beginningOffset; 33 | data.Read(temp, 0, 4); Array.Reverse(temp, 0, 4); 34 | int uncompOffset = BitConverter.ToInt32(temp, 0) + beginningOffset; 35 | 36 | int currentOffset; 37 | 38 | while (output.Count < outputLength) 39 | { 40 | byte bits = (byte)data.ReadByte(); //byte of layout bits 41 | BitArray arrayOfBits = new BitArray(new byte[1] { bits }); 42 | 43 | for (int i = 7; i > -1 && (output.Count < outputLength); i--) 44 | { 45 | if (arrayOfBits[i] == true) 46 | { 47 | //non-compressed 48 | //add one byte from uncompressedOffset to newFile 49 | 50 | currentOffset = (int)data.Position; 51 | 52 | data.Seek(uncompOffset, SeekOrigin.Begin); 53 | 54 | output.Add((byte)data.ReadByte()); 55 | uncompOffset++; 56 | 57 | data.Seek(currentOffset, SeekOrigin.Begin); 58 | 59 | } 60 | else 61 | { 62 | //compressed 63 | //read 2 bytes 64 | //4 bits = length 65 | //12 bits = offset 66 | 67 | currentOffset = (int)data.Position; 68 | data.Seek(compOffset, SeekOrigin.Begin); 69 | 70 | byte byte1 = (byte)data.ReadByte(); 71 | byte byte2 = (byte)data.ReadByte(); 72 | compOffset += 2; 73 | 74 | byte byte1Upper = (byte)((byte1 & 0x0F)); 75 | byte byte1Lower = (byte)((byte1 & 0xF0) >> 4); 76 | 77 | int finalOffset = ((byte1Upper << 8) | byte2) + 1; 78 | int finalLength; 79 | 80 | if (byte1Lower == 0) 81 | { 82 | data.Seek(uncompOffset, SeekOrigin.Begin); 83 | finalLength = (byte)data.ReadByte() + 0x12; 84 | uncompOffset++; 85 | } 86 | else 87 | { 88 | finalLength = byte1Lower + 2; 89 | } 90 | 91 | for (int j = 0; j < finalLength; j++) //add data for finalLength iterations 92 | { 93 | output.Add(output[output.Count - finalOffset]); //add byte at offset (fileSize - finalOffset) to file 94 | } 95 | 96 | data.Seek(currentOffset, SeekOrigin.Begin); //return to layout bits 97 | 98 | } 99 | } 100 | } 101 | 102 | return output.ToArray(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /mfs_library/MFS/MFS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace mfs_library 9 | { 10 | public static class MFS 11 | { 12 | //MFS Volume 13 | public struct VolumeAttr 14 | { 15 | public bool isWriteProtected; //Filesystem is Write Protected 16 | public bool isVolumeWriteProtected; //Cannot be written by other applications 17 | public bool isVolumeReadProtected; //Cannot be read by other applications 18 | } 19 | 20 | public const string ROM_ID = "64dd-Multi0201"; 21 | public const string RAM_ID = "64dd-Multi0101"; 22 | 23 | //MFS FAT 24 | public enum FAT : ushort 25 | { 26 | Unused = 0x0000, 27 | DontManage = 0xFFFD, 28 | Prohibited = 0xFFFE, 29 | LastFileBlock = 0xFFFF 30 | } 31 | 32 | public const int FAT_MAX = 2874; 33 | 34 | //MFS Entry 35 | public struct EntryAttr 36 | { 37 | public bool CopyLimit; //Limit Copy 38 | public bool Encode; //Encode 39 | public bool Hidden; //Hidden 40 | public bool DisableRead; //Cannot be read by other applications 41 | public bool DisableWrite; //Cannot be written, renamed, or deleted by other applications 42 | } 43 | 44 | public static readonly ushort[] EntryLimit = { 899, 814, 729, 644, 559, 474, 0 }; 45 | 46 | public class Date 47 | { 48 | public uint Year; 49 | public uint Month; 50 | public uint Day; 51 | public uint Hour; 52 | public uint Minute; 53 | public uint Second; 54 | 55 | public Date() 56 | { 57 | Year = (uint)DateTime.Now.Year; 58 | Month = (uint)DateTime.Now.Month; 59 | Day = (uint)DateTime.Now.Day; 60 | Hour = (uint)DateTime.Now.Hour; 61 | Minute = (uint)DateTime.Now.Minute; 62 | Second = (uint)DateTime.Now.Second; 63 | } 64 | 65 | public Date(uint data) 66 | { 67 | Load(data); 68 | } 69 | 70 | public void Load(uint data) 71 | { 72 | Year = (data >> 25) + 1996; 73 | Month = (data >> 21) & 0xF; 74 | Day = (data >> 16) & 0x1F; 75 | Hour = (data >> 11) & 0x1F; 76 | Minute = (data >> 5) & 0x3F; 77 | Second = ((data >> 0) & 0x1F) * 2; 78 | } 79 | 80 | public uint Save() 81 | { 82 | uint temp = 0; 83 | 84 | temp |= (Year - 1996) << 25; 85 | temp |= Month << 21; 86 | temp |= Day << 16; 87 | temp |= Hour << 11; 88 | temp |= Minute << 5; 89 | temp |= (Second / 2) << 0; 90 | 91 | return temp; 92 | } 93 | } 94 | 95 | enum Error 96 | { 97 | Good = 0, Argument, Filename, FileNotExist, DiskFull, FileAlreadyExists 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /mfs_library/MFS/MFSDef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace mfs_library 9 | { 10 | public class MFSRAMVolume 11 | { 12 | public MFS.VolumeAttr Attributes; 13 | public byte DiskType; 14 | public string Name; 15 | public MFS.Date Date; 16 | public ushort Renewal; 17 | public byte Country; 18 | 19 | public ushort[] FAT; //5748 / 2 = 2874 FAT entries 20 | public List Entries; 21 | 22 | public MFSRAMVolume(byte[] Data, int Offset) 23 | { 24 | Load(Data, Offset); 25 | } 26 | 27 | public void Load(byte[] Data, int Offset) 28 | { 29 | Attributes.isWriteProtected = (Data[Offset + 0x0E] & 0x80) != 0; 30 | Attributes.isVolumeReadProtected = (Data[Offset + 0x0E] & 0x40) != 0; 31 | Attributes.isVolumeWriteProtected = (Data[Offset + 0x0E] & 0x20) != 0; 32 | DiskType = Data[Offset + 0x0F]; 33 | Name = Util.ReadStringN(Data, Offset + 0x10, 0x14); 34 | Date = new MFS.Date(Util.ReadBEU32(Data, Offset + 0x24)); 35 | Renewal = Util.ReadBEU16(Data, Offset + 0x28); 36 | Country = Data[Offset + 0x2A]; 37 | 38 | //Get All FAT Entries 39 | FAT = new ushort[MFS.FAT_MAX]; 40 | for (int i = 0; i < MFS.FAT_MAX; i++) 41 | { 42 | FAT[i] = Util.ReadBEU16(Data, Offset + 0x3C + (i * 2)); 43 | } 44 | 45 | //Get All File Entries 46 | Entries = new List(); 47 | for (int i = 0; i < MFS.EntryLimit[DiskType]; i++) 48 | { 49 | int check = (Data[Offset + 0x16B0 + (i * 0x30)] & 0xC0); 50 | if (check == 0x80) 51 | { 52 | //Directory 53 | MFSDirectory dir = new MFSDirectory(Data, Offset + 0x16B0 + (i * 0x30)); 54 | Entries.Add(dir); 55 | } 56 | else if (check == 0x40) 57 | { 58 | //File 59 | MFSFile file = new MFSFile(Data, Offset + 0x16B0 + (i * 0x30)); 60 | Entries.Add(file); 61 | } 62 | } 63 | } 64 | 65 | public byte[] SaveToArray() 66 | { 67 | byte[] temp = new byte[Leo.LBAToByte(DiskType, Leo.RamStartLBA[DiskType], 3)]; 68 | 69 | Util.WriteStringN(MFS.RAM_ID, temp, 0, MFS.RAM_ID.Length); 70 | temp[0x0E] = (byte)(0 | (Attributes.isVolumeWriteProtected ? 0x20 : 0) | (Attributes.isVolumeReadProtected ? 0x40 : 0) | (Attributes.isWriteProtected ? 0x80 : 0)); 71 | temp[0x0F] = (byte)DiskType; 72 | 73 | Util.WriteStringN(Name, temp, 0x10, 0x14); 74 | Util.WriteBEU32(Date.Save(), temp, 0x24); 75 | Util.WriteBEU16(Renewal, temp, 0x28); 76 | temp[0x2A] = Country; 77 | 78 | //Save FAT Entries 79 | for (int i = 0; i < FAT.Length; i++) 80 | { 81 | Util.WriteBEU16(FAT[i], temp, 0x3C + (i * 2)); 82 | } 83 | 84 | //Save File Entries 0x16B0 85 | for (int i = 0; i < Entries.Count; i++) 86 | { 87 | byte[] tempentry; 88 | if (Entries[i].GetType() == typeof(MFSDirectory)) 89 | tempentry = ((MFSDirectory)Entries[i]).Save(); 90 | else 91 | tempentry = ((MFSFile)Entries[i]).Save(); 92 | Array.Copy(tempentry, 0, temp, 0x16B0 + (0x30 * i), 0x30); 93 | } 94 | 95 | //Checksum 96 | uint crc = 0; 97 | for (int i = 0; i < (temp.Length / 4); i++) 98 | { 99 | crc ^= Util.ReadBEU32(temp, i * 4); 100 | } 101 | Util.WriteBEU32(crc, temp, 0x2C); 102 | 103 | return temp; 104 | } 105 | } 106 | 107 | public class MFSEntry 108 | { 109 | public MFS.EntryAttr Attributes; 110 | public ushort ParentDirectory; 111 | public string CompanyCode; 112 | public string GameCode; 113 | 114 | public string Name; 115 | 116 | public byte Renewal; 117 | public MFS.Date Date; 118 | 119 | public MFSEntry(string _name, string _ccode, string _gcode, ushort _dir = 0) 120 | { 121 | Attributes.CopyLimit = false; 122 | Attributes.Encode = false; 123 | Attributes.Hidden = false; 124 | Attributes.DisableRead = false; 125 | Attributes.DisableWrite = false; 126 | Name = _name; 127 | CompanyCode = _ccode; 128 | GameCode = _gcode; 129 | ParentDirectory = _dir; 130 | Renewal = 0; 131 | Date = new MFS.Date(); 132 | } 133 | 134 | public MFSEntry(byte[] Data, int Offset) 135 | { 136 | LoadEntry(Data, Offset); 137 | } 138 | 139 | public void LoadEntry(byte[] Data, int Offset) 140 | { 141 | Attributes.CopyLimit = (Util.ReadBEU16(Data, Offset) & 0x0200) != 0; 142 | Attributes.Encode = (Util.ReadBEU16(Data, Offset) & 0x0400) != 0; 143 | Attributes.Hidden = (Util.ReadBEU16(Data, Offset) & 0x0800) != 0; 144 | Attributes.DisableRead = (Util.ReadBEU16(Data, Offset) & 0x1000) != 0; 145 | Attributes.DisableWrite = (Util.ReadBEU16(Data, Offset) & 0x2000) != 0; 146 | 147 | ParentDirectory = Util.ReadBEU16(Data, Offset + 0x02); 148 | CompanyCode = Util.ReadStringN(Data, Offset + 0x04, 2); 149 | GameCode = Util.ReadStringN(Data, Offset + 0x06, 4); 150 | 151 | Name = Util.ReadStringN(Data, Offset + 0x10, 0x14); 152 | 153 | Renewal = Data[Offset + 0x2A]; 154 | Date = new MFS.Date(Util.ReadBEU32(Data, Offset + 0x2C)); 155 | } 156 | 157 | public byte[] SaveEntry() 158 | { 159 | byte[] temp = new byte[0x30]; 160 | 161 | //Attributes 162 | temp[0] = (byte)(0 | (Attributes.CopyLimit ? 0x02 : 0) | (Attributes.Encode ? 0x04 : 0) | (Attributes.Hidden ? 0x08 : 0) 163 | | (Attributes.DisableRead ? 0x10 : 0) | (Attributes.DisableWrite ? 0x20 : 0)); 164 | 165 | Util.WriteBEU16(ParentDirectory, temp, 2); 166 | Util.WriteStringN(CompanyCode, temp, 4, 2); 167 | Util.WriteStringN(GameCode, temp, 6, 4); 168 | Util.WriteStringN(Name, temp, 0x10, 0x14); 169 | 170 | temp[0x2A] = Renewal; 171 | Util.WriteBEU32(Date.Save(), temp, 0x2C); 172 | 173 | return temp; 174 | } 175 | 176 | public virtual string GetEntryName() 177 | { 178 | return Name; 179 | } 180 | } 181 | 182 | public class MFSDirectory : MFSEntry 183 | { 184 | public ushort DirectoryID; 185 | 186 | public MFSDirectory(byte[] Data, int Offset) : base(Data, Offset) 187 | { 188 | Load(Data, Offset); 189 | } 190 | 191 | public void Load(byte[] Data, int Offset) 192 | { 193 | DirectoryID = Util.ReadBEU16(Data, Offset + 0x0A); 194 | } 195 | 196 | public byte[] Save() 197 | { 198 | byte[] temp = SaveEntry(); 199 | 200 | Util.WriteBEU16(DirectoryID, temp, 0x0A); 201 | temp[0] |= 0x80; 202 | 203 | return temp; 204 | } 205 | } 206 | 207 | public class MFSFile : MFSEntry 208 | { 209 | public ushort FATEntry; 210 | public uint Size; 211 | 212 | public string Ext; 213 | public byte CopyNb; 214 | 215 | public MFSFile(string _name, string _ccode, string _gcode, string _ext, uint _size, ushort _dir = 0) : base(_name, _ccode, _gcode, _dir) 216 | { 217 | Ext = _ext; 218 | Size = _size; 219 | FATEntry = 0xFFFF; 220 | CopyNb = 0; 221 | } 222 | 223 | public MFSFile(byte[] Data, int Offset) : base(Data, Offset) 224 | { 225 | Load(Data, Offset); 226 | } 227 | 228 | public void Load(byte[] Data, int Offset) 229 | { 230 | FATEntry = Util.ReadBEU16(Data, Offset + 0x0A); 231 | Size = Util.ReadBEU32(Data, Offset + 0x0C); 232 | 233 | Ext = Util.ReadStringN(Data, Offset + 0x24, 5); 234 | CopyNb = Data[Offset + 0x29]; 235 | } 236 | 237 | public byte[] Save() 238 | { 239 | byte[] temp = SaveEntry(); 240 | 241 | Util.WriteBEU16(FATEntry, temp, 0x0A); 242 | Util.WriteBEU32(Size, temp, 0x0C); 243 | 244 | Util.WriteStringN(Ext, temp, 0x24, 5); 245 | temp[0x29] = CopyNb; 246 | temp[0] |= 0x40; 247 | return temp; 248 | } 249 | 250 | public override string GetEntryName() 251 | { 252 | return Name + "." + Ext; 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /mfs_library/MFS/MFSDisk.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Security.Cryptography; 8 | using System.Runtime.CompilerServices; 9 | using static mfs_library.MFS; 10 | 11 | namespace mfs_library 12 | { 13 | public class MFSDisk 14 | { 15 | public LeoDisk Disk; 16 | public MFSRAMVolume RAMVolume; 17 | 18 | public MFSDisk(string filepath) 19 | { 20 | //Load Disk 21 | Disk = new LeoDisk(filepath); 22 | LoadRAMVolume(); 23 | } 24 | 25 | public MFSDisk(LeoDisk disk) 26 | { 27 | //Reuse already loaded disk 28 | Disk = disk; 29 | LoadRAMVolume(); 30 | } 31 | 32 | void LoadRAMVolume() 33 | { 34 | //Load RAM Volume 35 | if (Disk.Format == LeoDisk.DiskFormat.Invalid) 36 | return; 37 | if (Disk.RAMFileSystem != LeoDisk.FileSystem.MFS) 38 | return; 39 | 40 | RAMVolume = new MFSRAMVolume(Disk.GetRAMAreaArray(), 0); 41 | } 42 | 43 | public void Save(string filepath) 44 | { 45 | //Save RAM Volume FAT Table into Disk data 46 | byte[] volume = RAMVolume.SaveToArray(); 47 | int offset = 0; 48 | for (int i = 0; i < 3; i++) 49 | { 50 | byte[] temp = new byte[Leo.LBAToByte(Disk.DiskType, Leo.RamStartLBA[Disk.DiskType] + i, 1)]; 51 | Array.Copy(volume, offset, temp, 0, temp.Length); 52 | Disk.WriteLBA(Leo.RamStartLBA[Disk.DiskType] + i, temp); 53 | offset += temp.Length; 54 | } 55 | 56 | offset = 0; 57 | for (int i = 3; i < 6; i++) 58 | { 59 | byte[] temp = new byte[Leo.LBAToByte(Disk.DiskType, Leo.RamStartLBA[Disk.DiskType] + i, 1)]; 60 | Array.Copy(volume, offset, temp, 0, temp.Length); 61 | Disk.WriteLBA(Leo.RamStartLBA[Disk.DiskType] + i, temp); 62 | offset += temp.Length; 63 | } 64 | 65 | //Save the Disk data 66 | Disk.Save(filepath); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /mfs_library/MFS/MFSUtil_Directory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace mfs_library 8 | { 9 | public static partial class MFSRAMUtil 10 | { 11 | public static MFSDirectory GetDirectoryFromPath(MFSDisk mfsDisk, string dirpath) 12 | { 13 | string[] path = dirpath.Split('/'); 14 | path[0] = "/"; 15 | 16 | return GetDirectoryFromList(mfsDisk, path.Take(path.Length - 1).ToArray(), GetAllDirectoriesFromDirID(mfsDisk, 0xFFFE)); 17 | } 18 | 19 | private static MFSDirectory GetDirectoryFromList(MFSDisk mfsDisk, string[] names, MFSDirectory[] list) 20 | { 21 | foreach (MFSDirectory dir in list) 22 | { 23 | if (dir.Name == names[0]) 24 | { 25 | if (names.Length > 1) 26 | return GetDirectoryFromList(mfsDisk, names.Skip(1).ToArray(), GetAllDirectoriesFromDirID(mfsDisk, dir.DirectoryID)); 27 | else 28 | return dir; 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | public static MFSDirectory[] GetAllDirectoriesFromDirID(MFSDisk mfsDisk, ushort id) 35 | { 36 | List list = new List(); 37 | 38 | foreach (MFSEntry entry in mfsDisk.RAMVolume.Entries) 39 | { 40 | if (entry.GetType() == typeof(MFSDirectory) && ((MFSDirectory)entry).ParentDirectory == id) 41 | { 42 | list.Add((MFSDirectory)entry); 43 | } 44 | } 45 | 46 | return list.ToArray(); 47 | } 48 | 49 | public static MFSDirectory GetDirectoryFromID(MFSDisk mfsDisk, ushort id) 50 | { 51 | foreach (MFSEntry entry in mfsDisk.RAMVolume.Entries) 52 | { 53 | if (entry.GetType() == typeof(MFSDirectory) && ((MFSDirectory)entry).DirectoryID == id) 54 | { 55 | return (MFSDirectory)entry; 56 | } 57 | } 58 | return null; 59 | } 60 | 61 | // As there can be multiple directories with the same name, it is preferable to input a parent Directory ID. 62 | public static bool CheckIfDirectoryAlreadyExists(MFSDisk mfsDisk, string _name, ushort _dir = 0xFFFF) 63 | { 64 | foreach (MFSDirectory entry in GetAllDirectoriesFromDirID(mfsDisk, _dir)) 65 | { 66 | if (entry.Name.Equals(_name)) 67 | { 68 | return true; 69 | } 70 | } 71 | return false; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /mfs_library/MFS/MFSUtil_File.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace mfs_library 9 | { 10 | public static partial class MFSRAMUtil 11 | { 12 | // Additional Methods 13 | public static byte[] ReadFile(MFSDisk mfsDisk, string filepath) 14 | { 15 | MFSFile file = GetFileFromPath(mfsDisk, filepath); 16 | if (file != null) 17 | return ReadFile(mfsDisk, file); 18 | else 19 | return null; 20 | } 21 | 22 | public static byte[] ReadFile(MFSDisk mfsDisk, MFSFile file) 23 | { 24 | byte[] filedata = new byte[file.Size]; 25 | 26 | //Add FAT Entries and Copy to data to blocks 27 | ushort nextblock = file.FATEntry; 28 | uint offset = 0; 29 | uint size = file.Size; 30 | 31 | //Recursively copy blocks 32 | do 33 | { 34 | byte[] blockdata = mfsDisk.Disk.ReadLBA(Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType] + nextblock); 35 | int blocksize = blockdata.Length; 36 | Array.Copy(blockdata, 0, filedata, offset, Math.Min(blocksize, size)); 37 | offset += (uint)Math.Min(blocksize, size); 38 | size -= (uint)Math.Min(blocksize, size); 39 | 40 | nextblock = mfsDisk.RAMVolume.FAT[nextblock]; 41 | } 42 | while (nextblock != (ushort)MFS.FAT.LastFileBlock); 43 | 44 | return filedata; 45 | } 46 | 47 | public static bool WriteFile(MFSDisk mfsDisk, byte[] filedata, string filepath) 48 | { 49 | MFSDirectory _dir = GetDirectoryFromPath(mfsDisk, filepath); 50 | 51 | return WriteFile(mfsDisk, filedata, Path.GetFileName(filepath), _dir.DirectoryID); 52 | } 53 | 54 | public static bool WriteFile(MFSDisk mfsDisk, byte[] filedata, string name, ushort dir = 0) 55 | { 56 | string _name = Path.GetFileNameWithoutExtension(name); 57 | string _ext = Path.GetExtension(name); 58 | 59 | if (_ext.StartsWith(".")) 60 | _ext = _ext.Substring(1); 61 | 62 | MFSFile file = new MFSFile(_name, mfsDisk.RAMVolume.Entries[0].CompanyCode, mfsDisk.RAMVolume.Entries[0].GameCode, _ext, (uint)filedata.Length, dir); 63 | return WriteFile(mfsDisk, filedata, file); 64 | } 65 | 66 | public static bool WriteFile(MFSDisk mfsDisk, byte[] filedata, MFSFile file) 67 | { 68 | if (CheckIfFileAlreadyExists(mfsDisk, file.Name, file.Ext, file.ParentDirectory)) 69 | return false; 70 | if (GetFreeSpaceSize(mfsDisk) < filedata.Length) 71 | return false; 72 | 73 | int FATentry = -1; 74 | int lastFATentry = -1; 75 | int offset = 0; 76 | 77 | //Write free data on every free block from the start 78 | for (int i = 6; i < Leo.SIZE_LBA - Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType]; i++) 79 | { 80 | if (mfsDisk.RAMVolume.FAT[i] == (ushort)MFS.FAT.Unused) 81 | { 82 | if (FATentry == -1) 83 | { 84 | FATentry = i; 85 | file.FATEntry = (ushort)i; 86 | } 87 | 88 | //Write File Data 89 | byte[] blockdata = new byte[Leo.LBAToByte(mfsDisk.RAMVolume.DiskType, Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType] + i, 1)]; 90 | Array.Copy(filedata, offset, blockdata, 0, Math.Min(blockdata.Length, filedata.Length - offset)); 91 | mfsDisk.Disk.WriteLBA(Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType] + i, blockdata); 92 | 93 | offset += Math.Min(blockdata.Length, filedata.Length - offset); 94 | 95 | //Change FAT entry 96 | if (lastFATentry != -1) 97 | { 98 | mfsDisk.RAMVolume.FAT[lastFATentry] = (ushort)i; 99 | } 100 | if (offset == filedata.Length) 101 | { 102 | mfsDisk.RAMVolume.FAT[i] = (ushort)MFS.FAT.LastFileBlock; 103 | 104 | mfsDisk.RAMVolume.Entries.Add(file); 105 | return true; 106 | } 107 | lastFATentry = i; 108 | } 109 | } 110 | return false; 111 | } 112 | 113 | public static bool DeleteFile(MFSDisk mfsDisk, string filepath) 114 | { 115 | MFSFile file = GetFileFromPath(mfsDisk, filepath); 116 | if (file != null) 117 | return DeleteFile(mfsDisk, file); 118 | else 119 | return false; 120 | } 121 | 122 | public static bool DeleteFile(MFSDisk mfsDisk, MFSFile file) 123 | { 124 | //Delete FAT Entries recursively 125 | ushort nextblock = file.FATEntry; 126 | ushort lastblock = 0; 127 | do 128 | { 129 | lastblock = nextblock; 130 | nextblock = mfsDisk.RAMVolume.FAT[nextblock]; 131 | mfsDisk.RAMVolume.FAT[lastblock] = 0; 132 | } 133 | while (nextblock != (ushort)MFS.FAT.LastFileBlock); 134 | 135 | //Delete File Data 136 | if (mfsDisk.RAMVolume.Entries.Remove(file)) 137 | { 138 | Console.WriteLine("found"); 139 | } 140 | 141 | return true; 142 | } 143 | 144 | public static bool MoveFile(MFSDisk mfsDisk, string filepathin, string filepathout) 145 | { 146 | MFSFile file = GetFileFromPath(mfsDisk, filepathin); 147 | if (file != null) 148 | { 149 | if (filepathout.StartsWith("/")) 150 | { 151 | MFSDirectory dir = GetDirectoryFromPath(mfsDisk, filepathout); 152 | if (dir != null) 153 | file.ParentDirectory = dir.DirectoryID; 154 | else 155 | return false; 156 | } 157 | if (!filepathout.EndsWith("/")) 158 | { 159 | file.Name = Path.GetFileNameWithoutExtension(filepathout); 160 | string newext = Path.GetExtension(filepathout); 161 | if (newext != "") 162 | newext = newext.Substring(1); 163 | file.Ext = newext; 164 | } 165 | return true; 166 | } 167 | else 168 | return false; 169 | } 170 | 171 | //Utilities 172 | public static MFSFile GetFileFromPath(MFSDisk mfsDisk, string filepath) 173 | { 174 | MFSDirectory dir = GetDirectoryFromPath(mfsDisk, filepath); 175 | string[] path = filepath.Split('/'); 176 | path[0] = "/"; 177 | 178 | string filename = Path.GetFileNameWithoutExtension(filepath); 179 | string ext = Path.GetExtension(filepath); 180 | if (ext.StartsWith(".")) 181 | ext = ext.Substring(1); 182 | 183 | foreach (MFSFile file in GetAllFilesFromDirID(mfsDisk, dir.DirectoryID)) 184 | { 185 | if (file.Name.Equals(filename) && file.Ext.Equals(ext)) 186 | { 187 | return file; 188 | } 189 | } 190 | 191 | return null; 192 | } 193 | 194 | public static MFSFile[] GetAllFilesFromDirID(MFSDisk mfsDisk, ushort id) 195 | { 196 | List list = new List(); 197 | 198 | foreach (MFSEntry entry in mfsDisk.RAMVolume.Entries) 199 | { 200 | if (entry.GetType() == typeof(MFSFile)) 201 | { 202 | if (id == 0xFFFF || ((MFSFile)entry).ParentDirectory == id) 203 | list.Add((MFSFile)entry); 204 | } 205 | } 206 | 207 | return list.ToArray(); 208 | } 209 | 210 | public static string GetFullPath(MFSDisk mfsDisk, MFSEntry file) 211 | { 212 | string temp = GetParentDirectoryPath(mfsDisk, file) + file.Name; 213 | 214 | if (file.GetType() == typeof(MFSFile) && ((MFSFile)file).Ext != "") 215 | temp += "." + ((MFSFile)file).Ext; 216 | else if (file.GetType() == typeof(MFSDirectory)) 217 | temp += "/"; 218 | 219 | return temp; 220 | } 221 | 222 | public static string GetParentDirectoryPath(MFSDisk mfsDisk, MFSEntry file) 223 | { 224 | string temp = ""; 225 | ushort dir_id = 0; 226 | MFSDirectory dir = GetDirectoryFromID(mfsDisk, file.ParentDirectory); 227 | while (dir != null) 228 | { 229 | dir_id = dir.ParentDirectory; 230 | temp = dir.Name + (dir_id != 0xFFFE ? "/" : "") + temp; 231 | dir = GetDirectoryFromID(mfsDisk, dir_id); 232 | } 233 | 234 | return temp; 235 | } 236 | 237 | // As there can be multiple files with the same name, it is preferable to input a parent Directory ID. 238 | public static bool CheckIfFileAlreadyExists(MFSDisk mfsDisk, string _filename, ushort _dir = 0xFFFF) 239 | { 240 | string _name = Path.GetFileNameWithoutExtension(_filename); 241 | string _ext = Path.GetExtension(_filename); 242 | 243 | if (_ext.StartsWith(".")) 244 | _ext = _ext.Substring(1); 245 | 246 | return CheckIfFileAlreadyExists(mfsDisk, _name, _ext, _dir); 247 | } 248 | 249 | public static bool CheckIfFileAlreadyExists(MFSDisk mfsDisk, string _name, string _ext, ushort _dir = 0xFFFF) 250 | { 251 | foreach (MFSFile file in GetAllFilesFromDirID(mfsDisk, _dir)) 252 | { 253 | if (file.Name.Equals(_name) && file.Ext.Equals(_ext)) 254 | { 255 | return true; 256 | } 257 | } 258 | return false; 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /mfs_library/MFS/MFSUtil_Other.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace mfs_library 8 | { 9 | public static partial class MFSRAMUtil 10 | { 11 | public static int GetTotalUsedSize(MFSDisk mfsDisk) 12 | { 13 | int totalsize = 0; 14 | for (int i = 6; i < Leo.SIZE_LBA - Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType]; i++) 15 | { 16 | switch (mfsDisk.RAMVolume.FAT[i]) 17 | { 18 | case (ushort)MFS.FAT.Unused: 19 | case (ushort)MFS.FAT.Prohibited: 20 | case (ushort)MFS.FAT.DontManage: 21 | break; 22 | default: 23 | totalsize += Leo.LBAToByte(mfsDisk.RAMVolume.DiskType, Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType] + i, 1); 24 | break; 25 | } 26 | } 27 | return totalsize; 28 | } 29 | 30 | public static int GetFreeSpaceSize(MFSDisk mfsDisk) 31 | { 32 | int unused = Leo.RamSize[mfsDisk.RAMVolume.DiskType] - GetTotalUsedSize(mfsDisk) - Leo.LBAToByte(mfsDisk.RAMVolume.DiskType, Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType], 6); 33 | 34 | return unused; 35 | } 36 | 37 | public static int GetCapacitySize(MFSDisk mfsDisk) 38 | { 39 | int totalsize = 0; 40 | for (int i = 6; i < Leo.SIZE_LBA - Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType]; i++) 41 | { 42 | switch (mfsDisk.RAMVolume.FAT[i]) 43 | { 44 | case (ushort)MFS.FAT.Prohibited: 45 | case (ushort)MFS.FAT.DontManage: 46 | break; 47 | default: 48 | totalsize += Leo.LBAToByte(mfsDisk.RAMVolume.DiskType, Leo.RamStartLBA[mfsDisk.RAMVolume.DiskType] + i, 1); 49 | break; 50 | } 51 | } 52 | return totalsize; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mfs_library/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Les informations générales relatives à un assembly dépendent de 6 | // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations 7 | // associées à un assembly. 8 | [assembly: AssemblyTitle("mfs_library")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("mfs_library")] 13 | [assembly: AssemblyCopyright("Copyright © LuigiBlood 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly 18 | // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de 19 | // COM, affectez la valeur true à l'attribut ComVisible sur ce type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM 23 | [assembly: Guid("8d1cf3e5-ba0f-4790-ba0a-c52b79302da3")] 24 | 25 | // Les informations de version pour un assembly se composent des quatre valeurs suivantes : 26 | // 27 | // Version principale 28 | // Version secondaire 29 | // Numéro de build 30 | // Révision 31 | // 32 | // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut 33 | // en utilisant '*', comme indiqué ci-dessous : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.2.0.0")] 36 | [assembly: AssemblyFileVersion("1.2.0.0")] 37 | -------------------------------------------------------------------------------- /mfs_library/Util/SJISUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace mfs_library 9 | { 10 | public static class SJISUtil 11 | { 12 | static Dictionary leoMapping = new Dictionary(); 13 | static bool isMappingReady = false; 14 | 15 | static void PrepareMapping() 16 | { 17 | if (isMappingReady) 18 | return; 19 | 20 | leoMapping = new Dictionary(); 21 | leoMapping.Add(0x86A3, '\u2660'); //Spade 22 | leoMapping.Add(0x86A4, '\u2663'); //Club 23 | leoMapping.Add(0x86A5, '\u2665'); //Heart 24 | leoMapping.Add(0x86A6, '\u2666'); //Diamond 25 | 26 | isMappingReady = true; 27 | } 28 | 29 | public static byte[] EncodeStringToSJIS(string str) 30 | { 31 | PrepareMapping(); 32 | 33 | Encoding encode = Encoding.GetEncoding(932, new EncoderExceptionFallback(), new CustomDecoder()); 34 | List output = new List(); 35 | 36 | foreach (char c in str) 37 | { 38 | byte[] byt = { }; 39 | 40 | try 41 | { 42 | byt = encode.GetBytes(c.ToString()); 43 | } 44 | catch (EncoderFallbackException e) 45 | { 46 | //Any invalid chars will dealt with here 47 | ushort[] keys = leoMapping.Keys.ToArray(); //SJIS 48 | char[] values = leoMapping.Values.ToArray(); //Unicode 49 | 50 | Debug.Assert(keys.Length == values.Length); 51 | 52 | //Reverse Search 53 | for (int i = 0; i < keys.Length; i++) 54 | { 55 | if (values[i] == e.CharUnknown) 56 | { 57 | byt = BitConverter.GetBytes(keys[i]).Reverse().ToArray(); 58 | break; 59 | } 60 | } 61 | } 62 | 63 | foreach (byte b in byt) 64 | output.Add(b); 65 | } 66 | 67 | return output.ToArray(); 68 | } 69 | 70 | //Decoder 71 | public class CustomDecoder : DecoderFallback 72 | { 73 | public string DefaultString; 74 | internal Dictionary mapping; 75 | 76 | public CustomDecoder() : this("*") 77 | { 78 | } 79 | 80 | public CustomDecoder(string defaultString) 81 | { 82 | this.DefaultString = defaultString; 83 | 84 | // Create table of mappings 85 | PrepareMapping(); 86 | mapping = leoMapping; 87 | } 88 | 89 | public override DecoderFallbackBuffer CreateFallbackBuffer() 90 | { 91 | return new CustomDecoderFallbackBuffer(this); 92 | } 93 | 94 | public override int MaxCharCount 95 | { 96 | get { return 2; } 97 | } 98 | } 99 | 100 | public class CustomDecoderFallbackBuffer : DecoderFallbackBuffer 101 | { 102 | int count = -1; // Number of characters to return 103 | int index = -1; // Index of character to return 104 | CustomDecoder fb; 105 | string charsToReturn; 106 | 107 | public CustomDecoderFallbackBuffer(CustomDecoder fallback) 108 | { 109 | this.fb = fallback; 110 | } 111 | 112 | public override bool Fallback(byte[] bytesUnknown, int index) 113 | { 114 | // Return false if there are already characters to map. 115 | if (count >= 1) return false; 116 | 117 | // Determine number of characters to return. 118 | charsToReturn = String.Empty; 119 | 120 | if (bytesUnknown.Length == 2) 121 | { 122 | ushort key = (ushort)((bytesUnknown[0] << 8) + bytesUnknown[1]); 123 | if (fb.mapping.ContainsKey(key)) 124 | { 125 | charsToReturn = Convert.ToString(fb.mapping[key]); 126 | count = 1; 127 | } 128 | else 129 | { 130 | // Return default. 131 | charsToReturn = fb.DefaultString; 132 | count = 1; 133 | } 134 | this.index = charsToReturn.Length - 1; 135 | } 136 | else 137 | { 138 | //Only full width 139 | charsToReturn = fb.DefaultString; 140 | count = 1; 141 | } 142 | return true; 143 | } 144 | 145 | public override char GetNextChar() 146 | { 147 | // We'll return a character if possible, so subtract from the count of chars to return. 148 | count--; 149 | // If count is less than zero, we've returned all characters. 150 | if (count < 0) 151 | return '\u0000'; 152 | 153 | this.index--; 154 | return charsToReturn[this.index + 1]; 155 | } 156 | 157 | public override bool MovePrevious() 158 | { 159 | // Original: if count >= -1 and pos >= 0 160 | if (count >= -1) 161 | { 162 | count++; 163 | return true; 164 | } 165 | else 166 | { 167 | return false; 168 | } 169 | } 170 | 171 | public override int Remaining 172 | { 173 | get { return count < 0 ? 0 : count; } 174 | } 175 | 176 | public override void Reset() 177 | { 178 | count = -1; 179 | index = -1; 180 | } 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /mfs_library/Util/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace mfs_library 8 | { 9 | public static class Util 10 | { 11 | //Read Methods 12 | public static uint ReadBEU32(byte[] Data, int Offset) 13 | { 14 | return (uint)((Data[Offset + 0] << 24) | (Data[Offset + 1] << 16) | (Data[Offset + 2] << 8) | (Data[Offset + 3] << 0)); 15 | } 16 | 17 | public static ushort ReadBEU16(byte[] Data, int Offset) 18 | { 19 | return (ushort)((Data[Offset + 0] << 8) | (Data[Offset + 1] << 0)); 20 | } 21 | 22 | public static string ReadStringN(byte[] Data, int Offset, int Size) 23 | { 24 | return Encoding.GetEncoding(932, new EncoderReplacementFallback(), new SJISUtil.CustomDecoder()).GetString(Data, Offset, Size).TrimEnd('\x00'); 25 | } 26 | 27 | //Write Methods 28 | public static void WriteBEU32(uint Input, byte[] Data, int Offset) 29 | { 30 | Data[Offset + 0] = (byte)((Input >> 24) & 0xFF); 31 | Data[Offset + 1] = (byte)((Input >> 16) & 0xFF); 32 | Data[Offset + 2] = (byte)((Input >> 8) & 0xFF); 33 | Data[Offset + 3] = (byte)((Input >> 0) & 0xFF); 34 | } 35 | 36 | public static void WriteBEU16(ushort Input, byte[] Data, int Offset) 37 | { 38 | Data[Offset + 0] = (byte)((Input >> 8) & 0xFF); 39 | Data[Offset + 1] = (byte)((Input >> 0) & 0xFF); 40 | } 41 | 42 | public static void WriteStringN(string Input, byte[] Data, int Offset, int Size) 43 | { 44 | byte[] temp = new byte[Size]; 45 | for (int i = 0; i < Size; i++) 46 | temp[i] = 0; 47 | 48 | byte[] text = SJISUtil.EncodeStringToSJIS(Input); 49 | Array.Copy(text, temp, Math.Min(text.Length, Size)); 50 | 51 | Array.Copy(temp, 0, Data, Offset, Size); 52 | 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mfs_library/mfs_library.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {8D1CF3E5-BA0F-4790-BA0A-C52B79302DA3} 8 | Library 9 | Properties 10 | mfs_library 11 | mfs_library 12 | v4.6.1 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /mfs_manager.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32922.545 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mfs_manager", "mfs_manager\mfs_manager.csproj", "{BD3CC0E8-3EA7-4A64-AD41-2D3747F93596}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mfs_gui", "mfs_gui\mfs_gui.csproj", "{48EC8983-43D6-4686-B33D-FD46FEC8CE8D}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mfs_library", "mfs_library\mfs_library.csproj", "{8D1CF3E5-BA0F-4790-BA0A-C52B79302DA3}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "save_manager", "save_manager\save_manager.csproj", "{DBC89E4D-7A97-47FB-AA34-2A7613A3B17C}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {BD3CC0E8-3EA7-4A64-AD41-2D3747F93596}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {BD3CC0E8-3EA7-4A64-AD41-2D3747F93596}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {BD3CC0E8-3EA7-4A64-AD41-2D3747F93596}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {BD3CC0E8-3EA7-4A64-AD41-2D3747F93596}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {48EC8983-43D6-4686-B33D-FD46FEC8CE8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {48EC8983-43D6-4686-B33D-FD46FEC8CE8D}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {48EC8983-43D6-4686-B33D-FD46FEC8CE8D}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {48EC8983-43D6-4686-B33D-FD46FEC8CE8D}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {8D1CF3E5-BA0F-4790-BA0A-C52B79302DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {8D1CF3E5-BA0F-4790-BA0A-C52B79302DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {8D1CF3E5-BA0F-4790-BA0A-C52B79302DA3}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {8D1CF3E5-BA0F-4790-BA0A-C52B79302DA3}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {DBC89E4D-7A97-47FB-AA34-2A7613A3B17C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {DBC89E4D-7A97-47FB-AA34-2A7613A3B17C}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {DBC89E4D-7A97-47FB-AA34-2A7613A3B17C}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {DBC89E4D-7A97-47FB-AA34-2A7613A3B17C}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {83855A69-2379-4A09-88BF-C9EAA8D939A7} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /mfs_manager/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /mfs_manager/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Text.RegularExpressions; 8 | using mfs_library; 9 | 10 | namespace mfs_manager 11 | { 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | Console.OutputEncoding = Encoding.Unicode; 17 | Console.WriteLine("64DD MFS Manager - by LuigiBlood"); 18 | if (args.Length < 2) 19 | { 20 | Console.WriteLine("Usage: mfs_manager "); 21 | Console.WriteLine(" can be the following:"); 22 | Console.WriteLine(" -d : List all directories with their IDs"); 23 | Console.WriteLine(" -f : List all files"); 24 | Console.WriteLine(" -e : Extract all files"); 25 | Console.WriteLine(" -e : Extract specified file (must start with \"/\")"); 26 | Console.WriteLine(" -i : Insert into (Path must start AND end with \"/\")"); 27 | Console.WriteLine(" -r : Delete (Path must start with \"/\")"); 28 | Console.WriteLine(" -m : Move to AND/OR rename if (Path must start with \"/\")"); 29 | } 30 | else if (args.Length >= 2) 31 | { 32 | MFSDisk mfsDisk = new MFSDisk(args[0]); 33 | string savefilename = Path.ChangeExtension(args[0], ".new" + Path.GetExtension(args[0])); 34 | 35 | if (mfsDisk.Disk.Format == LeoDisk.DiskFormat.Invalid) 36 | { 37 | Console.WriteLine("Disk file is invalid."); 38 | return; 39 | } 40 | 41 | if (mfsDisk.Disk.RAMFileSystem != LeoDisk.FileSystem.MFS) 42 | { 43 | Console.WriteLine("Disk file is not using MFS."); 44 | return; 45 | } 46 | 47 | if (args[1].Equals("-d")) 48 | { 49 | //Directory List 50 | Console.WriteLine("Directory List:"); 51 | foreach (MFSEntry entry in mfsDisk.RAMVolume.Entries) 52 | { 53 | if (entry.GetType() == typeof(MFSDirectory)) 54 | { 55 | Console.WriteLine(((MFSDirectory)entry).DirectoryID + ": " + MFSRAMUtil.GetFullPath(mfsDisk, entry));// + " (" + ((MFSDirectory)entry).CheckShiftJIS() + ")"); 56 | } 57 | } 58 | } 59 | else if (args[1].Equals("-f")) 60 | { 61 | //File List 62 | Console.WriteLine("File List:"); 63 | foreach (MFSEntry entry in mfsDisk.RAMVolume.Entries) 64 | { 65 | if (entry.GetType() == typeof(MFSFile)) 66 | { 67 | Console.WriteLine(MFSRAMUtil.GetFullPath(mfsDisk, entry)); 68 | } 69 | } 70 | } 71 | else if (args[1].Equals("-e")) 72 | { 73 | //Extract 74 | if (args.Length > 2) 75 | { 76 | Console.WriteLine("Extract " + args[2]); 77 | byte[] data = MFSRAMUtil.ReadFile(mfsDisk, args[2]); 78 | if (data != null) 79 | { 80 | FileStream fileExt = new FileStream(".\\extract\\" + Path.GetFileName(args[2]), FileMode.Create); 81 | fileExt.Write(data, 0, data.Length); 82 | fileExt.Close(); 83 | Console.WriteLine("Done"); 84 | } 85 | else 86 | { 87 | Console.WriteLine("Error"); 88 | } 89 | } 90 | else 91 | { 92 | foreach (MFSEntry entry in mfsDisk.RAMVolume.Entries) 93 | { 94 | if (entry.GetType() == typeof(MFSFile)) 95 | { 96 | Console.WriteLine("Extract " + MFSRAMUtil.GetFullPath(mfsDisk, entry)); 97 | byte[] data = MFSRAMUtil.ReadFile(mfsDisk, (MFSFile)entry); 98 | FileStream fileExt = new FileStream(".\\extract\\" + entry.Name + (((MFSFile)entry).Ext != "" ? "." : "") + ((MFSFile)entry).Ext, FileMode.Create); 99 | fileExt.Write(data, 0, data.Length); 100 | fileExt.Close(); 101 | } 102 | } 103 | } 104 | } 105 | else if (args[1].Equals("-i") && args.Length > 3) 106 | { 107 | //Insert File 108 | FileStream testAdd = new FileStream(args[2], FileMode.Open); 109 | byte[] testArray = new byte[testAdd.Length]; 110 | testAdd.Read(testArray, 0, (int)testAdd.Length); 111 | testAdd.Close(); 112 | bool file = false; 113 | if (args[3].StartsWith("/") && args[3].EndsWith("/")) 114 | { 115 | file = MFSRAMUtil.WriteFile(mfsDisk, testArray, args[3] + Path.GetFileName(args[2])); 116 | } 117 | else if (Regex.IsMatch(args[3], "^\\d+$")) 118 | { 119 | file = MFSRAMUtil.WriteFile(mfsDisk, testArray, Path.GetFileName(args[2]), ushort.Parse(args[3])); 120 | } 121 | 122 | if (!file) 123 | Console.WriteLine("Could not insert file"); 124 | else 125 | Console.WriteLine("File " + Path.GetFileName(args[2]) + " inserted successfully"); 126 | mfsDisk.Save(savefilename); 127 | } 128 | else if (args[1].Equals("-r")) 129 | { 130 | //Delete (-r emove) 131 | if (args.Length > 2) 132 | { 133 | Console.WriteLine("Delete " + args[2]); 134 | if (MFSRAMUtil.DeleteFile(mfsDisk, args[2])) 135 | { 136 | Console.WriteLine("Done"); 137 | mfsDisk.Save(savefilename); 138 | } 139 | else 140 | { 141 | Console.WriteLine("Error"); 142 | } 143 | } 144 | } 145 | else if (args[1].Equals("-m") && args.Length > 3) 146 | { 147 | //Insert File 148 | if (MFSRAMUtil.MoveFile(mfsDisk, args[2], args[3])) 149 | { 150 | Console.WriteLine("Done"); 151 | mfsDisk.Save(savefilename); 152 | } 153 | else 154 | Console.WriteLine("File was not moved/renamed successfully"); 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /mfs_manager/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Les informations générales relatives à un assembly dépendent de 6 | // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations 7 | // associées à un assembly. 8 | [assembly: AssemblyTitle("mfs_manager")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("mfs_manager")] 13 | [assembly: AssemblyCopyright("Copyright © LuigiBlood 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly 18 | // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de 19 | // COM, affectez la valeur true à l'attribut ComVisible sur ce type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM 23 | [assembly: Guid("bd3cc0e8-3ea7-4a64-ad41-2d3747f93596")] 24 | 25 | // Les informations de version pour un assembly se composent des quatre valeurs suivantes : 26 | // 27 | // Version principale 28 | // Version secondaire 29 | // Numéro de build 30 | // Révision 31 | // 32 | // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut 33 | // en utilisant '*', comme indiqué ci-dessous : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.3.0.0")] 36 | [assembly: AssemblyFileVersion("1.3.0.0")] 37 | -------------------------------------------------------------------------------- /mfs_manager/mfs_manager.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BD3CC0E8-3EA7-4A64-AD41-2D3747F93596} 8 | Exe 9 | mfs_manager 10 | mfs_manager 11 | v4.6.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {8d1cf3e5-ba0f-4790-ba0a-c52b79302da3} 59 | mfs_library 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /save_manager/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /save_manager/MainForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace save_manager 2 | { 3 | partial class MainForm 4 | { 5 | /// 6 | /// Variable nécessaire au concepteur. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Nettoyage des ressources utilisées. 12 | /// 13 | /// true si les ressources managées doivent être supprimées ; sinon, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Code généré par le Concepteur Windows Form 24 | 25 | /// 26 | /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas 27 | /// le contenu de cette méthode avec l'éditeur de code. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.label1 = new System.Windows.Forms.Label(); 32 | this.textBoxBaseFile = new System.Windows.Forms.TextBox(); 33 | this.buttonBrowse = new System.Windows.Forms.Button(); 34 | this.buttonExport = new System.Windows.Forms.Button(); 35 | this.buttonImport = new System.Windows.Forms.Button(); 36 | this.SuspendLayout(); 37 | // 38 | // label1 39 | // 40 | this.label1.AutoSize = true; 41 | this.label1.Location = new System.Drawing.Point(12, 9); 42 | this.label1.Name = "label1"; 43 | this.label1.Size = new System.Drawing.Size(143, 13); 44 | this.label1.TabIndex = 0; 45 | this.label1.Text = "Base 64DD Disk / RAM File:"; 46 | // 47 | // textBoxBaseFile 48 | // 49 | this.textBoxBaseFile.Location = new System.Drawing.Point(15, 25); 50 | this.textBoxBaseFile.Name = "textBoxBaseFile"; 51 | this.textBoxBaseFile.ReadOnly = true; 52 | this.textBoxBaseFile.Size = new System.Drawing.Size(236, 20); 53 | this.textBoxBaseFile.TabIndex = 1; 54 | // 55 | // buttonBrowse 56 | // 57 | this.buttonBrowse.Location = new System.Drawing.Point(257, 24); 58 | this.buttonBrowse.Name = "buttonBrowse"; 59 | this.buttonBrowse.Size = new System.Drawing.Size(75, 22); 60 | this.buttonBrowse.TabIndex = 2; 61 | this.buttonBrowse.Text = "Browse..."; 62 | this.buttonBrowse.UseVisualStyleBackColor = true; 63 | this.buttonBrowse.Click += new System.EventHandler(this.buttonBrowse_Click); 64 | // 65 | // buttonExport 66 | // 67 | this.buttonExport.Location = new System.Drawing.Point(15, 91); 68 | this.buttonExport.Name = "buttonExport"; 69 | this.buttonExport.Size = new System.Drawing.Size(93, 23); 70 | this.buttonExport.TabIndex = 3; 71 | this.buttonExport.Text = "Export RAM..."; 72 | this.buttonExport.UseVisualStyleBackColor = true; 73 | this.buttonExport.Click += new System.EventHandler(this.buttonExport_Click); 74 | // 75 | // buttonImport 76 | // 77 | this.buttonImport.Location = new System.Drawing.Point(239, 91); 78 | this.buttonImport.Name = "buttonImport"; 79 | this.buttonImport.Size = new System.Drawing.Size(93, 23); 80 | this.buttonImport.TabIndex = 4; 81 | this.buttonImport.Text = "Import RAM..."; 82 | this.buttonImport.UseVisualStyleBackColor = true; 83 | this.buttonImport.Click += new System.EventHandler(this.buttonImport_Click); 84 | // 85 | // MainForm 86 | // 87 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 88 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 89 | this.ClientSize = new System.Drawing.Size(343, 125); 90 | this.Controls.Add(this.buttonImport); 91 | this.Controls.Add(this.buttonExport); 92 | this.Controls.Add(this.buttonBrowse); 93 | this.Controls.Add(this.textBoxBaseFile); 94 | this.Controls.Add(this.label1); 95 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 96 | this.Name = "MainForm"; 97 | this.Text = "64DD Save Manager"; 98 | this.ResumeLayout(false); 99 | this.PerformLayout(); 100 | 101 | } 102 | 103 | #endregion 104 | 105 | private System.Windows.Forms.Label label1; 106 | private System.Windows.Forms.TextBox textBoxBaseFile; 107 | private System.Windows.Forms.Button buttonBrowse; 108 | private System.Windows.Forms.Button buttonExport; 109 | private System.Windows.Forms.Button buttonImport; 110 | } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /save_manager/MainForm.cs: -------------------------------------------------------------------------------- 1 | using mfs_library; 2 | using System; 3 | using System.IO; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Data; 7 | using System.Drawing; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | using System.Linq.Expressions; 13 | 14 | namespace save_manager 15 | { 16 | public partial class MainForm : Form 17 | { 18 | public MainForm() 19 | { 20 | InitializeComponent(); 21 | } 22 | 23 | private void buttonBrowse_Click(object sender, EventArgs e) 24 | { 25 | OpenFileDialog ofs = new OpenFileDialog(); 26 | ofs.Title = "Open Base 64DD Disk / RAM File..."; 27 | ofs.Multiselect = false; 28 | ofs.Filter = "All Supported Files (*.ndd, *.ndr, *.ram, *.n64, *.z64, *.disk)|*.ndd;*.ndr;*.ram;*.n64;*.z64;*.disk|64DD RAM Area Image (*.ram)|*.ram|64DD Disk Image (*.ndd, *.ndr, *.disk)|*.ndd;*.ndr;*.disk|N64 Cartridge Port Image (*.n64, *.z64)|*.n64;*.z64|All files|*.*"; 29 | if (ofs.ShowDialog() == DialogResult.OK) 30 | { 31 | var error = Program.Load(ofs.FileName); 32 | if (error == Program.DiskError.NotDiskFile) 33 | { 34 | MessageBox.Show("This file is not a disk file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 35 | return; 36 | } 37 | else if (error == Program.DiskError.NoRAMArea) 38 | { 39 | MessageBox.Show("This disk does not contain a RAM Area.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 40 | return; 41 | } 42 | else if (error == Program.DiskError.FileNotExist) 43 | { 44 | MessageBox.Show("This file does not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 45 | return; 46 | } 47 | textBoxBaseFile.Text = Program.Disk.Filename; 48 | } 49 | } 50 | 51 | private void buttonExport_Click(object sender, EventArgs e) 52 | { 53 | if (Program.Disk == null) 54 | { 55 | MessageBox.Show("Please load a disk file first.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 56 | return; 57 | } 58 | 59 | SaveFileDialog sfd = new SaveFileDialog(); 60 | sfd.Title = "Export RAM File..."; 61 | sfd.Filter = "64DD RAM Area Image (*.ram)|*.ram"; 62 | if (sfd.ShowDialog() == DialogResult.OK) 63 | { 64 | bool saveError = false; 65 | try 66 | { 67 | Program.ExportRAM(sfd.FileName); 68 | } 69 | catch 70 | { 71 | saveError = true; 72 | } 73 | 74 | if (!saveError) 75 | { 76 | MessageBox.Show("File has been saved.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 77 | } 78 | } 79 | } 80 | 81 | private void buttonImport_Click(object sender, EventArgs e) 82 | { 83 | if (Program.Disk == null) 84 | { 85 | MessageBox.Show("Please load a disk file first.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 86 | return; 87 | } 88 | 89 | OpenFileDialog ofs = new OpenFileDialog(); 90 | ofs.Title = "Import 64DD Disk / RAM File..."; 91 | ofs.Multiselect = false; 92 | ofs.Filter = "All Supported Files (*.ndd, *.ndr, *.ram, *.n64, *.z64, *.disk)|*.ndd;*.ndr;*.ram;*.n64;*.z64;*.disk|64DD RAM Area Image (*.ram)|*.ram|64DD Disk Image (*.ndd, *.ndr, *.disk)|*.ndd;*.ndr;*.disk|N64 Cartridge Port Image (*.n64, *.z64)|*.n64;*.z64|All files|*.*"; 93 | if (ofs.ShowDialog() == DialogResult.OK) 94 | { 95 | var error = Program.ImportRAM(ofs.FileName); 96 | if (error == Program.DiskError.NotDiskFile) 97 | { 98 | MessageBox.Show("This file is not a disk file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 99 | return; 100 | } 101 | else if (error == Program.DiskError.NoRAMArea) 102 | { 103 | MessageBox.Show("This disk does not contain a RAM Area.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 104 | return; 105 | } 106 | else if (error == Program.DiskError.FileNotExist) 107 | { 108 | MessageBox.Show("This file does not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 109 | return; 110 | } 111 | else if (error == Program.DiskError.Mismatch) 112 | { 113 | MessageBox.Show("Both disks' RAM Area format do not match.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 114 | return; 115 | } 116 | 117 | 118 | SaveFileDialog sfd = new SaveFileDialog(); 119 | sfd.Title = "Save Modified Disk..."; 120 | switch (Program.Disk.Format) 121 | { 122 | case LeoDisk.DiskFormat.SDK: 123 | case LeoDisk.DiskFormat.MAME: 124 | sfd.Filter = "64DD Disk Image (*.ndd, *.ndr, *.disk)|*.ndd;*.ndr;*.disk"; 125 | break; 126 | case LeoDisk.DiskFormat.RAM: 127 | sfd.Filter = "64DD RAM Area Image (*.ram)|*.ram"; 128 | break; 129 | case LeoDisk.DiskFormat.N64: 130 | sfd.Filter = "N64 Cartridge Port Image (*.n64, *.z64)|*.n64;*.z64"; 131 | break; 132 | default: 133 | return; 134 | } 135 | 136 | if (sfd.ShowDialog() == DialogResult.OK) 137 | { 138 | bool saveError = false; 139 | try 140 | { 141 | Program.Disk.Save(sfd.FileName); 142 | } 143 | catch 144 | { 145 | saveError = true; 146 | } 147 | 148 | if (!saveError) 149 | { 150 | MessageBox.Show("File has been saved.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 151 | } 152 | } 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /save_manager/MainForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /save_manager/Program.cs: -------------------------------------------------------------------------------- 1 | using mfs_library; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows.Forms; 8 | 9 | namespace save_manager 10 | { 11 | internal static class Program 12 | { 13 | /// 14 | /// Point d'entrée principal de l'application. 15 | /// 16 | [STAThread] 17 | static void Main() 18 | { 19 | Application.EnableVisualStyles(); 20 | Application.SetCompatibleTextRenderingDefault(false); 21 | Application.Run(new MainForm()); 22 | } 23 | 24 | public static LeoDisk Disk; 25 | 26 | public enum DiskError 27 | { 28 | Success, 29 | NotDiskFile, 30 | NoRAMArea, 31 | FileNotExist, 32 | Mismatch 33 | } 34 | 35 | public static DiskError Load(string filepath) 36 | { 37 | LeoDisk _disk; 38 | var error = Load(filepath, out _disk); 39 | 40 | if (error == DiskError.Success) 41 | Disk = _disk; 42 | 43 | return error; 44 | } 45 | 46 | public static DiskError Load(string filepath, out LeoDisk _disk) 47 | { 48 | _disk = null; 49 | if (!File.Exists(filepath)) 50 | return DiskError.FileNotExist; 51 | 52 | _disk = new LeoDisk(filepath); 53 | if (_disk.Format == LeoDisk.DiskFormat.Invalid) 54 | return DiskError.NotDiskFile; 55 | if (_disk.DiskType >= 6) 56 | return DiskError.NoRAMArea; 57 | 58 | return DiskError.Success; 59 | } 60 | 61 | public static void ExportRAM(string filepath) 62 | { 63 | if (Disk == null) return; 64 | 65 | File.WriteAllBytes(filepath, Disk.GetRAMAreaArray()); 66 | } 67 | 68 | public static DiskError ImportRAM(string filepath) 69 | { 70 | LeoDisk _disk; 71 | var error = Load(filepath, out _disk); 72 | 73 | if (error != DiskError.Success) return error; 74 | if (_disk.DiskType != Disk.DiskType) return DiskError.Mismatch; 75 | 76 | for (var lba = Leo.RamStartLBA[_disk.DiskType]; lba < Leo.SIZE_LBA; lba++) 77 | { 78 | Disk.WriteLBA(lba, _disk.ReadLBA(lba)); 79 | } 80 | 81 | return DiskError.Success; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /save_manager/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Les informations générales relatives à un assembly dépendent de 6 | // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations 7 | // associées à un assembly. 8 | [assembly: AssemblyTitle("64DD Save Manager")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("64DD Save Manager")] 13 | [assembly: AssemblyCopyright("Copyright © LuigiBlood 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly 18 | // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de 19 | // COM, affectez la valeur true à l'attribut ComVisible sur ce type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM 23 | [assembly: Guid("dbc89e4d-7a97-47fb-aa34-2a7613a3b17c")] 24 | 25 | // Les informations de version pour un assembly se composent des quatre valeurs suivantes : 26 | // 27 | // Version principale 28 | // Version secondaire 29 | // Numéro de build 30 | // Révision 31 | // 32 | // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut 33 | // en utilisant '*', comme indiqué ci-dessous : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /save_manager/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Ce code a été généré par un outil. 4 | // Version du runtime :4.0.30319.42000 5 | // 6 | // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si 7 | // le code est régénéré. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace save_manager.Properties 12 | { 13 | 14 | 15 | /// 16 | /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. 17 | /// 18 | // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder 19 | // à l'aide d'un outil, tel que ResGen ou Visual Studio. 20 | // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen 21 | // avec l'option /str ou régénérez votre projet VS. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("save_manager.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Remplace la propriété CurrentUICulture du thread actuel pour toutes 56 | /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /save_manager/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /save_manager/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace save_manager.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /save_manager/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /save_manager/save_manager.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {DBC89E4D-7A97-47FB-AA34-2A7613A3B17C} 8 | WinExe 9 | save_manager 10 | save_manager 11 | v4.6.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Form 51 | 52 | 53 | MainForm.cs 54 | 55 | 56 | 57 | 58 | MainForm.cs 59 | 60 | 61 | ResXFileCodeGenerator 62 | Resources.Designer.cs 63 | Designer 64 | 65 | 66 | True 67 | Resources.resx 68 | 69 | 70 | SettingsSingleFileGenerator 71 | Settings.Designer.cs 72 | 73 | 74 | True 75 | Settings.settings 76 | True 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | {8D1CF3E5-BA0F-4790-BA0A-C52B79302DA3} 85 | mfs_library 86 | 87 | 88 | 89 | --------------------------------------------------------------------------------