DS-Pokemon-Rom-Editor/DS_Map/LibNDSFormats/NSBTX/filesystem2.cs
2020-10-06 22:57:27 +02:00

254 lines
8.2 KiB
C#

/*
* This file is part of NSMB Editor 5.
*
* NSMB Editor 5 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NSMB Editor 5 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NSMB Editor 5. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace NSMBe4.DSFileSystem
{
public class Filesystem
{
protected FilesystemSource source;
public List<File> allFiles = new List<File>();
public List<Directory> allDirs = new List<Directory>();
protected Dictionary<int, File> filesById = new Dictionary<int, File>();
// protected Dictionary<string, File> filesByName = new Dictionary<string, File>();
protected Dictionary<int, Directory> dirsById = new Dictionary<int, Directory>();
// protected Dictionary<string, Directory> dirsByName = new Dictionary<string, Directory>();
public Stream s;
public Directory mainDir;
protected File freeSpaceDelimiter;
public int fileDataOffset = 0;
//public FilesystemBrowser viewer;
public Filesystem(FilesystemSource fs)
{
this.source = fs;
this.s = source.load();
}
public File getFileById(int id)
{
if (!filesById.ContainsKey(id))
return null;
return filesById[id];
}
public File getFileByName(string name)
{
foreach (File f in allFiles)
if (!f.isSystemFile && f.name == name)
return f;
return null;
}
public Directory getDirByPath(string path)
{
string[] shit = path.Split(new char[] { '/' });
Directory dir = mainDir;
for (int i = 0; i < shit.Length; i++)
{
Directory newDir = null;
foreach (Directory d in dir.childrenDirs)
if (d.name == shit[i])
{
newDir = d;
break;
}
if (newDir == null) return null;
dir = newDir;
}
return dir;
}
protected void addFile(File f)
{
allFiles.Add(f);
if (f.id != -1)
if (!filesById.ContainsKey(f.id))
filesById.Add(f.id, f);
// filesByName.Add(f.name, f);
}
protected void addDir(Directory d)
{
allDirs.Add(d);
if (d.id != -1)
dirsById.Add(d.id, d);
// dirsByName.Add(d.name, d);
}
//Tries to find LEN bytes of continuous unused space AFTER the freeSpaceDelimiter (usually fat or fnt)
public int findFreeSpace(int len, int align)
{
allFiles.Sort(); //sort by offset
File bestSpace = null;
int bestSpaceLeft = int.MaxValue;
for (int i = allFiles.IndexOf(freeSpaceDelimiter); i < allFiles.Count - 1; i++)
{
int spBegin = allFiles[i].fileBegin + allFiles[i].fileSize; //- 1 + 1;
if (spBegin % align != 0)
spBegin += align - spBegin % align;
int spEnd = allFiles[i + 1].fileBegin - 1;
if (spEnd % align != 0)
spEnd -= spEnd % align;
int spSize = spEnd - spBegin + 1;
if (spSize >= len)
{
int spLeft = len - spSize;
if (spLeft < bestSpaceLeft && (allFiles[i].fileBegin >= 0x1400000))
{
bestSpaceLeft = spLeft;
bestSpace = allFiles[i];
}
}
}
if (bestSpace != null)
return bestSpace.fileBegin + bestSpace.fileSize + 10;
else //if (allFiles[allFiles.Count - 1].fileBegin >= 0x1400000)
return allFiles[allFiles.Count - 1].fileBegin + allFiles[allFiles.Count - 1].fileSize + 10;
// else
// return 0x1400000; //just add the file at the very end
//The 0x1400000 hack is not needed anymore. We now know what data we were overwriting: the RSA sig.
//See http://board.dirbaio.net/thread.php?id=185 for more details...
}
//yeah, i'm tired of looking through the dump myself ;)
public bool findErrors()
{
allFiles.Sort();
bool res = false;
for (int i = 0; i < allFiles.Count - 1; i++)
{
int firstEnd = allFiles[i].fileBegin + allFiles[i].fileSize - 1;
int secondStart = allFiles[i + 1].fileBegin;
if (firstEnd >= secondStart)
{
Console.Out.WriteLine("ERROR: FILES OVERLAP:");
allFiles[i].dumpFile(2);
allFiles[i + 1].dumpFile(2);
res = true;
}
}
return res;
}
public void close()
{
source.close();
}
public void save()
{
source.save();
}
public void dumpFilesOrdered(TextWriter outs)
{
allFiles.Sort();
foreach (File f in allFiles)
outs.WriteLine(f.fileBegin.ToString("X8") + " .. " + (f.fileBegin + f.fileSize - 1).ToString("X8") + ": " + f.getPath());
}
public virtual void fileMoved(File f)
{
}
public int getFilesystemEnd()
{
allFiles.Sort();
File lastFile = allFiles[allFiles.Count - 1];
int end = lastFile.fileBegin + lastFile.fileSize; //well, 1 byte doesnt matter
return end;
}
public uint readUInt(Stream s)
{
uint res = 0;
for (int i = 0; i < 4; i++)
{
res |= (uint)s.ReadByte() << 8 * i;
}
return res;
}
public void moveAllFiles(File first, int firstOffs)
{
allFiles.Sort();
Console.Out.WriteLine("Moving file " + first.name);
Console.Out.WriteLine("Into " + firstOffs.ToString("X"));
int firstStart = first.fileBegin;
int diff = (int)firstOffs - (int)firstStart;
Console.Out.WriteLine("DIFF " + diff.ToString("X"));
//if (diff < 0)
//throw new Exception("DOSADJODJOSAJD");
// return;
//WARNING: I assume all the aligns are powers of 2
int maxAlign = 4;
for (int i = allFiles.IndexOf(first); i < allFiles.Count; i++)
{
if (allFiles[i].alignment > maxAlign)
maxAlign = allFiles[i].alignment;
}
//To preserve the alignment of all the moved files
if (diff % maxAlign != 0)
diff += (int)(maxAlign - diff % maxAlign);
int fsEnd = getFilesystemEnd();
int toCopy = (int)fsEnd - (int)firstStart;
byte[] data = new byte[toCopy];
s.Seek(firstStart, SeekOrigin.Begin);
s.Read(data, 0, toCopy);
s.Seek(firstStart + diff, SeekOrigin.Begin);
s.Write(data, 0, toCopy);
for (int i = allFiles.IndexOf(first); i < allFiles.Count; i++)
allFiles[i].fileBegin += diff;
for (int i = allFiles.IndexOf(first); i < allFiles.Count; i++)
allFiles[i].saveOffsets();
}
//public void filesystemModified()
//{
// if (viewer != null && !viewer.IsDisposed)
// viewer.Load(this);
//}
}
}