anonl / nvlist
Showing 25 of 167 files from the diff.
Other files ignored by Codecov
build.gradle has changed.
CHANGELOG.md has changed.
api/build.gradle has changed.
core/build.gradle has changed.

@@ -42,8 +42,8 @@
Loading
42 42
    }
43 43
44 44
    private void open() {
45 -
        resFileSystem = new DesktopGdxFileSystem(folderConfig.getResFolder());
46 -
        outputFileSystem = new RegularFileSystem(folderConfig.getResFolder());
45 +
        resFileSystem = new DesktopGdxFileSystem(folderConfig.getResFolder().toFile());
46 +
        outputFileSystem = new RegularFileSystem(folderConfig.getResFolder().toFile());
47 47
48 48
        preferences = new NovelPrefsStore(resFileSystem, outputFileSystem);
49 49
        try {

@@ -165,6 +165,7 @@
Loading
165 165
    @Override
166 166
    public void load(INovel novel, int slot) {
167 167
        loadRequests.add(new LoadRequest(slot));
168 +
        LOG.info("Load requested: {}", slot);
168 169
    }
169 170
170 171
    private void doLoad(INovel novel, LoadRequest lr) throws IOException {

@@ -64,7 +64,7 @@
Loading
64 64
    public static FilePath applyToRootFolder(FilePath path, IResourceQualifier qualifier) {
65 65
        String pathString = path.toString();
66 66
        // Replace "abc/" with "abc-qualifier/"
67 -
        pathString = pathString.replaceFirst("([^/]+)[/]",
67 +
        pathString = pathString.replaceFirst("([^/]+)[/]?",
68 68
                "$1-" + qualifier.toPathString() + "/");
69 69
        return FilePath.of(pathString);
70 70
    }

@@ -4,6 +4,11 @@
Loading
4 4
import java.util.Arrays;
5 5
import java.util.List;
6 6
7 +
import org.slf4j.Logger;
8 +
import org.slf4j.LoggerFactory;
9 +
10 +
import com.google.common.annotations.VisibleForTesting;
11 +
7 12
import nl.weeaboo.io.Filenames;
8 13
import nl.weeaboo.vn.buildtools.file.IEncodedResource;
9 14
import nl.weeaboo.vn.buildtools.file.ITempFileProvider;
@@ -15,6 +20,8 @@
Loading
15 20
 */
16 21
public final class FfmpegVideoEncoder extends FfmpegEncoder implements IVideoEncoder {
17 22
23 +
    private static final Logger LOG = LoggerFactory.getLogger(FfmpegVideoEncoder.class);
24 +
18 25
    private static final String OUTPUT_EXT = "webm";
19 26
    private static final String VIDEO_CODEC = "libvpx";
20 27
    private static final String VIDEO_QUALITY = "3";
@@ -22,7 +29,12 @@
Loading
22 29
    private static final String AUDIO_QUALITY = "3";
23 30
24 31
    public FfmpegVideoEncoder(ITempFileProvider tempFileProvider) {
25 -
        super(tempFileProvider);
32 +
        this(LOG, tempFileProvider);
33 +
    }
34 +
35 +
    @VisibleForTesting
36 +
    FfmpegVideoEncoder(Logger logger, ITempFileProvider tempFileProvider) {
37 +
        super(logger, tempFileProvider);
26 38
    }
27 39
28 40
    @Override

@@ -23,14 +23,14 @@
Loading
23 23
import nl.weeaboo.vn.input.INativeInput;
24 24
import nl.weeaboo.vn.input.KeyCode;
25 25
26 -
final class ScreenshotTaker {
26 +
final class ScreenshotTaker implements IScreenshotTaker {
27 27
28 28
    private static final Logger LOG = LoggerFactory.getLogger(ScreenshotTaker.class);
29 29
30 30
    private @Nullable IScreenshot pendingScreenshot;
31 31
32 -
    /** Handle input and update internal state. */
33 -
    void update(IEnvironment env, INativeInput input) {
32 +
    @Override
33 +
    public void update(IEnvironment env, INativeInput input) {
34 34
        if (pendingScreenshot != null && (pendingScreenshot.isAvailable() || pendingScreenshot.isFailed())) {
35 35
            Pixmap pixmap = GdxScreenshotUtil.borrowPixels(pendingScreenshot);
36 36
            if (pixmap != null) {

@@ -14,6 +14,7 @@
Loading
14 14
import nl.weeaboo.lua2.vm.LuaTable;
15 15
import nl.weeaboo.lua2.vm.Varargs;
16 16
import nl.weeaboo.vn.core.IContext;
17 +
import nl.weeaboo.vn.core.IDestructible;
17 18
import nl.weeaboo.vn.impl.core.ContextUtil;
18 19
import nl.weeaboo.vn.impl.core.DestructibleElemList;
19 20
import nl.weeaboo.vn.impl.script.ScriptEventDispatcher;
@@ -27,7 +28,7 @@
Loading
27 28
/**
28 29
 * Default implementation of {@link IScriptContext}
29 30
 */
30 -
public class LuaScriptContext implements IScriptContext {
31 +
public class LuaScriptContext implements IScriptContext, IDestructible {
31 32
32 33
    private static final long serialVersionUID = LuaImpl.serialVersionUID;
33 34
    private static final Logger LOG = LoggerFactory.getLogger(LuaScriptContext.class);
@@ -51,6 +52,16 @@
Loading
51 52
        threads.add(mainThread);
52 53
    }
53 54
55 +
    @Override
56 +
    public void destroy() {
57 +
        threads.destroyAll();
58 +
    }
59 +
60 +
    @Override
61 +
    public boolean isDestroyed() {
62 +
        return mainThread.isDestroyed();
63 +
    }
64 +
54 65
    /**
55 66
     * Calls a no-arg function in a new thread.
56 67
     * @throws ScriptException If the function can't be called, or the thread can't be created.
@@ -77,6 +88,10 @@
Loading
77 88
        return thread;
78 89
    }
79 90
91 +
    public IScriptExceptionHandler getDefaultExceptionHandler() {
92 +
        return scriptEnv.getExceptionHandler();
93 +
    }
94 +
80 95
    /**
81 96
     * @return The Lua table containing the system-global settings.
82 97
     */
@@ -146,7 +161,6 @@
Loading
146 161
            try {
147 162
                thread.update();
148 163
            } catch (ScriptException e) {
149 -
                LOG.warn("Exception while executing thread: {}", thread, e);
150 164
                exceptionHandler.onScriptException(thread, e);
151 165
            }
152 166
        }

@@ -19,7 +19,6 @@
Loading
19 19
import nl.weeaboo.vn.buildtools.optimizer.IOptimizerFileSet;
20 20
import nl.weeaboo.vn.buildtools.optimizer.IParallelExecutor;
21 21
import nl.weeaboo.vn.buildtools.optimizer.MainOptimizerConfig;
22 -
import nl.weeaboo.vn.buildtools.optimizer.sound.encoder.FfmpegSoundEncoder;
23 22
import nl.weeaboo.vn.buildtools.optimizer.video.encoder.FfmpegVideoEncoder;
24 23
import nl.weeaboo.vn.buildtools.optimizer.video.encoder.IVideoEncoder;
25 24
import nl.weeaboo.vn.buildtools.optimizer.video.encoder.NoOpVideoEncoder;
@@ -41,8 +40,7 @@
Loading
41 40
    private final IFileSystem resFileSystem;
42 41
43 42
    // --- State during optimization ---
44 -
    private boolean ffmpegAvailable;
45 -
43 +
    private EAvailable ffmpegAvailable;
46 44
47 45
    public VideoOptimizer(IOptimizerContext context) {
48 46
        executor = context.getExecutor();
@@ -52,10 +50,12 @@
Loading
52 50
53 51
        NvlistProjectConnection project = context.getProject();
54 52
        resFileSystem = project.getResFileSystem();
53 +
54 +
        resetState();
55 55
    }
56 56
57 57
    private void resetState() {
58 -
        ffmpegAvailable = FfmpegSoundEncoder.isAvailable();
58 +
        ffmpegAvailable = EAvailable.UNKNOWN;
59 59
    }
60 60
61 61
    /**
@@ -118,11 +118,15 @@
Loading
118 118
    }
119 119
120 120
    private IVideoEncoder createEncoder() {
121 -
        if (ffmpegAvailable) {
122 -
            return new FfmpegVideoEncoder(tempFileProvider);
123 -
        } else {
124 -
            return new NoOpVideoEncoder();
121 +
        if (ffmpegAvailable != EAvailable.NO) {
122 +
            FfmpegVideoEncoder encoder = new FfmpegVideoEncoder(tempFileProvider);
123 +
            if (ffmpegAvailable == EAvailable.YES || encoder.isAvailable()) {
124 +
                ffmpegAvailable = EAvailable.YES;
125 +
                return encoder;
126 +
            }
125 127
        }
128 +
        ffmpegAvailable = EAvailable.NO;
129 +
        return new NoOpVideoEncoder();
126 130
    }
127 131
128 132
    private FilePath getOutputPath(FilePath inputPath, String outputFilename) {
@@ -130,4 +134,7 @@
Loading
130 134
        return folder.resolve(outputFilename);
131 135
    }
132 136
137 +
    private enum EAvailable {
138 +
        YES, NO, UNKNOWN
139 +
    }
133 140
}

@@ -12,13 +12,6 @@
Loading
12 12
    FilePathPattern() {
13 13
    }
14 14
15 -
    /**
16 -
     * Matches all paths equal to, or inside the given folder path.
17 -
     */
18 -
    public static FilePathPattern inFolder(FilePath folderPath) {
19 -
        return new FolderPrefixPattern(folderPath);
20 -
    }
21 -
22 15
    /**
23 16
     * Matches paths using a simple glob-style pattern. The supported special characters are:
24 17
     * <table summary="Supported glob patterns">
@@ -35,24 +28,6 @@
Loading
35 28
     */
36 29
    public abstract boolean matches(FilePath path);
37 30
38 -
    /**
39 -
     * @see #inFolder(FilePath)
40 -
     */
41 -
    private static final class FolderPrefixPattern extends FilePathPattern {
42 -
43 -
        private FilePath folderPath;
44 -
45 -
        public FolderPrefixPattern(FilePath folderPath) {
46 -
            this.folderPath = folderPath;
47 -
        }
48 -
49 -
        @Override
50 -
        public boolean matches(FilePath path) {
51 -
            return path.startsWith(folderPath);
52 -
        }
53 -
54 -
    }
55 -
56 31
    /**
57 32
     * @see #fromGlob(String)
58 33
     */

@@ -54,8 +54,7 @@
Loading
54 54
    // --- State during optimization ---
55 55
    /** Definition per (optimized) sound file */
56 56
    private final Map<FilePath, SoundDefinition> optimizedDefs = Maps.newHashMap();
57 -
    private boolean ffmpegAvailable;
58 -
57 +
    private EAvailable ffmpegAvailable;
59 58
60 59
    public SoundOptimizer(IOptimizerContext context) {
61 60
        executor = context.getExecutor();
@@ -66,12 +65,14 @@
Loading
66 65
        NvlistProjectConnection project = context.getProject();
67 66
        resFileSystem = project.getResFileSystem();
68 67
        soundDefCache = new SoundDefinitionCache(resFileSystem);
68 +
69 +
        resetState();
69 70
    }
70 71
71 72
    private void resetState() {
72 73
        optimizedDefs.clear();
73 74
74 -
        ffmpegAvailable = FfmpegSoundEncoder.isAvailable();
75 +
        ffmpegAvailable = EAvailable.UNKNOWN;
75 76
    }
76 77
77 78
    /**
@@ -162,11 +163,15 @@
Loading
162 163
    }
163 164
164 165
    private ISoundEncoder createEncoder() {
165 -
        if (ffmpegAvailable) {
166 -
            return new FfmpegSoundEncoder(tempFileProvider);
167 -
        } else {
168 -
            return new NoOpSoundEncoder();
166 +
        if (ffmpegAvailable != EAvailable.NO) {
167 +
            FfmpegSoundEncoder encoder = new FfmpegSoundEncoder(tempFileProvider);
168 +
            if (ffmpegAvailable == EAvailable.YES || encoder.isAvailable()) {
169 +
                ffmpegAvailable = EAvailable.YES;
170 +
                return encoder;
171 +
            }
169 172
        }
173 +
        ffmpegAvailable = EAvailable.NO;
174 +
        return new NoOpSoundEncoder();
170 175
    }
171 176
172 177
    private FilePath getOutputPath(FilePath inputPath, String outputFilename) {
@@ -174,4 +179,7 @@
Loading
174 179
        return folder.resolve(outputFilename);
175 180
    }
176 181
182 +
    private enum EAvailable {
183 +
        YES, NO, UNKNOWN
184 +
    }
177 185
}

@@ -5,45 +5,47 @@
Loading
5 5
import java.io.IOException;
6 6
import java.io.InputStreamReader;
7 7
import java.nio.charset.StandardCharsets;
8 +
import java.util.ArrayList;
8 9
import java.util.Arrays;
9 10
import java.util.List;
10 11
11 12
import org.slf4j.Logger;
12 -
import org.slf4j.LoggerFactory;
13 13
14 14
import com.google.common.base.Joiner;
15 15
import com.google.common.collect.Lists;
16 16
import com.google.common.io.Files;
17 17
18 18
import nl.weeaboo.common.Checks;
19 +
import nl.weeaboo.common.StringUtil;
19 20
import nl.weeaboo.vn.buildtools.file.EncodedResource;
20 21
import nl.weeaboo.vn.buildtools.file.IEncodedResource;
21 22
import nl.weeaboo.vn.buildtools.file.ITempFileProvider;
22 -
import nl.weeaboo.vn.buildtools.optimizer.video.encoder.FfmpegVideoEncoder;
23 23
24 24
/**
25 25
 * Resource encoder using ffmpeg.
26 26
 */
27 27
public abstract class FfmpegEncoder {
28 28
29 -
    private static final Logger LOG = LoggerFactory.getLogger(FfmpegVideoEncoder.class);
30 -
29 +
    private final Logger logger;
31 30
    private final ITempFileProvider tempFileProvider;
32 31
33 -
    protected FfmpegEncoder(ITempFileProvider tempFileProvider) {
32 +
    private String program = "ffmpeg";
33 +
34 +
    protected FfmpegEncoder(Logger logger, ITempFileProvider tempFileProvider) {
35 +
        this.logger = Checks.checkNotNull(logger);
34 36
        this.tempFileProvider = Checks.checkNotNull(tempFileProvider);
35 37
    }
36 38
37 39
    /**
38 40
     * @return {@code true} if a usable ffmpeg executable was found, allowing this encoder to be used.
39 41
     */
40 -
    public static boolean isAvailable() {
42 +
    public boolean isAvailable() {
41 43
        try {
42 -
            doRunProcess(Arrays.asList("ffmpeg", "-h"));
43 -
            LOG.debug("ffmpeg is available");
44 +
            doRunProcess(program, Arrays.asList("-h"));
45 +
            logger.debug("ffmpeg is available");
44 46
            return true;
45 47
        } catch (IOException e) {
46 -
            LOG.info("ffmpeg not available: {}", e.toString());
48 +
            logger.info("ffmpeg not available: {}", e.toString());
47 49
            return false;
48 50
        }
49 51
    }
@@ -57,7 +59,7 @@
Loading
57 59
            // Copy sound to temp file (input)
58 60
            Files.write(resource.readBytes(), inputFile);
59 61
60 -
            runProcess(getCommandLineArgs(inputFile, outputFile));
62 +
            runProcess(program, getCommandLineArgs(inputFile, outputFile));
61 63
            resultAudioData = EncodedResource.fromTempFile(outputFile);
62 64
        } finally {
63 65
            inputFile.delete();
@@ -66,13 +68,18 @@
Loading
66 68
        return resultAudioData;
67 69
    }
68 70
69 -
    protected void runProcess(List<String> command) throws IOException {
70 -
        doRunProcess(command);
71 +
    protected void runProcess(String program, List<String> args) throws IOException {
72 +
        doRunProcess(program, args);
71 73
    }
72 74
73 -
    private static void doRunProcess(List<String> command) throws IOException {
75 +
    private void doRunProcess(String program, List<String> args) throws IOException {
76 +
        List<String> command = new ArrayList<>();
77 +
        command.add(program);
78 +
        command.addAll(args);
74 79
        String commandString = Joiner.on(' ').join(command);
75 80
81 +
        logger.trace("Starting process: {}", command);
82 +
76 83
        Process process = new ProcessBuilder()
77 84
                .command(command)
78 85
                .redirectErrorStream(true)
@@ -85,7 +92,7 @@
Loading
85 92
            while (process.isAlive()) {
86 93
                String line = in.readLine();
87 94
88 -
                LOG.trace(line);
95 +
                logger.trace(line);
89 96
90 97
                output.append(line);
91 98
                output.append('\n');
@@ -93,9 +100,9 @@
Loading
93 100
94 101
            int exitCode = process.waitFor();
95 102
            if (exitCode != 0) {
96 -
                throw new IOException("Process terminated with an error: " + exitCode
97 -
                        + "\ncommand: " + commandString
98 -
                        + "\noutput: " + output);
103 +
                throw new IOException(StringUtil.formatRoot(
104 +
                        "Process terminated with an error: %s\ncommand: %s\noutput: %s",
105 +
                        exitCode, commandString, output));
99 106
            }
100 107
        } catch (InterruptedException e) {
101 108
            throw new IOException("Process interrupted", e);
@@ -103,24 +110,27 @@
Loading
103 110
    }
104 111
105 112
    private List<String> getCommandLineArgs(File inputFile, File outputFile) {
106 -
        List<String> command = Lists.newArrayList();
107 -
        command.add("ffmpeg");
113 +
        List<String> args = Lists.newArrayList();
108 114
109 115
        // Input file
110 -
        command.add("-i");
111 -
        command.add(inputFile.getAbsolutePath());
116 +
        args.add("-i");
117 +
        args.add(inputFile.getAbsolutePath());
112 118
113 119
        // File format (container)
114 -
        command.add("-f");
115 -
        command.add(getFileFormat());
120 +
        args.add("-f");
121 +
        args.add(getFileFormat());
116 122
117 123
        // Codec
118 -
        command.addAll(getCodecArgs());
124 +
        args.addAll(getCodecArgs());
119 125
120 126
        // Output file
121 -
        command.add("-y"); // Overwrite output file (is usually an empty temp file)
122 -
        command.add(outputFile.getAbsolutePath());
123 -
        return command;
127 +
        args.add("-y"); // Overwrite output file (is usually an empty temp file)
128 +
        args.add(outputFile.getAbsolutePath());
129 +
        return args;
130 +
    }
131 +
132 +
    public void setProgram(String program) {
133 +
        this.program = program;
124 134
    }
125 135
126 136
    protected abstract List<String> getCodecArgs();

@@ -0,0 +1,19 @@
Loading
1 +
package nl.weeaboo.vn.impl.script;
2 +
3 +
import org.slf4j.Logger;
4 +
import org.slf4j.LoggerFactory;
5 +
6 +
import nl.weeaboo.vn.script.IScriptExceptionHandler;
7 +
import nl.weeaboo.vn.script.IScriptThread;
8 +
9 +
public enum DefaultScriptExceptionHandler implements IScriptExceptionHandler {
10 +
    INSTANCE;
11 +
12 +
    private static final Logger LOG = LoggerFactory.getLogger(DefaultScriptExceptionHandler.class);
13 +
14 +
    @Override
15 +
    public void onScriptException(IScriptThread thread, Exception exception) {
16 +
        LOG.warn("Exception while executing thread: {}", thread, exception);
17 +
    }
18 +
19 +
}

@@ -13,6 +13,7 @@
Loading
13 13
import nl.weeaboo.vn.gdx.res.NativeMemoryTracker;
14 14
import nl.weeaboo.vn.gdx.scene2d.Scene2dEnv;
15 15
import nl.weeaboo.vn.impl.save.SaveParams;
16 +
import nl.weeaboo.vn.impl.script.lua.ILuaConsole;
16 17
import nl.weeaboo.vn.impl.script.lua.LuaConsole;
17 18
import nl.weeaboo.vn.input.INativeInput;
18 19
import nl.weeaboo.vn.input.KeyCode;
@@ -25,12 +26,16 @@
Loading
25 26
26 27
    private static final Logger LOG = LoggerFactory.getLogger(DebugControls.class);
27 28
28 -
    private final LuaConsole luaConsole;
29 -
    private final ScreenshotTaker screenshotTaker;
29 +
    private final ILuaConsole luaConsole;
30 +
    private final IScreenshotTaker screenshotTaker;
30 31
31 32
    public DebugControls(Scene2dEnv sceneEnv) {
32 -
        this.luaConsole = new LuaConsole(sceneEnv);
33 -
        this.screenshotTaker = new ScreenshotTaker();
33 +
        this(new LuaConsole(sceneEnv), new ScreenshotTaker());
34 +
    }
35 +
36 +
    DebugControls(ILuaConsole console, IScreenshotTaker screenshotTaker) {
37 +
        this.luaConsole = console;
38 +
        this.screenshotTaker = screenshotTaker;
34 39
    }
35 40
36 41
    /**
@@ -39,6 +44,7 @@
Loading
39 44
    public void update(INovel novel, INativeInput input) {
40 45
        IEnvironment env = novel.getEnv();
41 46
        screenshotTaker.update(env, input);
47 +
        luaConsole.update(env, input);
42 48
43 49
        if (!env.getPref(NovelPrefs.DEBUG)) {
44 50
            return; // Debug mode not enabled
@@ -66,26 +72,19 @@
Loading
66 72
        // Save/load
67 73
        ISaveModule saveModule = env.getSaveModule();
68 74
        int slot = saveModule.getQuickSaveSlot(99);
69 -
        if (input.consumePress(KeyCode.PLUS)) {
75 +
        if (input.consumePress(KeyCode.NUMPAD_ADD)) {
70 76
            try {
71 77
                saveModule.save(novel, slot, new SaveParams());
72 78
            } catch (IOException e) {
73 79
                LOG.warn("Save error", e);
74 80
            }
75 -
        } else if (input.consumePress(KeyCode.MINUS)) {
81 +
        } else if (input.consumePress(KeyCode.NUMPAD_SUBTRACT)) {
76 82
            try {
77 83
                saveModule.load(novel, slot);
78 84
            } catch (IOException e) {
79 85
                LOG.warn("Load error", e);
80 86
            }
81 87
        }
82 -
83 -
        // Lua console
84 -
        luaConsole.setContext(env.getContextManager());
85 -
        if (input.consumePress(KeyCode.F1)) {
86 -
            // TODO: LuaConsole needs to intercept the F1 key in order to hide itself
87 -
            luaConsole.setVisible(!luaConsole.isVisible());
88 -
        }
89 88
    }
90 89
91 90
}

@@ -54,6 +54,7 @@
Loading
54 54
        if (!destroyed) {
55 55
            destroyed = true;
56 56
57 +
            scriptContext.destroy();
57 58
            LOG.debug("Context destroyed: {}", this);
58 59
            fireDestroyed();
59 60
        }
@@ -115,9 +116,12 @@
Loading
115 116
        LuaScriptThread mainThread = scriptContext.getMainThread();
116 117
        boolean mainThreadWasRunnable = mainThread.isRunnable();
117 118
119 +
        IScriptExceptionHandler defaultExceptionHandler = scriptContext.getDefaultExceptionHandler();
118 120
        scriptContext.updateThreads(this, new IScriptExceptionHandler() {
119 121
            @Override
120 122
            public void onScriptException(IScriptThread thread, Exception exception) {
123 +
                defaultExceptionHandler.onScriptException(thread, exception);
124 +
121 125
                for (IScriptExceptionHandler ls : contextListeners) {
122 126
                    ls.onScriptException(thread, exception);
123 127
                }

@@ -1,7 +1,8 @@
Loading
1 1
package nl.weeaboo.vn.buildtools.project;
2 2
3 -
import java.io.File;
4 3
import java.io.IOException;
4 +
import java.nio.file.Path;
5 +
import java.nio.file.Paths;
5 6
import java.util.Locale;
6 7
import java.util.Objects;
7 8
@@ -10,14 +11,14 @@
Loading
10 11
 */
11 12
public final class ProjectFolderConfig {
12 13
13 -
    private final File projectFolder;
14 -
    private final File buildToolsFolder;
14 +
    private final Path projectFolder;
15 +
    private final Path buildToolsFolder;
15 16
16 17
    public ProjectFolderConfig() {
17 -
        this(new File(""), new File("build-tools"));
18 +
        this(Paths.get(""), Paths.get("build-tools"));
18 19
    }
19 20
20 -
    public ProjectFolderConfig(File projectFolder, File buildToolsFolder) {
21 +
    public ProjectFolderConfig(Path projectFolder, Path buildToolsFolder) {
21 22
        this.projectFolder = Objects.requireNonNull(projectFolder);
22 23
        this.buildToolsFolder = Objects.requireNonNull(buildToolsFolder);
23 24
    }
@@ -25,7 +26,7 @@
Loading
25 26
    /**
26 27
     * Returns a new instance, using a different project folder.
27 28
     */
28 -
    public ProjectFolderConfig withProjectFolder(File newProjectFolder) {
29 +
    public ProjectFolderConfig withProjectFolder(Path newProjectFolder) {
29 30
        return new ProjectFolderConfig(newProjectFolder, buildToolsFolder);
30 31
    }
31 32
@@ -33,57 +34,57 @@
Loading
33 34
     * The folder in which the /res and /build-res folders are stored. This is the root folder for the NVList
34 35
     * project.
35 36
     */
36 -
    public File getProjectFolder() {
37 +
    public Path getProjectFolder() {
37 38
        return projectFolder;
38 39
    }
39 40
40 41
    /**
41 42
     * Folder containing resources files used by the project.
42 43
     */
43 -
    public File getResFolder() {
44 +
    public Path getResFolder() {
44 45
        return getResFolder(projectFolder);
45 46
    }
46 47
47 48
    /**
48 49
     * Folder containing resources files used by the project.
49 50
     */
50 -
    public static File getResFolder(File projectFolder) {
51 -
        return new File(projectFolder, "res");
51 +
    public static Path getResFolder(Path projectFolder) {
52 +
        return projectFolder.resolve("res");
52 53
    }
53 54
54 55
    /**
55 56
     * Folder containing resources and config for building the project.
56 57
     */
57 -
    public File getBuildResFolder() {
58 +
    public Path getBuildResFolder() {
58 59
        return getBuildResFolder(projectFolder);
59 60
    }
60 61
61 62
    /**
62 63
     * Folder containing resources and config for building the project.
63 64
     */
64 -
    public static File getBuildResFolder(File projectFolder) {
65 -
        return new File(projectFolder, "build-res");
65 +
    public static Path getBuildResFolder(Path projectFolder) {
66 +
        return projectFolder.resolve("build-res");
66 67
    }
67 68
68 69
    /**
69 70
     * Properties file containing build properties.
70 71
     */
71 -
    public File getBuildPropertiesFile() {
72 -
        return new File(getBuildResFolder(), "build.properties");
72 +
    public Path getBuildPropertiesFile() {
73 +
        return getBuildResFolder().resolve("build.properties");
73 74
    }
74 75
75 76
    /**
76 77
     * Generated build artifacts are stored in this folder.
77 78
     */
78 -
    public File getBuildOutFolder() {
79 -
        return new File(projectFolder, "build-out");
79 +
    public Path getBuildOutFolder() {
80 +
        return projectFolder.resolve("build-out");
80 81
    }
81 82
82 83
    /**
83 84
     * The build-tools folder containing the Gradle build scripts and other engine-version-specific resources.
84 85
     * This folder may be a sub-folder of the project folder, or an entire separate folder.
85 86
     */
86 -
    public File getBuildToolsFolder() {
87 +
    public Path getBuildToolsFolder() {
87 88
        return buildToolsFolder;
88 89
    }
89 90
@@ -91,11 +92,11 @@
Loading
91 92
     * Returns the canonical path for the given file. In some cases, no canonical path can be determined. In
92 93
     * those cases, the file's absolute path is returned instead.
93 94
     */
94 -
    public static String toCanonicalPath(File file) {
95 +
    public static String toCanonicalPath(Path file) {
95 96
        try {
96 -
            return file.getCanonicalPath();
97 +
            return file.toRealPath().toString();
97 98
        } catch (IOException ioe) {
98 -
            return file.getAbsolutePath();
99 +
            return file.toAbsolutePath().toString();
99 100
        }
100 101
    }
101 102

@@ -4,12 +4,15 @@
Loading
4 4
import java.io.ObjectInputStream;
5 5
import java.util.ArrayList;
6 6
import java.util.List;
7 +
import java.util.Objects;
7 8
8 9
import nl.weeaboo.common.Checks;
9 10
import nl.weeaboo.io.CustomSerializable;
10 11
import nl.weeaboo.lua2.LuaRunState;
11 12
import nl.weeaboo.lua2.vm.LuaTable;
13 +
import nl.weeaboo.vn.impl.script.DefaultScriptExceptionHandler;
12 14
import nl.weeaboo.vn.script.IScriptEnv;
15 +
import nl.weeaboo.vn.script.IScriptExceptionHandler;
13 16
import nl.weeaboo.vn.script.IScriptLoader;
14 17
import nl.weeaboo.vn.script.ScriptException;
15 18
@@ -24,6 +27,7 @@
Loading
24 27
    private final LuaRunState runState;
25 28
    private final LuaScriptLoader loader;
26 29
    private final List<ILuaScriptEnvInitializer> initializers = new ArrayList<>();
30 +
    private IScriptExceptionHandler exceptionHandler = DefaultScriptExceptionHandler.INSTANCE;
27 31
28 32
    private boolean initialized;
29 33
@@ -90,4 +94,13 @@
Loading
90 94
        return loader;
91 95
    }
92 96
97 +
    public IScriptExceptionHandler getExceptionHandler() {
98 +
        return exceptionHandler;
99 +
    }
100 +
101 +
    @Override
102 +
    public void setExceptionHandler(IScriptExceptionHandler exceptionHandler) {
103 +
        this.exceptionHandler = Objects.requireNonNull(exceptionHandler);
104 +
    }
105 +
93 106
}

@@ -1,6 +1,7 @@
Loading
1 1
package nl.weeaboo.vn.buildtools.optimizer;
2 2
3 3
import java.io.File;
4 +
import java.nio.file.Paths;
4 5
import java.util.ArrayList;
5 6
import java.util.List;
6 7
import java.util.regex.Matcher;
@@ -30,8 +31,8 @@
Loading
30 31
     * Opens a connection to a NVList project using the settings from this config.
31 32
     */
32 33
    public NvlistProjectConnection openProject() {
33 -
        return NvlistProjectConnection.openProject(new ProjectFolderConfig(new File(projectFolder),
34 -
                new File(buildToolsFolder)));
34 +
        return NvlistProjectConnection.openProject(new ProjectFolderConfig(Paths.get(projectFolder),
35 +
                Paths.get(buildToolsFolder)));
35 36
    }
36 37
37 38
    /**

@@ -7,7 +7,10 @@
Loading
7 7
import nl.weeaboo.filesystem.FilePath;
8 8
import nl.weeaboo.vn.buildtools.file.FilePathPattern;
9 9
10 -
final class OptimizerFileSet implements IOptimizerFileSet {
10 +
/**
11 +
 * Default implementation of {@link IOptimizerFileSet}.
12 +
 */
13 +
public final class OptimizerFileSet implements IOptimizerFileSet {
11 14
12 15
    private final Set<FilePath> optimized = Sets.newHashSet();
13 16

@@ -1,7 +1,8 @@
Loading
1 1
package nl.weeaboo.vn.buildtools.project;
2 2
3 -
import java.io.File;
4 3
import java.io.IOException;
4 +
import java.nio.file.Files;
5 +
import java.nio.file.Path;
5 6
6 7
/**
7 8
 * Generates a new NVList project based on a project template (folder).
@@ -9,27 +10,27 @@
Loading
9 10
public final class TemplateProjectGenerator implements IProjectGenerator {
10 11
11 12
    @Override
12 -
    public void createNewProject(File targetFolder) throws IOException {
13 +
    public void createNewProject(Path targetFolder) throws IOException {
13 14
        // TODO: Implement
14 15
15 -
        File resFolder = ProjectFolderConfig.getResFolder(targetFolder);
16 +
        Path resFolder = ProjectFolderConfig.getResFolder(targetFolder);
16 17
        createFolder(resFolder);
17 18
18 -
        File buildResFolder = ProjectFolderConfig.getBuildResFolder(targetFolder);
19 +
        Path buildResFolder = ProjectFolderConfig.getBuildResFolder(targetFolder);
19 20
        createFolder(buildResFolder);
20 21
21 -
        createFile(new File(buildResFolder, "build.properties"));
22 +
        createFile(buildResFolder.resolve("build.properties"));
22 23
    }
23 24
24 -
    private void createFolder(File folder) throws IOException {
25 -
        if (!folder.isDirectory() && !folder.mkdirs()) {
26 -
            throw new IOException("Unable to create folder: " + folder);
25 +
    private void createFolder(Path folder) throws IOException {
26 +
        if (!Files.isDirectory(folder)) {
27 +
            Files.createDirectories(folder);
27 28
        }
28 29
    }
29 30
30 -
    private void createFile(File file) throws IOException {
31 -
        if (!file.isFile() && !file.createNewFile()) {
32 -
            throw new IOException("Unable to create file: " + file);
31 +
    private void createFile(Path file) throws IOException {
32 +
        if (!Files.isRegularFile(file)) {
33 +
            Files.createFile(file);
33 34
        }
34 35
    }
35 36
36 37
imilarity index 96%
37 38
ename from buildtools/src/main/java/nl/weeaboo/vn/buildtools/task/AbstractTask.java
38 39
ename to buildtools/src/main/java/nl/weeaboo/vn/buildtools/task/Task.java

@@ -4,6 +4,11 @@
Loading
4 4
import java.util.Arrays;
5 5
import java.util.List;
6 6
7 +
import org.slf4j.Logger;
8 +
import org.slf4j.LoggerFactory;
9 +
10 +
import com.google.common.annotations.VisibleForTesting;
11 +
7 12
import nl.weeaboo.io.Filenames;
8 13
import nl.weeaboo.vn.buildtools.file.IEncodedResource;
9 14
import nl.weeaboo.vn.buildtools.file.ITempFileProvider;
@@ -17,12 +22,19 @@
Loading
17 22
 */
18 23
public final class FfmpegSoundEncoder extends FfmpegEncoder implements ISoundEncoder {
19 24
25 +
    private static final Logger LOG = LoggerFactory.getLogger(FfmpegSoundEncoder.class);
26 +
20 27
    private static final String OUTPUT_EXT = "ogg";
21 28
    private static final String AUDIO_CODEC = "libvorbis";
22 29
    private static final String AUDIO_QUALITY = "3";
23 30
24 31
    public FfmpegSoundEncoder(ITempFileProvider tempFileProvider) {
25 -
        super(tempFileProvider);
32 +
        this(LOG, tempFileProvider);
33 +
    }
34 +
35 +
    @VisibleForTesting
36 +
    FfmpegSoundEncoder(Logger logger, ITempFileProvider tempFileProvider) {
37 +
        super(logger, tempFileProvider);
26 38
    }
27 39
28 40
    @Override

@@ -1,5 +1,6 @@
Loading
1 1
package nl.weeaboo.vn.buildtools.optimizer;
2 2
3 +
import java.io.File;
3 4
import java.util.concurrent.ExecutorService;
4 5
import java.util.concurrent.TimeUnit;
5 6
@@ -9,7 +10,6 @@
Loading
9 10
import com.google.common.base.Preconditions;
10 11
import com.google.common.collect.ClassToInstanceMap;
11 12
import com.google.common.collect.MutableClassToInstanceMap;
12 -
import com.google.common.io.Files;
13 13
import com.google.common.util.concurrent.MoreExecutors;
14 14
15 15
import nl.weeaboo.common.Checks;
@@ -35,7 +35,9 @@
Loading
35 35
    public OptimizerContext(NvlistProjectConnection projectConnection, MainOptimizerConfig mainConfig) {
36 36
        this.projectConnection = projectConnection;
37 37
38 -
        tempFileProvider = new TempFileProvider(Files.createTempDir());
38 +
        File tempDir = new File(System.getProperty("java.io.tmpdir"), "nvlist-" + System.nanoTime());
39 +
        tempDir.mkdirs();
40 +
        tempFileProvider = new TempFileProvider(tempDir);
39 41
        fileSet = new OptimizerFileSet();
40 42
41 43
        executorService = ParallelExecutor.newExecutorService();

@@ -8,7 +8,7 @@
Loading
8 8
/**
9 9
 * Base implementation of {@link ITask}.
10 10
 */
11 -
public abstract class AbstractTask implements ITask {
11 +
public class Task implements ITask {
12 12
13 13
    private final CopyOnWriteArrayList<IProgressListener> progressListeners = new CopyOnWriteArrayList<>();
14 14

@@ -23,14 +23,18 @@
Loading
23 23
24 24
import nl.weeaboo.vn.core.IContext;
25 25
import nl.weeaboo.vn.core.IContextManager;
26 +
import nl.weeaboo.vn.core.IEnvironment;
27 +
import nl.weeaboo.vn.core.NovelPrefs;
26 28
import nl.weeaboo.vn.gdx.scene2d.Scene2dEnv;
27 29
import nl.weeaboo.vn.gdx.scene2d.Scene2dUtil;
30 +
import nl.weeaboo.vn.input.INativeInput;
31 +
import nl.weeaboo.vn.input.KeyCode;
28 32
import nl.weeaboo.vn.script.ScriptException;
29 33
30 34
/**
31 -
 * Interactive Lua terminal/REPL
35 +
 * Default implementation of {@link ILuaConsole}.
32 36
 */
33 -
public class LuaConsole {
37 +
public class LuaConsole implements ILuaConsole {
34 38
35 39
    private static final Logger LOG = LoggerFactory.getLogger(LuaConsole.class);
36 40
    private static final int INPUT_BUFFER_LIMIT = 16;
@@ -51,27 +55,25 @@
Loading
51 55
        this.sceneEnv = sceneEnv;
52 56
    }
53 57
54 -
    /**
55 -
     * @return {@code true} if the console is currently visible.
56 -
     */
57 -
    public boolean isVisible() {
58 -
        return visible;
59 -
    }
60 -
61 -
    /** Change visibility of the console GUI */
62 -
    public void setVisible(boolean v) {
63 -
        if (visible != v) {
64 -
            visible = v;
58 +
    @Override
59 +
    public void update(IEnvironment env, INativeInput input) {
60 +
        if (!env.getPref(NovelPrefs.DEBUG)) {
61 +
            return; // Debug mode not enabled
62 +
        }
65 63
66 -
            if (v) {
67 -
                show();
64 +
        if (input.consumePress(KeyCode.F1)) {
65 +
            if (isVisible()) {
66 +
                close();
68 67
            } else {
69 -
                hide();
68 +
                open(env.getContextManager());
70 69
            }
71 70
        }
72 71
    }
73 72
74 -
    private void show() {
73 +
    public void open(IContextManager contextManager) {
74 +
        this.contextManager = contextManager;
75 +
        visible = true;
76 +
75 77
        final Stage stage = sceneEnv.getStage();
76 78
        final Skin skin = sceneEnv.getSkin();
77 79
@@ -118,12 +120,11 @@
Loading
118 120
        layout.row();
119 121
        layout.add(inputField).bottom().expandX().fill();
120 122
        layout.add(inputButton).bottom().fill();
121 -
122 123
        layout.addListener(new InputListener() {
123 124
            @Override
124 125
            public boolean keyDown(InputEvent event, int keycode) {
125 126
                if (keycode == Keys.F1) {
126 -
                    setVisible(false);
127 +
                    close();
127 128
                    return true;
128 129
                }
129 130
                return false;
@@ -153,7 +154,8 @@
Loading
153 154
        eval(text);
154 155
    }
155 156
156 -
    private void hide() {
157 +
    public void close() {
158 +
        visible = false;
157 159
        if (layout != null) {
158 160
            layout.remove();
159 161
            layout = null;
@@ -203,8 +205,11 @@
Loading
203 205
        console.appendText("\n" + str);
204 206
    }
205 207
206 -
    public void setContext(@Nullable IContextManager contextManager) {
207 -
        this.contextManager = contextManager;
208 +
    /**
209 +
     * @return {@code true} if the console is currently visible.
210 +
     */
211 +
    public boolean isVisible() {
212 +
        return visible;
208 213
    }
209 214
210 215
}

@@ -11,7 +11,7 @@
Loading
11 11
    public long creationTime;
12 12
    public Storage userData;
13 13
    public ThumbnailInfoJson thumbnail;
14 -
    
14 +
15 15
    public static SaveFileHeader decode(SaveFileHeaderJson headerJson) throws SaveFormatException, IOException {
16 16
        if (headerJson.format != SaveFileConstants.FORMAT_VERSION) {
17 17
            throw new SaveFormatException("Unsupported save format: " + headerJson.format);
@@ -19,19 +19,21 @@
Loading
19 19
20 20
        // Return result
21 21
        SaveFileHeader header = new SaveFileHeader(headerJson.creationTime);
22 -
        header.setUserData(headerJson.userData);
22 +
        if (headerJson.userData != null) {
23 +
            header.setUserData(headerJson.userData);
24 +
        }
23 25
        if (headerJson.thumbnail != null) {
24 26
            header.setThumbnail(ThumbnailInfoJson.decode(headerJson.thumbnail));
25 27
        }
26 28
        return header;
27 29
    }
28 30
29 -
    public static SaveFileHeaderJson encode(SaveFileHeader header) {        
30 -
        SaveFileHeaderJson headerJson = new SaveFileHeaderJson();        
31 +
    public static SaveFileHeaderJson encode(SaveFileHeader header) {
32 +
        SaveFileHeaderJson headerJson = new SaveFileHeaderJson();
31 33
        headerJson.format = SaveFileConstants.FORMAT_VERSION;
32 34
        headerJson.creationTime = header.getCreationTime();
33 35
        headerJson.userData = new Storage(header.getUserData());
34 -
        
36 +
35 37
        ThumbnailInfo thumbnail = header.getThumbnail();
36 38
        if (thumbnail != null) {
37 39
            headerJson.thumbnail = ThumbnailInfoJson.encode(thumbnail);

@@ -66,6 +66,9 @@
Loading
66 66
    @Override
67 67
    public void update() {
68 68
        getSaveModule().processSaveLoadRequests();
69 +
        if (isDestroyed()) {
70 +
            return;
71 +
        }
69 72
70 73
        // Perform synchronous part of asset loading
71 74
        AssetManager assetManager = StaticEnvironment.ASSET_MANAGER.getIfPresent();
@@ -78,6 +81,7 @@
Loading
78 81
        }
79 82
80 83
        getContextManager().update();
84 +
        getSaveModule().processSaveLoadRequests();
81 85
    }
82 86
83 87
    /** Called when the global preferences change. */

@@ -1,8 +1,9 @@
Loading
1 1
package nl.weeaboo.vn.buildtools.project;
2 2
3 -
import java.io.File;
4 -
import java.io.FileInputStream;
5 3
import java.io.IOException;
4 +
import java.io.InputStream;
5 +
import java.nio.file.Files;
6 +
import java.nio.file.Path;
6 7
import java.util.Properties;
7 8
8 9
/**
@@ -26,9 +27,9 @@
Loading
26 27
     * @throws IOException If an I/O error occurs while trying to read the properties file.
27 28
     * @throws IllegalArgumentException If the properties file is malformed.
28 29
     */
29 -
    public static BuildProperties fromFile(File file) throws IOException {
30 +
    public static BuildProperties fromFile(Path file) throws IOException {
30 31
        Properties properties = new Properties();
31 -
        try (FileInputStream in = new FileInputStream(file)) {
32 +
        try (InputStream in = Files.newInputStream(file)) {
32 33
            properties.load(in);
33 34
        }
34 35
Files Complexity Coverage
api/src/main/java/nl/weeaboo/vn 91.53% 96.76%
buildtools/src/main/java/nl/weeaboo/vn/buildtools 83.33% 89.43%
core/src/main/java/nl/weeaboo/vn 82.27% 86.43%
Project Totals (369 files) 83.16% 87.49%
1
codecov:
2
  status:
3
    project: yes
4
    patch: no
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading