ns-usbloader/src/main/java/nsusbloader/Utilities/patches/fs/FsIniMaker.java

164 lines
6.7 KiB
Java

/*
Copyright 2019-2023 Dmitry Isaenko
This file is part of NS-USBloader.
NS-USBloader 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.
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
*/
package nsusbloader.Utilities.patches.fs;
import libKonogonka.Converter;
import nsusbloader.ModelControllers.ILogPrinter;
import nsusbloader.NSLDataTypes.EMsgType;
import nsusbloader.Utilities.patches.MalformedIniFileException;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
public class FsIniMaker {
private static final String FILE_HEADER_TEXT = "# UTF-8\n" +
"# A KIP section is [kip1_name:sha256_hex_8bytes]\n" +
"# A patchset is .patch_name=kip_section_dec:offset_hex_0x:length_hex_0x:src_data_hex,dst_data_hex\n" +
"# _dec: 1 char decimal | _hex_0x: max u32 prefixed with 0x | _hex: hex array.\n" +
"# Kip1 section decimals: TEXT: 0, RODATA: 1, DATA: 2.\n"; // Sending good vibes to Mr. ITotalJustice
private final ILogPrinter logPrinter;
private final String saveToLocation;
private final int offset1;
private final int offset2;
private String firmwareVersionInformationNotice;
private String sectionDeclaration;
private String patchSet1;
private String patchSet2;
public FsIniMaker(ILogPrinter logPrinter,
String saveToLocation,
byte[] _textSection,
int wizardOffset1,
int wizardOffset2,
byte[] sdkVersion,
String patchName,
boolean filesystemTypeFat32) throws Exception{
this.logPrinter = logPrinter;
this.saveToLocation = saveToLocation;
this.offset1 = wizardOffset1 - 4;
this.offset2 = wizardOffset2 - 4;
mkDirs();
makeFwVersionInformationNotice(filesystemTypeFat32, sdkVersion);
makeSectionDeclaration(patchName);
makePatchSet1(_textSection);
makePatchSet2(_textSection);
writeFile();
}
private void mkDirs(){
File parentFolder = new File(saveToLocation + File.separator + "bootloader");
parentFolder.mkdirs();
}
private void makeFwVersionInformationNotice(boolean isFat32, byte[] fwVersion){
String fwVersionFormatted = fwVersion[3]+"."+fwVersion[2]+"."+fwVersion[1]+"."+fwVersion[0];
if (isFat32)
firmwareVersionInformationNotice = "\n#FS (FAT)"+fwVersionFormatted+"\n";
else
firmwareVersionInformationNotice = "\n#FS (ExFAT) "+fwVersionFormatted+"\n";
}
private void makeSectionDeclaration(String patchName){
sectionDeclaration = "[FS:"+patchName.substring(0, 16)+"]";
}
private void makePatchSet1(byte[] _textSection){
if (offset1 > 0) {
byte[] originalInstruction = Arrays.copyOfRange(_textSection, offset1, offset1 + 4);
patchSet1 = String.format(".nosigchk=0:0x%02X:0x4:%s,1F2003D5",
offset1, Converter.byteArrToHexStringAsLE(originalInstruction, true));
}
}
private void makePatchSet2(byte[] _textSection){
if (offset2 > 0) {
byte[] originalInstruction = Arrays.copyOfRange(_textSection, offset2, offset2 + 4);
patchSet2 = String.format(".nosigchk=0:0x%02X:0x4:%s,E0031F2A",
offset2, Converter.byteArrToHexStringAsLE(originalInstruction, true));
}
}
private void writeFile() throws Exception{
final String iniLocation = saveToLocation + File.separator + "bootloader" + File.separator + "patches.ini";
final Path iniLocationPath = Paths.get(iniLocation);
boolean iniNotExists = Files.notExists(iniLocationPath);
try (RandomAccessFile ini = new RandomAccessFile(iniLocation, "rw")){
if (iniNotExists)
ini.writeBytes(FILE_HEADER_TEXT);
else {
String line;
while ((line = ini.readLine()) != null){
if (! line.startsWith(sectionDeclaration))
continue;
if (offset1 > 0) {
String expression1 = ini.readLine();
if (expression1 == null || ! expression1.startsWith(patchSet1))
throw new MalformedIniFileException("Somewhere near "+ini.getFilePointer());
}
String expression2 = ini.readLine();
if (offset2 > 0) {
if (expression2 == null || ! expression2.startsWith(patchSet2))
throw new MalformedIniFileException("Somewhere near "+ini.getFilePointer());
}
else {
if (expression2 == null || ! expression2.startsWith(".nosigchk"))
return;
throw new MalformedIniFileException("Somewhere near "+ini.getFilePointer());
}
return; // Ini file already contains correct information regarding patch file we made.
}
}
ini.writeBytes(firmwareVersionInformationNotice);
ini.writeBytes(sectionDeclaration);
ini.writeBytes("\n");
if (offset1 > 0) {
ini.writeBytes(patchSet1);
ini.writeBytes("\n");
}
if (offset2 > 0) {
ini.writeBytes(patchSet2);
ini.writeBytes("\n");
}
}
catch (MalformedIniFileException e){
e.printStackTrace();
logPrinter.print(
"Existing patches.ini file is malformed or contains incorrect (outdated) information regarding current patch.\n" +
"It's now saved at "+iniLocation+".OLD\n" +
"New patches.ini file created instead.", EMsgType.WARNING);
Files.move(iniLocationPath, Paths.get(iniLocation+".OLD"),
StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
writeFile();
}
}
}