summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile69
-rw-r--r--README4
-rwxr-xr-xaencode.sh25
-rwxr-xr-xiencode.sh32
-rwxr-xr-xsetup.sh9
-rw-r--r--unrpyc/Makefile4
-rwxr-xr-xvencode.sh23
-rw-r--r--www/js/api.js235
8 files changed, 183 insertions, 218 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5b0b99d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,69 @@
+FFMPEG ?= ffmpeg
+FFMPEG += -v warning $(FFMPEGFLAGS)
+OPUSENC ?= opusenc
+ZOPFLIPNG ?= zopflipng
+JPEGOPTIM ?= jpegoptim
+CWEBP ?= cwebp
+
+DUMP ?= www/dump
+
+all: video audio images
+
+# === VIDEO ===
+
+VIDEO := $(wildcard $(DUMP)/video/*.mkv)
+MP4 := $(patsubst %.mkv,%.mp4,$(VIDEO))
+WEBM := $(patsubst %.mkv,%.webm,$(VIDEO))
+OGV := $(patsubst %.mkv,%.ogv,$(VIDEO))
+CVIDEO := $(MP4) $(WEBM) $(OGV)
+
+video: $(CVIDEO)
+
+%.mp4: %.mkv
+ $(FFMPEG) -i $< -c:v libx264 -preset slower -tune animation -c:a libfdk_aac $@
+
+%.webm: %.mkv
+ $(FFMPEG) -i $< -c:v libvpx -crf 15 -b:v 1M -c:a copy $@
+
+%.ogv: %.mkv
+ $(FFMPEG) -i $< -c:v libtheora -qscale:v 6 -c:a copy $@
+
+# === AUDIO ===
+
+AUDIO := $(shell find $(DUMP)/bgm $(DUMP)/sfx -name '*.ogg')
+OPUS := $(patsubst %.ogg,%.opus,$(AUDIO))
+M4A := $(patsubst %.ogg,%.m4a,$(AUDIO))
+CAUDIO := $(OPUS) $(M4A)
+
+audio: $(CAUDIO)
+
+%.wav: %.ogg
+ $(FFMPEG) -i $< -c:a pcm_s16le $@
+
+%.opus: %.wav
+ $(OPUSENC) --bitrate 64 $< $@
+
+%.m4a: %.wav
+ $(FFMPEG) -i $< -c:a libfdk_aac -vbr 2 $@
+
+# === IMAGES ===
+
+PNG := $(shell find $(DUMP) -name '*.png')
+JPG := $(shell find $(DUMP) -name '*.jpg')
+WEBP := $(patsubst %.png,%.webp,$(PNG)) \
+ $(patsubst %.jpg,%.webp,$(JPG))
+
+images: $(WEBP)
+
+%.webp: %.png
+ $(ZOPFLIPNG) -m -y $< $<
+ $(CWEBP) -q 99 -m 6 $< -o $@
+
+%.webp: %.jpg
+ $(JPEGOPTIM) --strip-all $<
+ $(CWEBP) -q 90 -m 6 $< -o $@
+
+clean:
+ rm $(CVIDEO) $(CAUDIO) $(WEBP)
+
+.PHONY: video audio images clean
diff --git a/README b/README
index 60fa4a2..69ef535 100644
--- a/README
+++ b/README
@@ -5,17 +5,17 @@ jQuery is not used, but when.js [1] is used as a sensible Promises/A+ implementa
== Requirements ==
- Katawa Shoujo (obviously)
- when.js
-- Bash
- Firefox/Chrome
- ffmpeg with AAC, libtheora, libvpx, libx264
- opus-tools
- libwebp
+- make (non-GNU untested)
How to use:
0. Get Katawa Shoujo.
1. Copy *.rpyc from Katawa Shoujo/game into unrpyc/ directory.
2. Extract files from data.rpa with an appropriate tool. Put files in www/dump.
-3. Run setup.sh [2]. Use environment variable THREADS to control number of threads/processes spawned.
+3. Run make.
4. Run nginx.sh [2] to start nginx with appropriate options for development, then connect to localhost:8080.
-- or --
diff --git a/aencode.sh b/aencode.sh
deleted file mode 100755
index af101be..0000000
--- a/aencode.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-
-# configure ffmpeg location if not in PATH
-FFMPEG=ffmpeg
-# configure flags
-FFMPEG_FLAGS=""
-
-set -e
-
-ffmpeg() {
- set -x
- command ${FFMPEG} -threads ${THREADS} ${FFMPEG_FLAGS} "$@"
- set +x
-}
-
-for d in bgm sfx; do
- pushd $(dirname $0)/www/dump/${d}
- for f in *.ogg; do
- OUT=${f%.ogg}
- ffmpeg -y -i $f -c:a pcm_s16le ${OUT}.wav
- opusenc --bitrate 64 ${OUT}.wav ${OUT}.opus
- ffmpeg -n -i ${OUT}.wav -c:a libfdk_aac -vbr 2 ${OUT}.m4a || true
- done
- popd
-done
diff --git a/iencode.sh b/iencode.sh
deleted file mode 100755
index ac898d7..0000000
--- a/iencode.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-
-# configure cwebp location if not in PATH
-CWEBP=cwebp
-# configure flags
-CWEBP_FLAGS="-m 6"
-
-set -e
-
-cd $(dirname $0)/www/dump
-
-iencode() {
- EXT="$1"
- QUAL="$2"
- export EXT QUAL CWEBP CWEBP_FLAGS
- set -x
- find . -name \*."${EXT}" -print0 | xargs -0 -P ${THREADS} -n 1 bash -c '
- IN="$0"
- OUT="${IN%.${EXT}}.webp"
- [[ -f ${OUT} ]] || ${CWEBP} -q "${QUAL}" ${CWEBP_FLAGS} ${IN} -o ${OUT}
- '
-}
-
-iencode jpg 90
-iencode png 99
-
-if hash zopflipng; then
- find . -name \*.png -print0 | xargs -0 -I '{}' -P ${THREADS} zopflipng -m -y '{}' '{}'
-else
- echo >&2 "Install zopfli (https://code.google.com/p/zopfli/) to improve PNG compression."
- echo >&2 "KS 1.1 .png files reduce by ~2.6M (~1.6%) with -m, taking several hours."
-fi
diff --git a/setup.sh b/setup.sh
index bb3c7c0..ff4fda9 100755
--- a/setup.sh
+++ b/setup.sh
@@ -1,7 +1,5 @@
#!/bin/bash
-export THREADS=${THREADS:-$(nproc)}
-
cd $(dirname $0)
git submodule update --init
@@ -10,8 +8,6 @@ pushd unrpyc
make install
popd
-./vencode.sh
-
trim() {
convert -trim "$@" "$@"
optipng -o7 "$@"
@@ -20,7 +16,6 @@ trim() {
trim www/dump/ui/bt-cf-unchecked.png
trim www/dump/ui/bt-cf-checked.png
-convert www/dump/ui/icon.png -resize 256x256 -transparent white www/favicon.ico
+make
-./iencode.sh
-./aencode.sh
+convert www/dump/ui/icon.png -resize 256x256 -transparent white www/favicon.ico
diff --git a/unrpyc/Makefile b/unrpyc/Makefile
index 431e10b..a92a91d 100644
--- a/unrpyc/Makefile
+++ b/unrpyc/Makefile
@@ -1,9 +1,9 @@
-gzip := $(shell ./find-gzip.sh)
+GZIP := $(shell ./find-gzip.sh)
all: script.json script.json.gz imachine.json imachine.json.gz imachine_replay.json imachine_replay.json.gz ui-strings.json ui-strings.json.gz
%.json.gz: %.json
- $(gzip) -c $< > $@
+ $(GZIP) -c $< > $@
touch $< $@
script.json: $(patsubst %.rpyc,%.json,$(wildcard script-*.rpyc))
diff --git a/vencode.sh b/vencode.sh
deleted file mode 100755
index 2b8233e..0000000
--- a/vencode.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-
-# configure ffmpeg location if not in PATH
-FFMPEG=ffmpeg
-# configure flags
-FFMPEG_FLAGS=""
-
-set -e
-
-cd $(dirname $0)/www/dump/video
-
-ffmpeg() {
- set -x
- command ${FFMPEG} -threads ${THREADS} ${FFMPEG_FLAGS} "$@"
- set +x
-}
-
-for f in *.mkv; do
- OUT=${f%.mkv}
- ffmpeg -i $f -c:v libx264 -preset slower -tune animation -c:a libfdk_aac ${OUT}.mp4
- ffmpeg -i $f -c:v libvpx -crf 15 -b:v 1M -c:a copy ${OUT}.webm
- ffmpeg -i $f -c:v libtheora -qscale:v 6 -c:a copy ${OUT}.ogv
-done
diff --git a/www/js/api.js b/www/js/api.js
index 4aa6f5f..869b1cf 100644
--- a/www/js/api.js
+++ b/www/js/api.js
@@ -1,85 +1,34 @@
"use strict";
window.html5ks.api = {
init: function () {
-
- // tag character names
var chars = html5ks.data.characters;
for (var ch in chars) {
- var chr = chars[ch];
- if (chr.name) {
- chr.name = this.tag(chr.name);
+ var char = chars[ch];
+ if (char.name) {
+ char.name = this.tag(char.name);
}
}
-
- },
-
-
- iscene: function (target, is_h, is_end) {
-
- // TODO: implement timeskip
- if (target === "timeskip") {
- deferred.resolve();
- return deferred.promise;
- }
-
- html5ks.state.status = "scene";
-
- var deferred = when.defer(),
- label = html5ks.data.script[html5ks.persistent.language + "_" + target],
- i = 0;
-
- var run = function (ret) {
- if (label[i]) {
- this.runInst(label[i++]).then(run, console.error);
- } else {
- deferred.resolve(ret);
- }
- }.bind(this);
-
- run();
-
- return deferred.promise;
},
-
- runInst: function (inst) {
- var cmd = inst[0].replace(/"/g, ''),
- args = inst.slice(1);
- if (html5ks.data.characters[cmd]) {
- return this.character(cmd, args[0]);
- } else {
- if (this[cmd]) {
- return this[cmd].apply(this, args);
- } else if (inst.length === 1) {
- return this.character("name_only", cmd);
- } else if (/^[A-Z]/.test(cmd)) {
- return this.character(cmd, args[0]);
- } else {
- console.error("no such cmd " + cmd);
- return when.defer().resolve();
- }
- }
- },
-
- _fades: {},
+ _fading: {},
set_volume: function (target, delay, channel) {
var deferred = when.defer(),
- audioElement = html5ks.elements.audio[channel],
- step = (target - audioElement.volume) / (delay * 20);
+ audio = html5ks.elements.audio[channel],
+ step = (target - audio.volume) / (delay * 20);
if (!delay) {
- audioElement.volume = target;
+ audio.volume = target;
} else {
- if (this._fades[channel]) clearInterval(this._fades[channel]);
- this._fades[channel] = setInterval(function () {
+ this._fading[channel] = setInterval(function () {
// clamp new volume 0-1
- audioElement.volume = Math.min(Math.max(audioElement.volume + step, 0), 1);
- if (audioElement.volume === 0 || audioElement.volume === 1) {
- clearInterval(this._fades[channel]);
+ audio.volume = Math.min(Math.max(audio.volume + step, 0), 1);
+ if (audio.volume === 0 || audio.volume === 1) {
+ clearInterval(this._fading);
}
}.bind(this), 50);
}
- return deferred.resolve();
+ deferred.resolve();
+ return deferred.promise;
},
play: function (channel, name, ignore, fade) {
@@ -102,12 +51,17 @@ window.html5ks.api = {
src += "sfx/" + html5ks.data.sfx[name];
}
- ["opus", "ogg", "m4a", "wav"].some(function (type) {
- if (Modernizr.audio[type]) {
- audio.src = src + "." + type;
- return true;
- }
- });
+ if (Modernizr.audio.opus) {
+ audio.src = src + ".opus";
+ } else if (Modernizr.audio.ogg) {
+ audio.src = src + ".ogg";
+ } else if (Modernizr.audio.m4a) {
+ audio.src = src + ".m4a";
+ } else if (Modernizr.audio.wav) {
+ audio.src = src + ".wav";
+ } else {
+ console.error("wtf, no audio formats");
+ }
audio.load();
var volume = html5ks.persistent[channel + "Volume"];
@@ -128,19 +82,15 @@ window.html5ks.api = {
stop: function (channel, ignore, fade) {
if (channel === "all") {
- return ["music", "sound", "ambient"].forEach(function (channel) {
- this.stop(channel, ignore, fade);
- }.bind(this));
+ this.stop("music", ignore, fade);
+ this.stop("sound", ignore, fade);
+ return this.stop("ambient", ignore, fade);
}
-
var deferred = when.defer(),
audio = html5ks.elements.audio[channel];
-
- // clear fade
- if (this._fades[channel]) {
- clearInterval(this._fades[channel]);
+ if (this._fading[channel]) {
+ clearInterval(this._fading[channel]);
}
-
if (fade) {
this.set_volume(0, fade, channel);
} else {
@@ -153,31 +103,27 @@ window.html5ks.api = {
movie_cutscene: function (vid_src, skippable) {
var deferred = when.defer(),
- video = html5ks.elements.video;
+ video = html5ks.elements.video,
+ src = "dump/video/" + vid_src + ".";
this.stop("all");
this.speed("auto", false);
this.speed("skip", false);
- var types = {
- webm: "webm",
- ogg: "ogv",
- h264: "mp4"
- };
- for (var type in types) {
- if (Modernizr.video[type]) {
- video.src = "dump/video/" + vid_src + "." + types[type];
- break;
- }
+ if (Modernizr.video.webm) {
+ video.src = src + "webm";
+ } else if (Modernizr.video.ogg) {
+ video.src = src + "ogv";
+ } else if (Modernizr.video.h264) {
+ video.src = src + "mp4";
+ } else {
+ console.error("wtf is this, no video formats");
}
- video.oncanplaythrough = function () {
- video.volume = html5ks.persistent.musicVolume;
- video.style.display = "block";
- video.play();
- video.onended = done;
- };
-
+ video.load();
+ video.style.display = "block";
+ video.volume = html5ks.persistent.musicVolume;
+ video.play();
var done = function () {
video.style.display = "none";
video.pause();
@@ -195,13 +141,10 @@ window.html5ks.api = {
done();
}
};
-
+ video.onended = done;
video.onerror = function () {
deferred.reject(this.error);
};
-
- video.load();
-
return deferred.promise;
},
@@ -211,19 +154,60 @@ window.html5ks.api = {
},
+ iscene: function (target, is_h, is_end) {
+ html5ks.state.status = "scene";
+ var deferred = when.defer(),
+ label = html5ks.data.script[html5ks.persistent.language + "_" + target],
+ i = 0;
+ (function run(ret) {
+ if (label[i]) {
+ html5ks.api.runInst(label[i]).then(run, console.error);
+ i++;
+ } else {
+ deferred.resolve(ret);
+ }
+ }());
+ return deferred.promise;
+ },
+
+
+ runInst: function (inst) {
+ var cmd = inst[0].replace(/"/g, ''),
+ args = inst.slice(1);
+ if (html5ks.data.characters[cmd]) {
+ return this.character(cmd, args[0]);
+ } else {
+ if (this[cmd]) {
+ return this[cmd].apply(this, args);
+ } else if (inst.length === 1) {
+ return this.character("name_only", cmd);
+ } else if (/^[A-Z]/.test(cmd)) {
+ return this.character(cmd, args[0]);
+ } else {
+ console.error("no such cmd " + cmd);
+ var deferred = when.defer();
+ deferred.resolve();
+ return deferred.promise;
+ }
+ }
+ },
+
+
window: function (action, transition) {
- var windowEl = html5ks.elements.window;
+ var windw = html5ks.elements.window,
+ deferred = when.defer();
switch (action) {
case "show":
- windowEl.style.display = "block";
+ windw.style.display = "block";
break;
case "hide":
- windowEl.style.display = "none";
+ windw.style.display = "none";
break;
default:
- return windowEl.style.display !== "none";
+ return windw.style.display !== "none";
}
- return when.defer().resolve(action);
+ deferred.resolve(action);
+ return deferred.promise;
},
@@ -322,20 +306,18 @@ window.html5ks.api = {
}
return deferred.promise;
},
-
hide: function (name) {
var deferred = when.defer();
var show = html5ks.elements.show.children;
for (var i = show.length - 1; i >= 0; i--) {
if (show[i].id === name) {
- show[i].style.display = "none";
- deferred.resolve();
- return deferred.promise;
+ html5ks.elements.show.removeChild(show[i]);
}
}
+ deferred.resolve();
+ return deferred.promise;
},
-
tag: function (str) {
var tags = [
/&/g, "&amp;",
@@ -364,29 +346,29 @@ window.html5ks.api = {
character: function (charName, str, extend) {
var deferred = when.defer(),
text = this.tag(str),
- chr = typeof charName === "string" ? html5ks.data.characters[charName] : charName,
+ char = typeof charName === "string" ? html5ks.data.characters[charName] : charName,
w = /{w=?(\d*\.\d*)?}(.*)/.exec(str);
- if (!chr) {
- chr = {
+ if (!char) {
+ char = {
name: charName
};
}
- if (typeof chr.what_prefix === "undefined") {
- chr.what_prefix = "“";
- chr.what_suffix = "”";
+ if (typeof char.what_prefix === "undefined") {
+ char.what_prefix = "“";
+ char.what_suffix = "”";
}
- this._lastchar = chr;
+ this._lastchar = char;
- if (!extend && chr.what_prefix) {
- text = chr.what_prefix + text;
+ if (!extend && char.what_prefix) {
+ text = char.what_prefix + text;
}
- if ((!w || !w[1]) && chr.what_suffix) {
- text = text + chr.what_suffix;
+ if ((!w || !w[1]) && char.what_suffix) {
+ text = text + char.what_suffix;
}
- if (chr.kind === "nvl") {
+ if (char.kind === "nvl") {
html5ks.elements.nvlsay.innerHTML += "<span class='nvl-block'>" + text + "</span>";
html5ks.elements.nvlctc.style.display = "block";
html5ks.next = function () {
@@ -397,9 +379,9 @@ window.html5ks.api = {
} else {
var who = html5ks.elements.who;
if (!extend) {
- who.innerHTML = chr.name;
- if (chr.color) {
- who.style.color = chr.color;
+ who.innerHTML = char.name;
+ if (char.color) {
+ who.style.color = char.color;
} else {
who.style.color = "#ffffff";
}
@@ -487,7 +469,6 @@ window.html5ks.api = {
},
menu: function (choices) {
- var args = Array.prototype.slice.call(arguments, 0);
var deferred = when.defer();
var menu = html5ks.elements.choices,
frag = document.createDocumentFragment(),