/* 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 . */ 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(); } } }