├── .gitmodules ├── .gitignore ├── run.sh ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.m2e.core.prefs └── org.eclipse.jdt.core.prefs ├── docs ├── TOSEC Naming Convention 2015-03-23.pdf ├── The Official No-Intro Convention (20071030).pdf ├── GoodCodes.txt └── xmlheaders.txt ├── src └── com │ └── github │ └── jakz │ └── nit │ ├── scripts │ ├── ScriptStdout.java │ ├── Statement.java │ ├── FindStatement.java │ ├── ScriptEnvironment.java │ ├── SelectStatement.java │ ├── Script.java │ ├── ConsolePanel.java │ └── ScriptParser.java │ ├── Command.java │ ├── data │ ├── header │ │ ├── Test.java │ │ ├── Header.java │ │ ├── Rule.java │ │ ├── TestData.java │ │ ├── Headers.java │ │ └── SkippingStream.java │ └── Searcher.java │ ├── emitter │ ├── Emitter.java │ ├── ClrMameProEmitter.java │ ├── CreatorOptions.java │ └── GameSetCreator.java │ ├── renamer │ ├── RenamerRule.java │ └── TermSetRenamerRule.java │ ├── batch │ ├── BatchDatClassification.java │ ├── SetToPathMapper.java │ ├── BatchVerifyResult.java │ └── BatchOptions.java │ ├── DatType.java │ ├── gui │ ├── FrameSet.java │ ├── SimpleFrame.java │ ├── GameSetMenu.java │ ├── GameSetComparePanel.java │ ├── LogPanel.java │ ├── SearchPanel.java │ ├── GameSetListPanel.java │ └── GameListPanel.java │ ├── merger │ ├── ArchiveEntry.java │ ├── ArchiveInfo.java │ ├── TitleNormalizer.java │ └── Merger.java │ ├── parser │ ├── HeaderParser.java │ └── ClrMameProParserDat.java │ ├── persistence │ └── HandleSerializer.java │ ├── config │ ├── MergeOptions.java │ └── Config.java │ ├── scanner │ └── Renamer.java │ ├── Options.java │ ├── Main.java │ ├── DevMain.java │ ├── Args.java │ └── Operations.java ├── .project ├── .classpath ├── README.md └── pom.xml /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | doc/** 3 | dats*/ 4 | config.json 5 | *.log 6 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | java -cp target/classes:target/lib/* com.jack.nit.Main "$@" -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | encoding/src=UTF-8 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /docs/TOSEC Naming Convention 2015-03-23.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jakz/no-intro-tools/HEAD/docs/TOSEC Naming Convention 2015-03-23.pdf -------------------------------------------------------------------------------- /docs/The Official No-Intro Convention (20071030).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jakz/no-intro-tools/HEAD/docs/The Official No-Intro Convention (20071030).pdf -------------------------------------------------------------------------------- /src/com/github/jakz/nit/scripts/ScriptStdout.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.scripts; 2 | 3 | public interface ScriptStdout 4 | { 5 | public void append(String string); 6 | } 7 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/scripts/Statement.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.scripts; 2 | 3 | @FunctionalInterface 4 | public interface Statement 5 | { 6 | public void execute(ScriptEnvironment env); 7 | } -------------------------------------------------------------------------------- /src/com/github/jakz/nit/Command.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit; 2 | 3 | public enum Command 4 | { 5 | ORGANIZE, 6 | CREATE_DAT, 7 | COMPARE_DAT, 8 | GUI, 9 | CONSOLE, 10 | EXPORT 11 | } 12 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/data/header/Test.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.data.header; 2 | 3 | import java.util.function.Predicate; 4 | 5 | import com.pixbits.lib.io.BinaryBuffer; 6 | 7 | public abstract class Test 8 | { 9 | abstract Predicate build(); 10 | } 11 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 3 | org.eclipse.jdt.core.compiler.compliance=1.8 4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 5 | org.eclipse.jdt.core.compiler.source=1.8 6 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/emitter/Emitter.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.emitter; 2 | 3 | import java.io.IOException; 4 | 5 | import com.github.jakz.romlib.data.set.GameSet; 6 | 7 | public interface Emitter 8 | { 9 | public void generate(CreatorOptions options, GameSet set) throws IOException; 10 | } 11 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/renamer/RenamerRule.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.renamer; 2 | 3 | @FunctionalInterface 4 | public interface RenamerRule 5 | { 6 | String apply(String name); 7 | 8 | public static final RenamerRule TRIM = name -> name.trim(); 9 | public static final RenamerRule REMOVE_MULTIPLE_WHITESPACE = name -> name.replaceAll(" +", " "); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/scripts/FindStatement.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.scripts; 2 | 3 | import java.util.function.Predicate; 4 | 5 | import com.github.jakz.romlib.data.game.Game; 6 | 7 | public class FindStatement implements Statement 8 | { 9 | FindStatement(Predicate query) { } 10 | 11 | public void execute(ScriptEnvironment env) 12 | { 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/com/github/jakz/nit/scripts/ScriptEnvironment.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.scripts; 2 | 3 | import com.github.jakz.romlib.data.set.GameSet; 4 | 5 | public class ScriptEnvironment 6 | { 7 | final ScriptStdout out; 8 | final GameSet set; 9 | 10 | public ScriptEnvironment(GameSet set, ScriptStdout out) 11 | { 12 | this.out = out; 13 | this.set = set; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/scripts/SelectStatement.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.scripts; 2 | 3 | import java.util.function.Predicate; 4 | 5 | import com.github.jakz.romlib.data.game.Game; 6 | 7 | public class SelectStatement implements Statement 8 | { 9 | SelectStatement(Predicate query) { } 10 | 11 | public void execute(ScriptEnvironment env) 12 | { 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/com/github/jakz/nit/data/header/Header.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.data.header; 2 | 3 | public class Header 4 | { 5 | public final String name; 6 | public final String author; 7 | public final String version; 8 | public Header(String name, String author, String version, Rule[] rules) 9 | { 10 | this.name = name; 11 | this.author = author; 12 | this.version = version; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/scripts/Script.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.scripts; 2 | 3 | import java.util.List; 4 | 5 | public class Script 6 | { 7 | private final List statements; 8 | Script(List statements) { this.statements = statements; } 9 | public int length() { return statements.size(); } 10 | 11 | public void execute(ScriptEnvironment env) 12 | { 13 | statements.forEach(s -> s.execute(env)); 14 | } 15 | } -------------------------------------------------------------------------------- /src/com/github/jakz/nit/batch/BatchDatClassification.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.batch; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.github.jakz.romlib.data.set.GameSet; 7 | 8 | public class BatchDatClassification 9 | { 10 | public List revisions; 11 | public List notVersionable; 12 | 13 | BatchDatClassification() 14 | { 15 | this.revisions = new ArrayList(); 16 | this.notVersionable = new ArrayList(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/com/github/jakz/nit/DatType.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit; 2 | 3 | import java.util.Arrays; 4 | 5 | public enum DatType 6 | { 7 | CLR_MAME_PRO("clrmamepro"), 8 | LOGIQX("logiqx"), 9 | UNSPECIFIED("unknown") 10 | 11 | ; 12 | 13 | public final String name; 14 | 15 | private DatType(String name) 16 | { 17 | this.name = name; 18 | } 19 | 20 | public static DatType forName(String name) 21 | { 22 | return Arrays.stream(values()).filter(f -> f.name.equals(name)).findAny().orElse(DatType.UNSPECIFIED); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/renamer/TermSetRenamerRule.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.renamer; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | public abstract class TermSetRenamerRule implements RenamerRule 8 | { 9 | private final Set words; 10 | 11 | TermSetRenamerRule(String... words) 12 | { 13 | this.words = new HashSet(Arrays.asList(words)); 14 | } 15 | 16 | @Override 17 | public String apply(String name) { 18 | // TODO Auto- method stub 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/gui/FrameSet.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.gui; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import javax.swing.JFrame; 7 | 8 | public class FrameSet 9 | { 10 | private final Map frames; 11 | 12 | public FrameSet() 13 | { 14 | frames = new HashMap<>(); 15 | } 16 | 17 | public void add(String name, JFrame frame) 18 | { 19 | frames.put(name, frame); 20 | } 21 | 22 | @SuppressWarnings("unchecked") 23 | public T get(String name) 24 | { 25 | return (T)frames.get(name); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/data/header/Rule.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.data.header; 2 | 3 | public class Rule 4 | { 5 | public static long EOF = Long.MIN_VALUE; 6 | 7 | public static enum Type 8 | { 9 | none, 10 | bitswap, 11 | byteswap, 12 | wordswap, 13 | wordbyteswap 14 | }; 15 | 16 | public final long startOffset; 17 | public final long endOffset; 18 | public final Type type; 19 | 20 | public Rule(Type type, long startOffset, long endOffset, Test[] tests) 21 | { 22 | this.type = type; 23 | this.startOffset = startOffset; 24 | this.endOffset = endOffset; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/gui/SimpleFrame.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.gui; 2 | 3 | import javax.swing.JFrame; 4 | import javax.swing.JPanel; 5 | 6 | public class SimpleFrame extends JFrame 7 | { 8 | protected final T content; 9 | 10 | public SimpleFrame(String title, T content, boolean exitOnClose) 11 | { 12 | this.content = content; 13 | 14 | getContentPane().add(content); 15 | setTitle(title); 16 | 17 | pack(); 18 | 19 | if (exitOnClose) 20 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 21 | } 22 | 23 | public T panel() { return content; } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | nointrotools 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/gui/GameSetMenu.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.gui; 2 | 3 | import javax.swing.JMenu; 4 | import javax.swing.JMenuBar; 5 | import javax.swing.JMenuItem; 6 | 7 | import com.github.jakz.nit.DevMain; 8 | 9 | public class GameSetMenu extends JMenuBar 10 | { 11 | public GameSetMenu() 12 | { 13 | JMenu viewMenu = new JMenu("View"); 14 | 15 | JMenuItem showLog = new JMenuItem("Show Log"); 16 | showLog.addActionListener(e -> { 17 | DevMain.frames.get("log").setLocationRelativeTo(DevMain.frames.get("main")); 18 | DevMain.frames.get("log").setVisible(true); 19 | }); 20 | 21 | viewMenu.add(showLog); 22 | 23 | this.add(viewMenu); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/data/header/TestData.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.data.header; 2 | 3 | import java.util.Arrays; 4 | import java.util.function.Predicate; 5 | 6 | import com.pixbits.lib.io.BinaryBuffer; 7 | 8 | public class TestData extends Test 9 | { 10 | private final long offset; 11 | private final byte[] data; 12 | private boolean isPositive; 13 | 14 | public TestData(long offset, byte[] data, boolean isPositive) 15 | { 16 | this.offset = offset; 17 | this.data = data; 18 | this.isPositive = isPositive; 19 | } 20 | 21 | @Override Predicate build() 22 | { 23 | return b -> { 24 | byte[] test = new byte[data.length]; 25 | b.read(test, (int)offset); 26 | return Arrays.equals(data, test) ^ isPositive; 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/data/header/Headers.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.data.header; 2 | 3 | import java.io.InputStream; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | public class Headers 8 | { 9 | private interface StreamBuilder 10 | { 11 | public InputStream build(InputStream is); 12 | } 13 | 14 | private static final Map builders = new HashMap<>(); 15 | 16 | static 17 | { 18 | builders.put("nes", new StreamBuilder(){ 19 | @Override public InputStream build(InputStream is) 20 | { 21 | return new SkippingStream(is, new byte[] { 0x4e, 0x45, 0x53 }, 16); 22 | } 23 | }); 24 | 25 | builders.put("fds", new StreamBuilder(){ 26 | @Override public InputStream build(InputStream is) 27 | { 28 | return new SkippingStream(is, new byte[] { 0x46, 0x44, 0x53 }, 16); 29 | } 30 | }); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/merger/ArchiveEntry.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.merger; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | import com.pixbits.lib.io.archive.Compressible; 7 | import com.pixbits.lib.io.archive.handles.Handle; 8 | 9 | public class ArchiveEntry implements Compressible 10 | { 11 | private final Handle handle; 12 | private final String fileName; 13 | 14 | public ArchiveEntry(Handle handle) 15 | { 16 | this(handle, handle.plainInternalName()); 17 | } 18 | 19 | public ArchiveEntry(Handle handle, String fileName) 20 | { 21 | this.handle = handle; 22 | this.fileName = fileName; 23 | } 24 | 25 | public Handle handle() { return handle; } 26 | 27 | @Override public String fileName() { return fileName; } 28 | @Override public long size() { return handle.size(); } 29 | @Override public InputStream getInputStream() throws IOException { return handle.getInputStream(); } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/batch/SetToPathMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.batch; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.util.Map; 6 | 7 | import com.github.jakz.romlib.data.set.GameSet; 8 | import com.pixbits.lib.io.archive.ArchiveFormat; 9 | 10 | @FunctionalInterface 11 | public interface SetToPathMapper 12 | { 13 | Path getRompath(GameSet set); 14 | 15 | public static SetToPathMapper of(final Map nameToPathMap) 16 | { 17 | return set -> nameToPathMap.getOrDefault(set.info().getName(), null); 18 | } 19 | 20 | public static SetToPathMapper ofDefaultNamingInFolder(Path folder) 21 | { 22 | return set -> { 23 | String expectedName = set.info().getName(); 24 | 25 | Path asFolder = folder.resolve(expectedName); 26 | 27 | if (Files.exists(asFolder) && Files.isDirectory(asFolder)) 28 | return asFolder; 29 | else 30 | { 31 | for (ArchiveFormat format : ArchiveFormat.readableFormats) 32 | { 33 | Path asArchive = folder.resolve(expectedName + format.dottedExtension()); 34 | 35 | if (Files.exists(asArchive) && !Files.isDirectory(asArchive)) 36 | return asArchive; 37 | } 38 | 39 | return null; 40 | } 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/merger/ArchiveInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.merger; 2 | 3 | import java.nio.file.Path; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.Collection; 7 | import java.util.List; 8 | import java.util.stream.Stream; 9 | 10 | class ArchiveInfo 11 | { 12 | public final String name; 13 | public final List entries; 14 | 15 | ArchiveInfo(String name) 16 | { 17 | this.name = name; 18 | this.entries = new ArrayList<>(); 19 | } 20 | 21 | ArchiveInfo(String name, ArchiveEntry... entries) 22 | { 23 | this(name, Arrays.asList(entries)); 24 | } 25 | 26 | ArchiveInfo(String name, Collection entries) 27 | { 28 | this(name); 29 | this.entries.addAll(entries); 30 | } 31 | 32 | void add(ArchiveEntry entries) 33 | { 34 | this.entries.add(entries); 35 | } 36 | 37 | void add(Collection entries) 38 | { 39 | this.entries.addAll(entries); 40 | } 41 | 42 | public int size() { return entries.size(); } 43 | List entries() { return entries; } 44 | Stream stream() { return entries.stream(); } 45 | 46 | void relocate(Path dest) 47 | { 48 | entries.forEach(h -> h.handle().relocate(dest)); 49 | //TODO: should take care of internal name too 50 | } 51 | } -------------------------------------------------------------------------------- /src/com/github/jakz/nit/batch/BatchVerifyResult.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.batch; 2 | 3 | import java.nio.file.Path; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import com.github.jakz.romlib.data.game.GameStatus; 8 | import com.github.jakz.romlib.data.game.Rom; 9 | import com.github.jakz.romlib.data.set.GameSet; 10 | 11 | public class BatchVerifyResult 12 | { 13 | public final boolean skipped; 14 | public final Path scanPath; 15 | public final GameSet set; 16 | 17 | private final int[] gameCounts; 18 | private final int[] romCounts; 19 | 20 | public final List missingRoms; 21 | 22 | public BatchVerifyResult(GameSet set, Path scanPath, boolean skipped) 23 | { 24 | this.skipped = skipped; 25 | this.set = set; 26 | this.scanPath = scanPath; 27 | 28 | this.gameCounts = new int[GameStatus.values().length]; 29 | this.romCounts = new int[2]; 30 | 31 | this.missingRoms = new ArrayList<>(); 32 | } 33 | 34 | public void computeStats() 35 | { 36 | set.refreshStatus(); 37 | 38 | set.stream().forEach(game -> { 39 | ++gameCounts[game.getStatus().ordinal()]; 40 | 41 | game.stream().forEach(rom -> { 42 | ++romCounts[rom.isPresent() ? 1 : 0]; 43 | 44 | if (rom.isMissing()) 45 | missingRoms.add(rom); 46 | }); 47 | }); 48 | } 49 | 50 | public int getGameCountByStatus(GameStatus status) 51 | { 52 | return gameCounts[status.ordinal()]; 53 | } 54 | 55 | public int getFoundRomCount() { return romCounts[1]; } 56 | public int getMissingRomCount() { return romCounts[0]; } 57 | } 58 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/emitter/ClrMameProEmitter.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.emitter; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | import java.nio.file.Files; 6 | import javax.xml.bind.annotation.adapters.HexBinaryAdapter; 7 | 8 | import com.github.jakz.romlib.data.set.GameSet; 9 | 10 | import static com.pixbits.lib.lang.StringUtils.isEmpty; 11 | 12 | public class ClrMameProEmitter implements Emitter 13 | { 14 | private final HexBinaryAdapter hexConverter = new HexBinaryAdapter(); 15 | 16 | @Override 17 | public void generate(CreatorOptions options, GameSet set) throws IOException 18 | { 19 | try (PrintWriter wrt = new PrintWriter(Files.newBufferedWriter(options.destPath))) 20 | { 21 | wrt.print("clrmamepro (\n"); 22 | if (!isEmpty(set.info().getName())) 23 | wrt.printf("\tname \"%s\"\n", set.info().getName()); 24 | if (!isEmpty(set.info().getDescription())) 25 | wrt.printf("\tdescription \"%s\"\n", set.info().getDescription()); 26 | if (!isEmpty(set.info().getVersion())) 27 | wrt.printf("\tversion \"%s\"\n", set.info().getVersion()); 28 | if (!isEmpty(set.info().getComment())) 29 | wrt.printf("\tcomment \"%s\"\n", set.info().getComment()); 30 | if (!isEmpty(set.info().getAuthor())) 31 | wrt.printf("\tauthor \"%s\"\n", set.info().getAuthor()); 32 | wrt.print(")\n\n"); 33 | 34 | set.stream().sorted((g1,g2) -> g1.getTitle().compareToIgnoreCase(g2.getTitle())).forEach(game -> { 35 | wrt.print("game (\n"); 36 | wrt.printf("\tname \"%s\"\n", game.getTitle()); 37 | wrt.printf("\tdescription \"%s\"\n", game.getDescription()); 38 | 39 | game.stream().sorted((r1,r2) -> r1.name.compareToIgnoreCase(r2.name)).forEach(rom -> { 40 | wrt.printf("\trom ( name \"%s\" size %d crc %s md5 %s sha1 %s )\n", rom.name, rom.size(), Integer.toHexString((int)rom.crc() & 0xFFFFFFFF).toUpperCase(), hexConverter.marshal(rom.md5()), hexConverter.marshal(rom.sha1())); 41 | }); 42 | 43 | wrt.print(")\n\n"); 44 | 45 | }); 46 | } 47 | 48 | if (set.clones().size() > 0) 49 | { 50 | set.clones(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/parser/HeaderParser.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.parser; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.xml.sax.Attributes; 7 | 8 | import com.github.jakz.nit.data.header.Header; 9 | import com.github.jakz.nit.data.header.Rule; 10 | import com.github.jakz.nit.data.header.Test; 11 | import com.github.jakz.nit.data.header.TestData; 12 | import com.pixbits.lib.io.xml.XMLHandler; 13 | 14 | public class HeaderParser extends XMLHandler
15 | { 16 | Header header; 17 | List rules; 18 | List tests; 19 | 20 | @Override protected void init() 21 | { 22 | 23 | } 24 | 25 | @Override protected void end(String ns, String name) 26 | { 27 | if (name.equals("name") || name.equals("author") || name.equals("version")) 28 | mapOuter(name, asString()); 29 | else if (name.equals("detector")) 30 | { 31 | if (rules.size() == 0) throw new IllegalArgumentException("header must contain at least one rule"); 32 | header = new Header(value("name"), valueOrDefault("author", ""), valueOrDefault("version", ""), rules.toArray(new Rule[rules.size()])); 33 | } 34 | else if (name.equals("rule")) 35 | { 36 | rules.add(new Rule(value("operation"), value("start_offset"), value("end_offset"), tests.toArray(new Test[tests.size()]))); 37 | } 38 | else if (name.equals("data")) 39 | { 40 | tests.add(new TestData(value("offset"), value("value"), value("result"))); 41 | } 42 | } 43 | 44 | @Override protected void start(String ns, String name, Attributes attr) 45 | { 46 | if (name.equals("detector")) 47 | { 48 | rules = new ArrayList<>(); 49 | } 50 | else if (name.equals("rule")) 51 | { 52 | tests = new ArrayList<>(); 53 | 54 | map("start_offset", longHexAttributeOrDefault("start_offset", 0)); 55 | map("end_offset", longHexAttributeOrDefault("end_offset", Rule.EOF)); 56 | map("operation", Rule.Type.valueOf(this.stringAttributeOrDefault("operation", "none"))); 57 | } 58 | else if (name.equals("data")) 59 | { 60 | map("offset", longHexAttributeOrDefault("offset", 0)); 61 | map("value", hexByteArray("value")); 62 | map("result", boolOrDefault("result", true)); 63 | } 64 | } 65 | 66 | @Override public Header get() 67 | { 68 | return header; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/data/header/SkippingStream.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.data.header; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Arrays; 6 | 7 | public class SkippingStream extends InputStream 8 | { 9 | 10 | private final byte[] values; 11 | private final int bytesToSkip; 12 | 13 | private final byte[] buffer; 14 | 15 | private InputStream is; 16 | 17 | private Status status; 18 | private int read; 19 | 20 | private enum Status 21 | { 22 | JUST_OPENED, 23 | READY, 24 | INVALID, 25 | }; 26 | 27 | public SkippingStream(InputStream is, byte[] values, int bytesToSkip) 28 | { 29 | this.values = values; 30 | this.bytesToSkip = bytesToSkip; 31 | this.is = is; 32 | 33 | this.buffer = new byte[values.length]; 34 | 35 | this.status = Status.JUST_OPENED; 36 | this.read = 0; 37 | } 38 | 39 | public int read(byte[] buffer) throws IOException 40 | { 41 | return super.read(buffer); 42 | } 43 | 44 | public int read(byte[] buffer, int o, int l) throws IOException 45 | { 46 | return super.read(buffer, o, l); 47 | } 48 | 49 | public int read() throws IOException 50 | { 51 | switch (this.status) 52 | { 53 | case READY: 54 | return is.read(); 55 | 56 | case JUST_OPENED: 57 | { 58 | /* read enough bytes to be able to check magic number */ 59 | while ((read += is.read(buffer, read, values.length-read)) < values.length); 60 | 61 | /* magic number is correct, then we apply skip */ 62 | boolean valid = Arrays.equals(values, buffer); 63 | 64 | /* if check is valid then we should 65 | * skip all the remaining bytes from header 66 | * and return the next one 67 | */ 68 | if (valid) 69 | { 70 | while (read < bytesToSkip) 71 | { 72 | is.read(); 73 | ++read; 74 | } 75 | 76 | status = Status.READY; 77 | return is.read(); 78 | } 79 | else 80 | { 81 | /* otherwise we should return the 82 | * buffer and then keep with the stream 83 | */ 84 | status = Status.INVALID; 85 | byte value = buffer[values.length - read]; 86 | --read; 87 | return value; 88 | } 89 | } 90 | case INVALID: 91 | { 92 | byte value = buffer[values.length - read]; 93 | --read; 94 | if (read == 0) 95 | status = Status.READY; 96 | return value; 97 | } 98 | default: return -1; 99 | } 100 | 101 | 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/persistence/HandleSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.persistence; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.DataOutputStream; 5 | import java.io.IOException; 6 | import java.lang.reflect.Type; 7 | import java.util.Base64; 8 | import java.util.zip.GZIPOutputStream; 9 | 10 | import com.google.gson.JsonArray; 11 | import com.google.gson.JsonDeserializationContext; 12 | import com.google.gson.JsonDeserializer; 13 | import com.google.gson.JsonElement; 14 | import com.google.gson.JsonParseException; 15 | import com.google.gson.JsonPrimitive; 16 | import com.google.gson.JsonSerializationContext; 17 | import com.google.gson.JsonSerializer; 18 | import com.pixbits.lib.io.archive.handles.ArchiveHandle; 19 | import com.pixbits.lib.io.archive.handles.BinaryHandle; 20 | import com.pixbits.lib.io.archive.handles.Handle; 21 | 22 | public class HandleSerializer 23 | { 24 | public final static int BINARY = 0; 25 | public final static int ARCHIVE = 1; 26 | public final static int NESTED_ARCHIVE = 2; 27 | 28 | class RomHandleSerializer implements JsonDeserializer, JsonSerializer 29 | { 30 | @Override 31 | public Handle deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException 32 | { 33 | 34 | return null; 35 | } 36 | 37 | @Override 38 | public JsonElement serialize(Handle handle, Type type, JsonSerializationContext context) 39 | { 40 | JsonArray array = new JsonArray(); 41 | 42 | if (handle instanceof BinaryHandle) 43 | { 44 | array.add(new JsonPrimitive(BINARY)); 45 | array.add(context.serialize(handle.path())); 46 | } 47 | else if (handle instanceof ArchiveHandle) 48 | { 49 | ArchiveHandle ahandle = (ArchiveHandle)handle; 50 | 51 | array.add(new JsonPrimitive(ARCHIVE)); 52 | array.add(context.serialize(ahandle.path())); 53 | array.add(context.serialize(ahandle.format)); 54 | array.add(new JsonPrimitive(ahandle.internalName)); 55 | array.add(new JsonPrimitive(ahandle.indexInArchive)); 56 | array.add(new JsonPrimitive(ahandle.size)); 57 | array.add(new JsonPrimitive(ahandle.compressedSize)); 58 | } 59 | 60 | return array; 61 | } 62 | 63 | } 64 | 65 | public String serialize(Handle handle) throws IOException 66 | { 67 | try 68 | ( 69 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 70 | GZIPOutputStream zos = new GZIPOutputStream(bos); 71 | DataOutputStream dos = new DataOutputStream(zos); 72 | ) 73 | { 74 | Base64.getEncoder(); 75 | 76 | if (handle instanceof BinaryHandle) 77 | { 78 | 79 | } 80 | } 81 | 82 | return null; 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/batch/BatchOptions.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.batch; 2 | 3 | import java.nio.file.FileSystems; 4 | import java.nio.file.Path; 5 | import java.nio.file.PathMatcher; 6 | import java.nio.file.Paths; 7 | import java.util.Collections; 8 | import java.util.Comparator; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.function.Function; 13 | 14 | import com.github.jakz.nit.DatType; 15 | import com.github.jakz.romlib.data.set.GameSet; 16 | import com.pixbits.lib.io.archive.VerifierEntry; 17 | 18 | public class BatchOptions 19 | { 20 | public final Path datFolder; 21 | public final DatType forcedformat; 22 | 23 | public final Function, BatchDatClassification> datClassifier; 24 | 25 | public final Map> handleTransformers; 26 | 27 | public final boolean deleteLessRecentDats; 28 | 29 | private SetToPathMapper setToPathMapper; 30 | 31 | public final Path expectedSetsList; 32 | 33 | 34 | public PathMatcher getPathMatcher() 35 | { 36 | return FileSystems.getDefault().getPathMatcher("glob:*.{dat,xml}"); 37 | } 38 | 39 | public Path getRomPath(GameSet set) 40 | { 41 | return setToPathMapper.getRompath(set); 42 | } 43 | 44 | public BatchOptions() 45 | { 46 | datFolder = Paths.get("dats3/"); 47 | forcedformat = DatType.LOGIQX; 48 | 49 | expectedSetsList = Paths.get("dats2/profile.xml"); 50 | 51 | deleteLessRecentDats = true; 52 | 53 | datClassifier = sets -> { 54 | BatchDatClassification classification = new BatchDatClassification(); 55 | 56 | Function getVersion = s -> { 57 | try 58 | { 59 | String version = s.info().getVersion(); 60 | if (version == null) return -1L; 61 | return Long.parseLong(version.substring(0, 8)); 62 | } 63 | catch (NumberFormatException|StringIndexOutOfBoundsException e) 64 | { 65 | return -1L; 66 | } 67 | }; 68 | 69 | for (GameSet set : sets) 70 | { 71 | if (getVersion.apply(set) != -1L) 72 | classification.revisions.add(set); 73 | else 74 | classification.notVersionable.add(set); 75 | } 76 | 77 | Comparator comparator = (s1, s2) -> Long.compare(getVersion.apply(s1), getVersion.apply(s2)); 78 | Collections.sort(classification.revisions, comparator.reversed()); 79 | 80 | return classification; 81 | }; 82 | 83 | Map romPaths = new HashMap<>(); 84 | 85 | Path base = Paths.get("/Volumes/Vicky/Roms/sets/No Intro/new nointro/"); 86 | 87 | setToPathMapper = SetToPathMapper.ofDefaultNamingInFolder(base); 88 | 89 | handleTransformers = new HashMap<>(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/config/MergeOptions.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.config; 2 | 3 | import java.util.Arrays; 4 | 5 | import com.github.jakz.nit.Args; 6 | import com.pixbits.lib.io.archive.ArchiveFormat; 7 | 8 | import net.sourceforge.argparse4j.inf.Namespace; 9 | 10 | public class MergeOptions 11 | { 12 | public static enum Mode 13 | { 14 | UNCOMPRESSED("uncompressed"), 15 | SINGLE_ARCHIVE_PER_SET("single-archive"), 16 | SINGLE_ARCHIVE_PER_GAME("archive-by-game"), 17 | SINGLE_ARCHIVE_PER_CLONE("archive-by-clone"), 18 | NO_MERGE("no-merge") 19 | ; 20 | 21 | public final String mnemonic; 22 | 23 | private Mode(String mnemonic) 24 | { 25 | this.mnemonic = mnemonic; 26 | } 27 | 28 | public static Mode forName(String name) 29 | { 30 | return Arrays.stream(values()) 31 | .filter(m -> m.mnemonic.equals(name)) 32 | .findFirst().orElse(Mode.SINGLE_ARCHIVE_PER_CLONE); 33 | } 34 | 35 | }; 36 | 37 | public final Mode mode; 38 | public final ArchiveFormat archiveFormat; 39 | public final boolean useSolidArchives; 40 | public final int compressionLevel; 41 | public final boolean forceMergeInPlace; 42 | public final boolean forceFolderPerGameStructure; 43 | public final boolean automaticallyMergeClonesForSameNormalizedNames; 44 | 45 | public MergeOptions() 46 | { 47 | mode = Mode.NO_MERGE; 48 | archiveFormat = ArchiveFormat._7ZIP; 49 | useSolidArchives = true; 50 | compressionLevel = 9; 51 | forceMergeInPlace = false; 52 | forceFolderPerGameStructure = false; 53 | automaticallyMergeClonesForSameNormalizedNames = false; 54 | } 55 | 56 | public MergeOptions(Namespace args) 57 | { 58 | if (args.getBoolean("no-merge")) 59 | mode = MergeOptions.Mode.NO_MERGE; 60 | else 61 | mode = MergeOptions.Mode.forName(args.getString(Args.MERGE_MODE)); 62 | 63 | archiveFormat = ArchiveFormat._7ZIP; 64 | useSolidArchives = !args.getBoolean(Args.NO_SOLID_ARCHIVES); 65 | compressionLevel = args.getInt(Args.COMPRESSION_LEVEL); 66 | forceMergeInPlace = args.getBoolean(Args.IN_PLACE_MERGE); 67 | forceFolderPerGameStructure = args.getBoolean(Args.FORCE_FOLDER_PER_GAME); 68 | automaticallyMergeClonesForSameNormalizedNames = args.getBoolean(Args.AUTOMATICALLY_CREATE_CLONES); 69 | } 70 | 71 | public MergeOptions(MergeOptions other) 72 | { 73 | this.mode = other.mode; 74 | this.archiveFormat = other.archiveFormat; 75 | this.useSolidArchives = other.useSolidArchives; 76 | this.compressionLevel = other.compressionLevel; 77 | this.forceMergeInPlace = other.forceMergeInPlace; 78 | this.forceFolderPerGameStructure = other.forceFolderPerGameStructure; 79 | this.automaticallyMergeClonesForSameNormalizedNames = other.automaticallyMergeClonesForSameNormalizedNames; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/config/Config.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.config; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | import java.lang.reflect.Type; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.List; 10 | 11 | import com.github.jakz.romlib.data.platforms.Platform; 12 | import com.github.jakz.romlib.data.platforms.Platforms; 13 | import com.google.gson.Gson; 14 | import com.google.gson.GsonBuilder; 15 | import com.google.gson.JsonDeserializationContext; 16 | import com.google.gson.JsonDeserializer; 17 | import com.google.gson.JsonElement; 18 | import com.google.gson.JsonParseException; 19 | import com.google.gson.JsonSyntaxException; 20 | import com.pixbits.lib.exceptions.FatalErrorException; 21 | import com.pixbits.lib.json.PathAdapter; 22 | 23 | public class Config 24 | { 25 | public static class DatEntry 26 | { 27 | public Platform platform; 28 | public Path datFile; 29 | public Path xmdbFile; 30 | public List romsetPaths; 31 | } 32 | 33 | public static class CfgOptions 34 | { 35 | boolean multiThreaded; 36 | } 37 | 38 | public List dats; 39 | public CfgOptions options; 40 | 41 | private void verifyThatPathExists(Path path, boolean allowNull, String message) 42 | { 43 | if ((allowNull || path != null) && !Files.exists(path)) 44 | throw new FatalErrorException(new FileNotFoundException(path.toString()+": "+message)); 45 | } 46 | 47 | public void verify() 48 | { 49 | for (DatEntry entry : dats) 50 | { 51 | verifyThatPathExists(entry.datFile, false, "dat file doesn't exist"); 52 | verifyThatPathExists(entry.xmdbFile, true, "xmdb file doesn't exist"); 53 | if (entry.romsetPaths != null) 54 | for (Path romsetPath : entry.romsetPaths) 55 | verifyThatPathExists(romsetPath, true, "romset path doesn't exist"); 56 | } 57 | } 58 | 59 | 60 | 61 | static class PlatformDeserializer implements JsonDeserializer 62 | { 63 | @Override 64 | public Platform deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException 65 | { 66 | String string = context.deserialize(element, String.class); 67 | return Platforms.forIdent(string); 68 | } 69 | } 70 | 71 | public static Config load(Path fileName) 72 | { 73 | GsonBuilder builder = new GsonBuilder(); 74 | builder.registerTypeAdapter(Path.class, new PathAdapter()); 75 | builder.registerTypeAdapter(Platform.class, new PlatformDeserializer()); 76 | Gson gson = builder.create(); 77 | 78 | try (BufferedReader rdr = Files.newBufferedReader(fileName)) 79 | { 80 | Config config = gson.fromJson(rdr, Config.class); 81 | config.verify(); 82 | return config; 83 | } 84 | catch (JsonSyntaxException e) 85 | { 86 | throw new FatalErrorException(e); 87 | } 88 | catch (IOException e) 89 | { 90 | throw new FatalErrorException(e); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/emitter/CreatorOptions.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.emitter; 2 | 3 | import java.nio.file.FileSystems; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.PathMatcher; 7 | import java.nio.file.Paths; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.stream.Collectors; 11 | 12 | import com.github.jakz.nit.Args; 13 | import com.github.jakz.romlib.data.set.DatFormat; 14 | import com.pixbits.lib.exceptions.FatalErrorException; 15 | import com.pixbits.lib.io.archive.ArchiveFormat; 16 | 17 | public class CreatorOptions 18 | { 19 | public enum Mode 20 | { 21 | merged, 22 | multi; 23 | } 24 | 25 | public final boolean multiThreaded; 26 | 27 | public final List sourcePaths; 28 | public final Path destPath; 29 | public final DatFormat format; 30 | public final Mode mode; 31 | 32 | public final String[] binaryExtensions; 33 | 34 | public final PathMatcher archiveMatcher; 35 | public final PathMatcher binaryMatcher; 36 | 37 | public final boolean folderAsArchives; 38 | 39 | public final String name; 40 | public final String description; 41 | public final String version; 42 | public final String comment; 43 | public final String author; 44 | 45 | @SuppressWarnings("unchecked") 46 | public CreatorOptions(Map properties) 47 | { 48 | multiThreaded = !(boolean)properties.get(Args.NO_MULTI_THREAD); 49 | 50 | destPath = Paths.get((String)properties.get("outfile")); 51 | 52 | if (destPath == null || Files.isDirectory(destPath)) 53 | throw new FatalErrorException("destination path for dat creation is invalid/missing: "+destPath); 54 | 55 | sourcePaths = ((List)properties.get("infile")).stream().map(Paths::get).collect(Collectors.toList()); 56 | 57 | if (sourcePaths.stream().anyMatch(p -> !Files.exists(p))) 58 | throw new FatalErrorException("some of source paths for DAT creation does not exists"); 59 | 60 | format = DatFormat.of("clr-mame-pro","clr-mame-pro", "dat"); //(DatFormat)properties.get("format"); 61 | mode = (Mode)properties.get("mode"); 62 | 63 | archiveMatcher = ArchiveFormat.getReadableMatcher(); 64 | 65 | List extensions = (List)properties.get("exts"); 66 | binaryExtensions = extensions.toArray(new String[extensions.size()]); 67 | String matcher = extensions.stream().collect(Collectors.joining(",", "glob:*.{", "}")); 68 | binaryMatcher = FileSystems.getDefault().getPathMatcher(matcher); 69 | 70 | folderAsArchives = (boolean)properties.get("folder-as-archives"); 71 | 72 | name = (String)properties.get("name"); 73 | description = (String)properties.get("description"); 74 | version = (String)properties.get("version"); 75 | comment = (String)properties.get("comment"); 76 | author = (String)properties.get("author"); 77 | 78 | } 79 | 80 | public boolean shouldCalculateCRC() { return true; } 81 | public boolean shouldCalculateMD5() { return true; } 82 | public boolean shouldCalculateSHA1() { return true; } 83 | } 84 | -------------------------------------------------------------------------------- /src/com/github/jakz/nit/data/Searcher.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.data; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.function.Predicate; 7 | 8 | import com.github.jakz.romlib.data.game.Game; 9 | import com.github.jakz.romlib.data.game.GameClone; 10 | import com.github.jakz.romlib.data.game.Location; 11 | import com.github.jakz.romlib.data.game.Version; 12 | import com.pixbits.lib.searcher.BasicPredicate; 13 | import com.pixbits.lib.searcher.SearchPredicate; 14 | import com.pixbits.lib.searcher.BasicSearchParser; 15 | import com.pixbits.lib.searcher.LambdaPredicate; 16 | 17 | public class Searcher 18 | { 19 | com.pixbits.lib.searcher.Searcher searcher; 20 | 21 | public Searcher() 22 | { 23 | final LambdaPredicate freeSearch = new LambdaPredicate(token -> (g -> g.getTitle().toLowerCase().contains(token.toLowerCase()))); 24 | 25 | final SearchPredicate isProper = new BasicPredicate() 26 | { 27 | @Override public Predicate buildPredicate(String token) 28 | { 29 | if (isSearchArg(splitWithDelimiter(token, ":"), "is", "proper")) 30 | return g -> g.getVersion() == Version.PROPER; 31 | else 32 | return null; 33 | } 34 | }; 35 | 36 | final SearchPredicate isLicensed = new BasicPredicate() 37 | { 38 | @Override public Predicate buildPredicate(String token) 39 | { 40 | if (isSearchArg(splitWithDelimiter(token, ":"), "is", "licensed")) 41 | return g -> g.getLicensed(); 42 | else 43 | return null; 44 | } 45 | }; 46 | 47 | BasicSearchParser parser = new BasicSearchParser<>(freeSearch); 48 | 49 | searcher = new com.pixbits.lib.searcher.Searcher<>(parser, Arrays.asList(isProper, isLicensed)); 50 | } 51 | 52 | 53 | public Predicate buildPredicate(String text) 54 | { 55 | return searcher.search(text); 56 | } 57 | 58 | private Game findFirstClone(GameClone clone, Location... zones) 59 | { 60 | for (Location zone : zones) 61 | { 62 | Optional cgame = clone.stream().filter(g -> g.getLocation().isJust(zone)).findFirst(); 63 | 64 | if (cgame.isPresent()) 65 | return cgame.get(); 66 | 67 | cgame = clone.stream().filter(g -> g.getLocation().is(zone)).findFirst(); 68 | 69 | if (cgame.isPresent()) 70 | return cgame.get(); 71 | } 72 | 73 | return null; 74 | } 75 | 76 | public Predicate buildExportByRegionPredicate(Location... zones) 77 | { 78 | return game -> { 79 | GameClone clone = game.getClone(); 80 | List lzones = Arrays.asList(zones); 81 | 82 | /* if game doesn't have any clone then should be exported if it has correct zone */ 83 | if (clone == null) 84 | return lzones.stream().anyMatch(zone -> game.getLocation().isJust(zone)); 85 | else 86 | { 87 | Game cgame = findFirstClone(clone, zones); 88 | return cgame == game; 89 | } 90 | }; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NoIntro Tools 2 | 3 | This program is meant to scan, verify and organize romsets through DATs files released by NoIntro group (more information can be found [here](http://datomatic.no-intro.org/). I decided to start working on this because the alternatives are really outdated, or Windows only, or really cumbersome to use. 4 | 5 | This is really in a alpha stage! 6 | 7 | ### Scanner 8 | It's able to scan and verify against a DAT, by checking file size, CRC32, SHA-1 and MD5, 3 kinds of file at the moment: 9 | 10 | - binary files 11 | - archives 12 | - archives nested inside other archives 13 | 14 | All archive types supported by [7-Zip-JBinding](http://sevenzipjbind.sourceforge.net/) are theoretically supported but for now just ZIP, 7Z and RAR have been tested. 15 | 16 | ### Organizer 17 | 18 | The organizer is able to provide 4 kinds of organizatons at the moment: 19 | 20 | - uncompressed files 21 | - compressed in a single archive 22 | - one archive per game 23 | - one archive per clone (so multiple clones of the same game are compressed in same solid archive) 24 | 25 | The last mode is especially useful to save a lot of space but it requires additional clone data for the specific DAT to let the program know how to group games. 26 | 27 | ### Future an what not 28 | 29 | The architecture of the whole project is really modular, most of the structure related stuff comes from another project of mine: [RomLib](https://github.com/Jakz/romlib), which is a framework supposed to provide generic classes to work on roms and games. 30 | 31 | This means that it's easy to parser for a new format which could be supported, it's easy to add custom renamers to provide specific behavior to the organizer and what not. Feel free to contribute or fix/add functionality. 32 | 33 | ### Building 34 | I'm working with Eclipse and this project relies on other two Maven based projects. The simple solution for me has been to just add these two modules as related project in Eclipse. 35 | 36 | Other solutions would have required to be able to get them as Maven dependency but this would have forced me to commit every change and whatever. 37 | 38 | To keep this brief: __you can't use the root `pom.xml` to build the project. Rather you must go inside `build` folder and execute `build.sh` which will clone the two dependencies and compile all the source together with Maven based on a different `pom.xml`.__ It's ugly I know and it will be fixed one day. 39 | 40 | ### Running 41 | The only enabled subcommand at the moment is `organize`. You can try with 42 | 43 | java -jar nit.jar organize --help 44 | 45 | to get an idea of the various options. A simple example which would scan and merge a set from a folder to another folder is the following: 46 | 47 | java -jar nit.jar organize 48 | --dat-file gameboy.dat 49 | --dat-format logiqx 50 | --clones-file gameboy.xmdb 51 | --roms-path /myromsfolder 52 | --fast 53 | --skip-rename 54 | --merge-path /mymergedfolder 55 | --merge-mode archive-by-clone 56 | --auto-merge-clones 57 | 58 | Mind that it's a good idea to backup your test data before using the software because it hasn't been thoroughly tested. Please report any issue/feature you'd like to see. -------------------------------------------------------------------------------- /src/com/github/jakz/nit/scripts/ConsolePanel.java: -------------------------------------------------------------------------------- 1 | package com.github.jakz.nit.scripts; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Color; 5 | import java.awt.Dimension; 6 | import java.awt.Font; 7 | import java.awt.event.KeyEvent; 8 | import java.awt.event.KeyListener; 9 | 10 | import javax.swing.JPanel; 11 | import javax.swing.JScrollPane; 12 | import javax.swing.JTextArea; 13 | 14 | import org.codehaus.jparsec.Parser; 15 | import org.codehaus.jparsec.error.ParserException; 16 | 17 | public class ConsolePanel extends JPanel implements KeyListener, ScriptStdout 18 | { 19 | private final JTextArea console; 20 | private final JScrollPane pane; 21 | private int startCommandPosition; 22 | 23 | private Parser