//Temporary buffers for unzip\r
private static final byte infBuffer[] = new byte[64 * 1024];\r
private static final byte readBuffer[] = new byte[64 * 1024];\r
- \r
- public XP3Extractor() { \r
+\r
+ public XP3Extractor() {\r
}\r
- \r
+\r
//Functions\r
public static final int read_s32(InputStream in) throws IOException {\r
return (int)readLE(in, 4);\r
}\r
return result;\r
}\r
- \r
+\r
/**\r
* Warning: dst should use ascii-only in its pathname\r
*/\r
public void extract(String archive, String dst, ProgressListener pl) throws IOException {\r
Log.v("Extracting " + archive);\r
- \r
+\r
FileInputStream fin = new FileInputStream(archive);\r
FileChannel fc = fin.getChannel();\r
- \r
+\r
int origSize;\r
File uncompressedFile = new File(dst+"/__temp__.dat");\r
uncompressedFile.getParentFile().mkdirs();\r
- \r
+\r
{\r
byte signature[] = new byte[] {(byte)'X', (byte)'P', (byte)'3',\r
(byte)0x0D, (byte)0x0A, (byte) ' ', (byte)0x0A,\r
throw new IOException("FileFormat error");\r
}\r
}\r
- \r
+\r
int indexOffset = (int)read_s64(fin);\r
if (indexOffset > fc.size()) throw new IOException("FileFormat error");\r
fc.position(indexOffset);\r
- \r
+\r
boolean compression = readLE(fin, 1) != 0;\r
if (compression) {\r
int compSize = (int)read_s64(fin);\r
origSize = (int)read_s64(fin);\r
- \r
+\r
if (indexOffset+compSize+17 != fc.size()) throw new IOException("FileFormat error");\r
- \r
+\r
int uncompressedL = -1;\r
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(uncompressedFile));\r
try {\r
} finally {\r
bout.close();\r
}\r
- \r
+\r
if (uncompressedL != origSize) throw new IOException("FileFormat error");\r
} else {\r
origSize = (int)read_s64(fin);\r
\r
FileInputStream uncompressedIn = new FileInputStream(uncompressedFile);\r
FileChannel uncompressedC = uncompressedIn.getChannel();\r
- \r
+\r
byte out[] = new byte[1024 * 1024];\r
int outL = 0;\r
\r
int read = 0;\r
while (uncompressedC.position() < origSize) {\r
Entry entry = readEntry(uncompressedIn);\r
- \r
+\r
File outFile = new File(String.format("%s/%s", dst, entry.file));\r
outFile.getParentFile().mkdirs();\r
\r
outL = 0;\r
t++;\r
- \r
- if (pl != null && (t & 0xFF) == 0) { \r
+\r
+ if (pl != null && (t & 0xFF) == 0) {\r
pl.onProgress(read, (int)fc.size(), "");\r
}\r
- \r
+\r
//Log.verbose("[write] " + outFile.getAbsolutePath());\r
//Benchmark.tick();\r
- \r
+\r
//Write segments to seperate files\r
int totalSize = 0;\r
for (Segment segment : entry.segments) {\r
if (out.length < totalSize) {\r
out = new byte[totalSize];\r
}\r
- \r
+\r
for (Segment segment : entry.segments) {\r
fc.position(segment.offset);\r
\r
\r
read += segment.compSize;\r
}\r
- \r
- //Decrypt \r
+\r
+ //Decrypt\r
if (entry.encrypted) {\r
decrypt(outFile.getName(), out, outL);\r
}\r
- \r
+\r
try {\r
if (outFile.getName().endsWith(".tlg")) {\r
if (out.length >= 2 && out[0] == 'B' && out[1] == 'M') {\r
Process p = ProcessUtil.execInDir(\r
String.format("tlg2bmp \"%s\" \"%s\"",\r
tlgTemp, bmpTemp),\r
- "tools/");\r
+ "");\r
ProcessUtil.waitFor(p);\r
ProcessUtil.kill(p);\r
- \r
+\r
outFile.delete();\r
new File(tlgTemp).delete();\r
new File(bmpTemp).renameTo(new File(StringUtil.stripExtension(outFile.getAbsolutePath())+".bmp"));\r
*/\r
- \r
+\r
FileUtil.writeBytes(outFile, out, 0, outL);\r
}\r
} else {\r
Log.w(ioe.toString());\r
}\r
}\r
- \r
+\r
//Benchmark.tock(outFile.getName() + " %s");\r
}\r
- \r
+\r
uncompressedC.close();\r
uncompressedIn.close();\r
\r
fin.close();\r
\r
uncompressedFile.delete();\r
- \r
+\r
if (pl != null) pl.onFinished(archive + " fully extracted");\r
}\r
- \r
+\r
static synchronized int unzip(InputStream in, byte out[], int inL) throws IOException {\r
Inflater inf = new Inflater();\r
- \r
+\r
int read = 0;\r
int inflated = 0;\r
try {\r
if (r == -1) {\r
throw new EOFException("Unexpected end of ZLIB input stream");\r
}\r
- read += r; \r
+ read += r;\r
inf.setInput(readBuffer, 0, r);\r
}\r
}\r
throw new IOException(e);\r
}\r
}\r
- \r
+\r
static synchronized int unzip(InputStream in, OutputStream out, int inL) throws IOException {\r
Inflater inf = new Inflater();\r
- \r
+\r
int read = 0;\r
int inflated = 0;\r
try {\r
if (r == -1) {\r
throw new EOFException("Unexpected end of ZIP input stream");\r
}\r
- read += r; \r
+ read += r;\r
inf.setInput(readBuffer, 0, r);\r
}\r
}\r
throw new IOException(e);\r
}\r
}\r
- \r
+\r
@SuppressWarnings("unused")\r
protected Entry readEntry(InputStream in) throws IOException {\r
Entry entry = new Entry();\r
- \r
+\r
byte temp[] = new byte[4];\r
- \r
+\r
in.read(temp);\r
if (!new String(temp).equals("File")) throw new IOException("FileFormat error :: " + new String(temp));\r
int entryLength = (int)read_s64(in);\r
- \r
+\r
in.read(temp);\r
if (!new String(temp).equals("info")) throw new IOException("FileFormat error");\r
int infoLength = (int)read_s64(in);\r
- \r
+\r
entry.encrypted = read_s32(in) != 0;\r
int origSize = (int)read_s64(in);\r
int compSize = (int)read_s64(in);\r
- \r
+\r
int filenameL = (int)readLE(in, 2);\r
- //System.err.println(origSize + " " + compSize + " " + new String(temp) + " " + filenameL); \r
+ //System.err.println(origSize + " " + compSize + " " + new String(temp) + " " + filenameL);\r
if (infoLength != filenameL*2+22) throw new IOException("FileFormat error");\r
- \r
+\r
char filename[] = new char[filenameL];\r
for (int n = 0; n < filenameL; n++) {\r
filename[n] = (char)readLE(in, 2);\r
}\r
- entry.file = new String(filename); \r
- \r
+ entry.file = new String(filename);\r
+\r
in.read(temp);\r
if (!new String(temp).equals("segm")) throw new IOException("FileFormat error");\r
int numSegments = ((int)read_s64(in)) / 28;\r
s.offset = (int)read_s64(in);\r
s.origSize = (int)read_s64(in);\r
s.compSize = (int)read_s64(in);\r
- \r
+\r
entry.segments[n] = s;\r
}\r
\r
\r
return entry;\r
}\r
- \r
+\r
public void decrypt(String filename, byte data[], int dataL) {\r
if (dataL > 0x13) {\r
data[0x13] ^= 1;\r
}\r
- if (dataL > 0x2ea29) { \r
+ if (dataL > 0x2ea29) {\r
data[0x2ea29] ^= 3;\r
}\r
\r
data[n] ^= 0x36;\r
}\r
}\r
- \r
+\r
//Getters\r
- \r
+\r
//Setters\r
- \r
+\r
//Inner Classes\r
private static class Entry {\r
public String file;\r
public boolean compressed;\r
public int offset;\r
public int origSize;\r
- public int compSize; \r
+ public int compSize;\r
}\r
- \r
+\r
}\r
import nl.weeaboo.vnds.tools.SoundConverter;\r
\r
public class FateResourceConverter extends AbstractResourceConverter {\r
- \r
+\r
public FateResourceConverter() {\r
}\r
- \r
+\r
//Functions\r
public static void main(String args[]) {\r
System.setProperty("line.separator", "\n");\r
- \r
+\r
FateResourceConverter e = new FateResourceConverter();\r
try {\r
e.parseCommandLine(args, 2);\r
} catch (IOException ioe) {\r
printUsage(e.getClass());\r
return;\r
- } \r
- \r
+ }\r
+\r
try {\r
e.extract(args[0], args[1]);\r
} catch (IOException ioe) {\r
\r
//Extract game data\r
FateExtractor.main(new String[] {src, originalF.getAbsolutePath()});\r
- \r
+\r
//Clean up _generated folder\r
initOutputFolder(generatedF);\r
- \r
+\r
//Convert\r
\r
convertBackground(dstF);\r
convertForeground(dstF);\r
convertSound(dstF);\r
convertMusic(dstF);\r
- \r
+\r
//Template\r
File templateF = new File("template/fate");\r
copyTemplate(templateF, generatedF);\r
- \r
+\r
//Done\r
Log.v("Done");\r
}\r
- \r
- public void convertBackground(final File root) { \r
+\r
+ public void convertBackground(final File root) {\r
Log.v("Converting backgrounds...");\r
- \r
+\r
final ImageConverter ic = createBackgroundConverter();\r
- \r
+\r
Map<String, File> files = new HashMap<String, File>();\r
FileUtil.collectFiles(files, new File(root, "/_original/bgimage"), false, false, new FileCollectFilter() {\r
public boolean accept(String relpath, File file) {\r
return relpath.endsWith("tlg") || relpath.endsWith("bmp");\r
}\r
});\r
- \r
+\r
BatchProcess bp = createBatch();\r
try {\r
bp.run(files, new FileOp() {\r
Log.w("Batch Process Interrupted");\r
}\r
}\r
- \r
+\r
public void convertForeground(final File root) {\r
Log.v("Converting sprites...");\r
- \r
+\r
final ImageConverter ic = createForegroundConverter();\r
ic.setScalingType(ScalingType.SPRITE);\r
- \r
+\r
Map<String, File> files = new HashMap<String, File>();\r
FileUtil.collectFiles(files, new File(root, "/_original/fgimage"), false, false, new FileCollectFilter() {\r
public boolean accept(String relpath, File file) {\r
return relpath.endsWith("tlg") || relpath.endsWith("bmp");\r
}\r
});\r
- \r
+\r
BatchProcess bp = createBatch();\r
try {\r
bp.run(files, new FileOp() {\r
\r
protected static File convertTLG(File tlgF) throws IOException {\r
String hash = "__" + Long.toHexString(Thread.currentThread().getId()) + "__";\r
- \r
+\r
File tempTLG = new File(tlgF.getParentFile(), hash + ".tlg");\r
File tempBMP = new File(tlgF.getParentFile(), hash + ".bmp");\r
- \r
+\r
File bmpF = new File(StringUtil.replaceExt(tlgF.getAbsolutePath(), "bmp"));\r
bmpF.delete();\r
- \r
+\r
tlgF.renameTo(tempTLG);\r
- \r
+\r
try {\r
Process p = ProcessUtil.execInDir(\r
String.format("tlg2bmp \"%s\" \"%s\"",\r
tempTLG.getAbsolutePath(), tempBMP.getAbsolutePath()),\r
- "tools/");\r
+ "");\r
ProcessUtil.waitFor(p);\r
} finally {\r
tempTLG.delete();\r
- tempBMP.renameTo(bmpF); \r
+ tempBMP.renameTo(bmpF);\r
}\r
- \r
+\r
return bmpF;\r
}\r
- \r
+\r
public void convertSound(File root) {\r
final File targetFolder = new File(root, "_generated/sound");\r
- \r
+\r
try {\r
Log.v("Converting SFX...");\r
final SoundConverter sc = createSFXEncoder();\r
- \r
+\r
Map<String, File> files = new HashMap<String, File>();\r
FileUtil.collectFiles(files, new File(root, "/_original/sound"), false, false, new FileCollectFilter() {\r
public boolean accept(String relpath, File file) {\r
} catch (InterruptedException ie) {\r
Log.w("Batch Process Interrupted");\r
}\r
- \r
- if (convertVoice) { \r
+\r
+ if (convertVoice) {\r
Log.v("Converting Voice...");\r
final SoundConverter sc = createVoiceEncoder();\r
- \r
+\r
Map<String, File> files = new HashMap<String, File>();\r
FileUtil.collectFiles(files, new File(root, "/_original/patch6"), false, false, new FileCollectFilter() {\r
public boolean accept(String relpath, File file) {\r
\r
Log.v("Converting music...");\r
final SoundConverter sc = createMusicEncoder();\r
- \r
- //Convert music \r
+\r
+ //Convert music\r
Map<String, File> files = new HashMap<String, File>();\r
FileUtil.collectFiles(files, new File(root, "/_original/bgm"), false, false, new FileCollectFilter() {\r
public boolean accept(String relpath, File file) {\r
}\r
\r
//Getters\r
- \r
+\r
//Setters\r
- \r
+\r
}\r
\r
public class RealtaNuaSoundExtractor {\r
\r
- public RealtaNuaSoundExtractor() { \r
+ public RealtaNuaSoundExtractor() {\r
}\r
- \r
+\r
//Functions\r
public static void main(String args[]) {\r
RealtaNuaSoundExtractor se = new RealtaNuaSoundExtractor();\r
e.printStackTrace();\r
}\r
}\r
- \r
+\r
public static final int read_s32(InputStream in) throws IOException {\r
return (int)readLE(in, 4);\r
}\r
public void extract(String discRoot, String outFolder) throws IOException {\r
File outFolderFile = new File(outFolder);\r
outFolderFile.mkdirs();\r
- \r
- File newExe = new File(outFolder+"/ahx2wav.exe");\r
- FileUtil.copyFile(new File("tools/ahx2wav_v014/ahx2wav.exe"), newExe);\r
+\r
+ File newExe = new File("bin/ahx2wav");\r
+ FileUtil.copyFile(new File("bin/ahx2wav"), newExe);\r
FileInputStream fin = new FileInputStream(discRoot+"/data0.bin");\r
FileChannel fc = fin.getChannel();\r
- \r
+\r
Log.v("Extracting AHX sound files...");\r
- \r
+\r
try {\r
byte sig[] = new byte[] {(byte)'A', (byte)'F', (byte)'S', (byte)'\0'};\r
byte arcsig[] = new byte[4];\r
for (int n = 0; n < 4; n++) {\r
if (arcsig[n] != sig[n]) throw new IOException("FileFormat Error");\r
}\r
- \r
+\r
//0x808 -- file offset/size table offset\r
//0x466C3000 -- filename table offset\r
//0x00159660 -- filename table length\r
files[n].offset = 0x800 + read_s32(fin);\r
files[n].length = read_s32(fin);\r
}\r
- \r
+\r
byte nameBuffer[] = new byte[32];\r
for (int n = 0; n < filesL; n++) {\r
fc.position(0x466C3000 + 0x30 * n);\r
while (l < nameBuffer.length && nameBuffer[l] != '\0') l++;\r
files[n].filename = new String(nameBuffer, 0, l);\r
}\r
- \r
+\r
for (int n = 0; n < filesL; n++) {\r
if (n % 256 == 0) {\r
Log.v(String.format("(%d/%d) %s...", n+1, filesL, files[n].filename));\r
}\r
- \r
+\r
File outFile = new File(outFolder+'/'+files[n].filename);\r
FileOutputStream fout = new FileOutputStream(outFile);\r
int r = 0;\r
r += fc.transferTo(files[n].offset+r, files[n].length-r, fout.getChannel());\r
}\r
fout.flush();\r
- fout.close(); \r
+ fout.close();\r
\r
//Convert to wav\r
Process p = ProcessUtil.execInDir(String.format(\r
throw new IOException("Error converting file: " + outFile.getAbsolutePath() + "\nAborting sound extraction.");\r
}\r
ProcessUtil.kill(p);\r
- \r
+\r
//Delete original\r
- outFile.delete(); \r
+ outFile.delete();\r
}\r
- \r
- //Rename a.b.wav to a.wav \r
+\r
+ //Rename a.b.wav to a.wav\r
File converted[] = outFolderFile.listFiles();\r
for (File f : converted) {\r
String filename = f.getName();\r
newExe.delete();\r
}\r
}\r
- \r
+\r
//Getters\r
- \r
+\r
//Setters\r
\r
//Inner Classes\r
\r
public static String ffmpeg = "ffmpeg";\r
\r
- private VNSoundUtil() { \r
+ private VNSoundUtil() {\r
}\r
- \r
+\r
public static File padAudioFile(File src, File dst) throws IOException {\r
File tempF = new File(dst.getParent(), stripExtension(dst.getName())+".temp");\r
tempF.getParentFile().mkdirs();\r
- \r
+\r
try {\r
//Decode to raw PCM\r
\r
}\r
ProcessUtil.waitFor(p);\r
ProcessUtil.kill(p);\r
- \r
+\r
{\r
byte[] bytes = FileUtil.readBytes(tempF);\r
boolean isShort = bytes.length <= 10 * (44100*2*2);\r
float fadeSeconds = (isShort ? 1 : 3);\r
float padSeconds = (isShort ? 3 : 0);\r
- \r
+\r
ShortBuffer[] samples = deinterleave(bytes, 2);\r
fadeInOut(samples, Math.round(fadeSeconds * 44100),\r
Math.round(fadeSeconds * 44100));\r
bytes = interleave(samples);\r
- \r
- //Pad raw PCM with data \r
+\r
+ //Pad raw PCM with data\r
FileOutputStream fout = new FileOutputStream(tempF);\r
try {\r
fout.write(bytes);\r
fout.close();\r
}\r
}\r
- \r
+\r
//Encode to target format\r
p = ProcessUtil.execInDir(String.format(\r
"ffmpeg -y -f s16le -acodec pcm_s16le -ac 2 -ar 44100 -i \"%s\" \"%s\"",\r
tempF.getAbsolutePath(), dst.getAbsolutePath()),\r
- "tools");\r
+ "");\r
ProcessUtil.waitFor(p);\r
ProcessUtil.kill(p);\r
} finally {\r
tempF.delete();\r
}\r
- \r
+\r
return dst;\r
}\r
- \r
+\r
public static void fadeInOut(ShortBuffer[] channels, int inDuration, int outDuration) {\r
for (ShortBuffer ch : channels) {\r
inDuration = Math.min(ch.limit()/2, inDuration);\r
}\r
}\r
}\r
- \r
+\r
protected static ShortBuffer[] deinterleave(byte[] srcArray, int channels) {\r
ByteBuffer src = ByteBuffer.wrap(srcArray);\r
src.order(ByteOrder.LITTLE_ENDIAN);\r
ShortBuffer ssrc = src.asShortBuffer();\r
- \r
+\r
ShortBuffer[] dst = new ShortBuffer[channels];\r
for (int n = 0; n < channels; n++) {\r
dst[n] = ShortBuffer.allocate(ssrc.limit() / channels);\r
}\r
- \r
+\r
while (ssrc.remaining() > 0) {\r
for (ShortBuffer buf : dst) {\r
buf.put(ssrc.get());\r
for (ShortBuffer buf : dst) {\r
buf.rewind();\r
}\r
- \r
+\r
return dst;\r
}\r
\r
oldpos[n] = src[n].position();\r
len += src[n].limit();\r
}\r
- \r
+\r
ByteBuffer dst = ByteBuffer.allocate(2 * len);\r
dst.order(ByteOrder.LITTLE_ENDIAN);\r
ShortBuffer sdst = dst.asShortBuffer();\r
- \r
+\r
try {\r
while (sdst.remaining() > 0) {\r
for (ShortBuffer buf : src) {\r
}\r
}\r
dst.rewind();\r
- \r
+\r
return dst.array();\r
}\r
\r
import nl.weeaboo.vnds.VNImageUtil;\r
\r
public class ImageConverter {\r
- \r
+\r
public enum ConvertType {\r
TYPE_RAW_RGBA("RAW RGBA", "dta"),\r
TYPE_RAW_RGB256("RAW RGB256", "dta"),\r
TYPE_PNG("PNG", "png"),\r
TYPE_PNG_256_NEUQUANT("PNG 256 (neuquant)", "png"),\r
TYPE_PNG_256_MEDIAN("PNG 256 (median)", "png");\r
- \r
+\r
private String label;\r
private String fileExt;\r
- \r
+\r
private ConvertType(String label, String fileExt) {\r
this.label = label;\r
this.fileExt = fileExt;\r
}\r
- \r
+\r
public String getFileExt() { return fileExt; }\r
public String toString() { return label; }\r
}\r
- \r
+\r
public enum ScalingType {\r
NONE("None"), BACKGROUND("Background"), SPRITE("Sprite"), STRETCH("Stretch");\r
\r
String label;\r
- \r
+\r
private ScalingType(String label) {\r
this.label = label;\r
}\r
- \r
+\r
public String toString() { return label; }\r
}\r
- \r
+\r
public enum DitheringType {\r
NONE("None"), RANDOM("Random"), FLOYD_STEINBERG("Floyd-Steinberg");\r
- \r
+\r
String label;\r
- \r
+\r
private DitheringType(String label) {\r
this.label = label;\r
}\r
- \r
+\r
public String toString() { return label; }\r
}\r
- \r
+\r
private int maxThreads = 8;\r
private ConvertType mode = ConvertType.TYPE_RAW_RGBA;\r
private ScalingType scaling = ScalingType.NONE;\r
private int quality = 98; //JPG Only\r
private DitheringType dithering = DitheringType.NONE;\r
private boolean log;\r
- \r
+\r
//Temporary vars\r
private Map<File, StringBuilder> processLogs;\r
- \r
+\r
//Functions\r
protected static void printUsage() {\r
System.err.printf("Usage: java -jar ImageConverter.jar <flags> <file>\nflags:"\r
+ "\n\t-png <RGBA|256>"\r
+ "\n\t-jpg <quality (0-100)>"\r
// + "\n\t-size <width> <height>"\r
- + "\n"); \r
+ + "\n");\r
}\r
\r
public static void main(String args[]) throws IOException {\r
}\r
} else if (args[n].startsWith("-threads")) {\r
ic.maxThreads = Integer.parseInt(args[++n]);\r
-/* \r
+/*\r
} else if (args[n].startsWith("-size")) {\r
int w = Integer.parseInt(args[++n]);\r
int h = Integer.parseInt(args[++n]);\r
ic.setBackgroundSize(w, h);\r
-*/ \r
+*/\r
} else if (filename == null) {\r
filename = args[n];\r
} else {\r
printUsage();\r
return;\r
}\r
- \r
+\r
if (filename == null) {\r
printUsage();\r
return;\r
}\r
- \r
+\r
ic.convertFolder(filename, new ProgressListener() {\r
public void onFinished(String message) {\r
System.out.printf("%s\n", message);\r
}\r
});\r
}\r
- \r
+\r
public void convertFolder(String folder, final ProgressListener pl) throws IOException {\r
convertFolder(folder, pl, 1);\r
}\r
} else {\r
files.put(folderF.getName(), folderF);\r
}\r
- \r
+\r
BatchProcess bp = new BatchProcess();\r
bp.setTaskSize(32);\r
bp.setThreads(maxThreads);\r
e.printStackTrace();\r
}\r
}\r
- \r
+\r
public File convertFile(File file) {\r
return convertFile(file, null);\r
}\r
- public File convertFile(File file, File targetFolder) { \r
- String filenameNoExt = file.getName(); \r
+ public File convertFile(File file, File targetFolder) {\r
+ String filenameNoExt = file.getName();\r
if (filenameNoExt.lastIndexOf('.') > 0) {\r
filenameNoExt = filenameNoExt.substring(0, filenameNoExt.lastIndexOf('.'));\r
}\r
\r
StringBuilder log = new StringBuilder();\r
if (isLogging()) processLogs.put(file, log);\r
- \r
- try { \r
+\r
+ try {\r
BufferedImage result = null;\r
try {\r
if (file.getName().endsWith(".bmp")) {\r
Log.e("Unreadable image: " + file.getAbsolutePath());\r
return null;\r
}\r
- \r
+\r
int w = result.getWidth();\r
int h = result.getHeight();\r
\r
- //Scaling \r
+ //Scaling\r
if (scaling == ScalingType.STRETCH) {\r
w = targetScreenSize.width;\r
h = targetScreenSize.height;\r
scaledW = Math.max(1, Math.round(w * scale));\r
scaledH = Math.max(1, Math.round(h * scale));\r
}\r
- \r
+\r
Image scaled = ImageUtil.getScaledImage(result, scaledW, scaledH, Image.SCALE_AREA_AVERAGING);\r
- \r
+\r
if (scaling == ScalingType.BACKGROUND) {\r
w = targetScreenSize.width;\r
h = targetScreenSize.height;\r
w = scaledW;\r
h = scaledH;\r
}\r
- \r
+\r
result = ImageUtil.createCompatibleImage(w, h, scaled);\r
if (mode == ConvertType.TYPE_JPG) {\r
result = ImageUtil.asOpaqueImage(result);\r
}\r
- \r
+\r
Graphics2D g = (Graphics2D)result.getGraphics();\r
if (scaling == ScalingType.BACKGROUND) {\r
int sw = Math.round(scale*srcScreenSize.width);\r
g.drawImage(scaled, (w-scaledW)/2, (h-scaledH)/2, null);\r
g.dispose();\r
}\r
- \r
+\r
//Dithering\r
if (dithering == DitheringType.RANDOM) {\r
int[] rgb = result.getRGB(0, 0, w, h, null, 0, w);\r
- \r
+\r
Random rnd = new Random(0x13371337);\r
int t = 0;\r
for (int y = 0; y < h; y++) {\r
for (int x = 0; x < w; x++) {\r
int c = rgb[t];\r
- \r
+\r
double r = ((c>>16)&0xFF) * 31.0 / 255.0;\r
double g = ((c>>8 )&0xFF) * 31.0 / 255.0;\r
double b = ((c )&0xFF) * 31.0 / 255.0;\r
- \r
+\r
boolean ceil = rnd.nextFloat() < (r-Math.floor(r) + g-Math.floor(g) + b-Math.floor(b)) / 3.0;\r
- \r
+\r
int ri = (int)(ceil ? Math.ceil(r) : Math.floor(r));\r
int gi = (int)(ceil ? Math.ceil(g) : Math.floor(g));\r
int bi = (int)(ceil ? Math.ceil(b) : Math.floor(b));\r
- \r
+\r
rgb[t] = (c&0xFF000000) | (((ri<<3)&0xFF)<<16) | (((gi<<3)&0xFF)<<8) | ((bi<<3)&0xFF);\r
t++;\r
}\r
}\r
- \r
+\r
result.setRGB(0, 0, w, h, rgb, 0, w);\r
} else if (dithering == DitheringType.FLOYD_STEINBERG) {\r
int[] rgb = result.getRGB(0, 0, w, h, null, 0, w);\r
floydSteinberg(rgb, w, h);\r
result.setRGB(0, 0, w, h, rgb, 0, w);\r
}\r
- \r
+\r
if (targetFolder == null) {\r
targetFolder = file.getParentFile();\r
file.delete();\r
\r
//Create unique hash (multiple threads are writing temp files in the same folder)\r
String threadHash = String.valueOf(hashCode() ^ file.hashCode() ^ Thread.currentThread().hashCode());\r
- \r
+\r
file = new File(String.format("%s/%s.%s",\r
targetFolder, filenameNoExt, mode.getFileExt().toLowerCase()));\r
if (file.getParentFile() != null) {\r
file.getParentFile().mkdirs();\r
}\r
- \r
+\r
if (mode.getFileExt().equalsIgnoreCase("jpg")) {\r
String bmpFile = String.format("%s/__%s.bmp", targetFolder, threadHash);\r
String tmpFile = String.format("%s/__%s.jpg", targetFolder, threadHash);\r
\r
ImageIO.write(result, "bmp", new File(bmpFile));\r
- \r
+\r
Process p = ProcessUtil.execInDir(String.format(\r
"cjpeg -quality %d -optimize -dct fast \"%s\" \"%s\"",\r
quality, bmpFile, tmpFile),\r
- "tools/");\r
+ "");\r
ProcessUtil.waitFor(p);\r
ProcessUtil.kill(p);\r
file.delete();\r
new File(bmpFile).delete();\r
new File(tmpFile).renameTo(file);\r
- } else if (mode.getFileExt().equalsIgnoreCase("png")) { \r
+ } else if (mode.getFileExt().equalsIgnoreCase("png")) {\r
String tmpFile = String.format("%s/__%s.png", targetFolder, threadHash);\r
ImageIO.write(result, "png", new File(tmpFile));\r
- \r
+\r
if (mode == ConvertType.TYPE_PNG_256_MEDIAN) {\r
Process p = ProcessUtil.execInDir(String.format(\r
"pngquant 256 \"%s\"", tmpFile),\r
- "tools/pngquant-0.95/");\r
+ "");\r
ProcessUtil.waitFor(p);\r
ProcessUtil.kill(p);\r
file.delete();\r
} else if (mode == ConvertType.TYPE_PNG_256_NEUQUANT) {\r
Process p = ProcessUtil.execInDir(String.format(\r
"pngnq \"%s\"", tmpFile),\r
- "tools/pngnq-0.5-i386/");\r
+ "");\r
ProcessUtil.waitFor(p);\r
ProcessUtil.kill(p);\r
file.delete();\r
String crushedName = StringUtil.stripExtension(tmpFile)+".crushed.png";\r
Process p = ProcessUtil.execInDir(String.format(\r
"pngcrush -fix \"%s\" \"%s\"", tmpFile, crushedName),\r
- "tools/pngcrush-1.6.10/");\r
+ "");\r
ProcessUtil.waitFor(p);\r
ProcessUtil.kill(p);\r
file.delete();\r
new File(crushedName).renameTo(file);\r
}\r
- \r
+\r
new File(tmpFile).delete();\r
} else if (mode.getFileExt().equalsIgnoreCase("dta")) {\r
if (mode == ConvertType.TYPE_RAW_RGBA) {\r
int[] rgb = result.getRGB(0, 0, w, h, null, 0, w);\r
- \r
+\r
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(file));\r
for (int c : rgb) {\r
int a = ((c>>>24) >= 127 ? (1<<15) : 0);\r
int g = (c>>11) & 31;\r
int b = (c>>3) & 31;\r
c = a | (b<<10) | (g<<5) | (r);\r
- \r
+\r
bout.write(c&0xFF);\r
bout.write((c>>8)&0xFF);\r
}\r
\r
Process p = ProcessUtil.execInDir(String.format(\r
"pngnq \"%s\"", tmpFile.getAbsolutePath()),\r
- "tools/pngnq-0.5-i386/");\r
+ "");\r
ProcessUtil.waitFor(p);\r
ProcessUtil.kill(p);\r
tmpFile.delete();\r
IndexColorModel icm = (IndexColorModel)result.getColorModel();\r
\r
BufferedOutputStream bout;\r
- \r
+\r
bout = new BufferedOutputStream(new FileOutputStream(\r
StringUtil.stripExtension(file.getAbsolutePath())+".pal"));\r
for (int n = 0; n < icm.getMapSize(); n++) {\r
bout.write(dta[n]);\r
}\r
bout.close();\r
- \r
+\r
tmpFile.delete();\r
}\r
- } else { \r
- throw new IllegalArgumentException("Invalid file-ext: " + mode.getFileExt()); \r
+ } else {\r
+ throw new IllegalArgumentException("Invalid file-ext: " + mode.getFileExt());\r
}\r
return file;\r
} catch (Exception e) {\r
\r
return null;\r
}\r
- \r
+\r
\r
public File dumpLog(String filename) throws IOException {\r
File file = new File(filename);\r
PrintWriter out = new PrintWriter(new FileWriter(file));\r
- \r
+\r
out.println("----------------------------------------");\r
out.println("----------------------------------------");\r
for (Entry<File, StringBuilder> entry : getLogs().entrySet()) {\r
out.println();\r
out.println("Log for file:" + entry.getKey().getAbsolutePath());\r
out.println();\r
- \r
+\r
out.println(entry.getValue().toString());\r
- \r
+\r
out.println("----------------------------------------");\r
out.println("----------------------------------------");\r
}\r
- \r
+\r
out.close();\r
- \r
+\r
return file;\r
}\r
\r
private static int round(float f) {\r
return (int)f;\r
}\r
- \r
+\r
private static float saturate255(float f) {\r
if (f < 0f) return 0f;\r
if (f > 255f) return 255f;\r
return f;\r
}\r
- \r
+\r
/** Does dithering for 5-bit colors (equivalent to 8-bit / 8.0) */\r
private static void floydSteinberg(int rgb[], int w, int h) {\r
int L = w * h;\r
final int DOWN = w;\r
final int DOWN_LEFT = DOWN-1;\r
final int DOWN_RIGHT = DOWN+1;\r
- \r
+\r
for (int c = 0; c < 3; c++) {\r
float[] p = rgba[c];\r
int t = 0;\r
for (int y = 0; y < h; y++) {\r
- for (int x = 0; x < w; x++) { \r
+ for (int x = 0; x < w; x++) {\r
float oldc = p[t];\r
float newc = p[t] = round(oldc * DIV_8) << 3;\r
float error = oldc - newc;\r
p[t+DOWN_RIGHT] = saturate255(p[t+DOWN_RIGHT]);\r
}\r
}\r
- \r
+\r
t++;\r
- } \r
+ }\r
}\r
}\r
- \r
+\r
int t = 0;\r
for (int y = 0; y < h; y++) {\r
for (int x = 0; x < w; x++) {\r
}\r
}\r
}\r
- \r
+\r
// Getters\r
public Map<File, StringBuilder> getLogs() { return processLogs; }\r
public boolean isLogging() { return log; }\r
public int getQuality() { return quality; }\r
public DitheringType getDitheringType() { return dithering; }\r
public int getMaxThreads() { return maxThreads; }\r
- \r
+\r
// Setters\r
public void setMode(ConvertType mode) { this.mode = mode; }\r
public void setScalingType(ScalingType s) { this.scaling = s; }\r
public void setQuality(int quality) { this.quality = quality; }\r
public void setDitheringType(DitheringType dithering) { this.dithering = dithering; }\r
public void setMaxThreads(int mt) { this.maxThreads = mt; }\r
- \r
+\r
}\r
\r
public enum ConvertType {\r
TYPE_AAC("AAC"), TYPE_ADPCM("ADPCM"), TYPE_MP3("MP3"), TYPE_OGG("OGG");\r
- \r
+\r
private String label;\r
- \r
+\r
private ConvertType(String label) {\r
this.label = label;\r
}\r
- \r
+\r
public String toString() { return label; }\r
- \r
+\r
public static ConvertType fromExt(String string) {\r
string = string.toLowerCase();\r
if (string.equals("aac")) {\r
} else if (string.equals("ogg")) {\r
return TYPE_OGG;\r
}\r
- \r
+\r
throw new IllegalArgumentException("Unsupported conversion type: " + string);\r
}\r
}\r
- \r
+\r
public static final int AAC_Q_LOW = 35;\r
public static final int AAC_Q_MED = 50;\r
public static final int AAC_Q_HIGH = 70;\r
- \r
+\r
public static final int VORBIS_Q_LOW = 0;\r
public static final int VORBIS_Q_MED = 2;\r
public static final int VORBIS_Q_HIGH = 4;\r
- \r
- private String workingDir = "tools/";\r
+\r
+ private String workingDir = "";\r
private int maxThreads = 8;\r
private ConvertType mode = ConvertType.TYPE_AAC;\r
private int aac_quality = AAC_Q_HIGH;\r
private int mp3_minb = 8, mp3_maxb = 128, mp3_avgb = 96;\r
private int vorbis_quality = VORBIS_Q_MED;\r
- private int volume = 100; \r
+ private int volume = 100;\r
private boolean nameToUpperCase;\r
private boolean log;\r
- \r
+\r
public SoundConverter() {\r
}\r
- \r
+\r
// Functions\r
public static void main(String args[]) throws IOException {\r
SoundConverter ve = new SoundConverter();\r
- \r
+\r
//Fate/Stay Night\r
//ve.convertFolder("foldername/");\r
- \r
+\r
//Narcissu\r
//ve.setVolume(800);\r
//ve.setConvertNameToUpperCase(true);\r
//ve.convertFolder("foldername/", null);\r
- \r
+\r
ve.setMode(ConvertType.TYPE_OGG);\r
ve.convertFile(new File("z:/temp.mp3"));\r
}\r
- \r
+\r
public void convertFolder(String folder, final ProgressListener pl) {\r
Map<String, File> files = new HashMap<String, File>();\r
for (File file : new File(folder).listFiles()) {\r
if (!file.isDirectory()) files.put(file.getName(), file);\r
}\r
- \r
+\r
BatchProcess bp = new BatchProcess();\r
bp.setTaskSize(32);\r
bp.setThreads(maxThreads);\r
files.add(file);\r
}\r
}\r
- \r
+\r
protected static File[] createTempFiles(File file, File tempFolder, String... exts)\r
throws IOException\r
{\r
File[] result = new File[exts.length + 1];\r
- \r
+\r
String path = file.getAbsolutePath();\r
\r
//Check if entire path consists of ASCII characters\r
break;\r
}\r
}\r
- \r
+\r
if (ascii) {\r
result[0] = file;\r
- } else { \r
+ } else {\r
File dst = null;\r
String pre = String.format("temp-" + Thread.currentThread().hashCode());\r
String post = StringUtil.getExtension(file.getName()).toLowerCase();\r
FileUtil.copyFile(file, dst);\r
result[0] = dst;\r
}\r
- \r
+\r
for (int n = 0; n < exts.length; n++) {\r
String p = StringUtil.replaceExt(result[n].getAbsolutePath(), exts[n]);\r
File f = new File(p);\r
int t = 1;\r
while (f.exists()) {\r
t++;\r
- f = new File(StringUtil.stripExtension(p) + "-" + t + "." + StringUtil.getExtension(p)); \r
+ f = new File(StringUtil.stripExtension(p) + "-" + t + "." + StringUtil.getExtension(p));\r
}\r
result[n+1] = f;\r
}\r
- \r
+\r
return result;\r
}\r
- \r
+\r
public void convertFile(File file) throws IOException {\r
convertFile(file, mode, null);\r
}\r
public File convertFile(File srcF, ConvertType mode, File dstFolder) throws IOException {\r
if (dstFolder == null) {\r
dstFolder = srcF.getParentFile();\r
- } \r
+ }\r
dstFolder.mkdirs();\r
\r
String filters = "";\r
if (volume != 100) {\r
filters += " -vol " + volume;\r
- } \r
- \r
+ }\r
+\r
File[] temp;\r
String[] cmds;\r
switch (mode) {\r
case TYPE_AAC:\r
temp = createTempFiles(srcF, dstFolder, "wav", "aac");\r
- \r
+\r
cmds = new String[] {\r
String.format("ffmpeg -y -i \"%s\" -vn -ac 1 -ar 22050 %s \"%s\"",\r
temp[0], filters, temp[1]),\r
break;\r
case TYPE_MP3:\r
temp = createTempFiles(srcF, dstFolder, "wav", "mp3");\r
- \r
+\r
String bitrateFlags = String.format("--abr %d -b %d -B %d",\r
mp3_avgb, mp3_minb, mp3_maxb);\r
- \r
+\r
cmds = new String[] {\r
String.format("ffmpeg -y -i \"%s\" -vn -ac 2 -ar 32000 %s \"%s\"",\r
temp[0], filters, temp[1]),\r
break;\r
case TYPE_OGG:\r
temp = createTempFiles(srcF, dstFolder, "ogg");\r
- \r
+\r
cmds = new String[] {\r
String.format("ffmpeg -y -i \"%s\" -vn -acodec libvorbis -aq %d %s \"%s\"",\r
temp[0], vorbis_quality, filters, temp[1])\r
default:\r
throw new IllegalArgumentException("Illegal mode: " + mode);\r
}\r
- \r
+\r
File dstF = new File(dstFolder, StringUtil.replaceExt(srcF.getName(),\r
StringUtil.getExtension(temp[temp.length-1].getName()).toLowerCase()));\r
try {\r
Process p = ProcessUtil.execInDir(cmd, workingDir);\r
//System.out.println(ProcessUtil.read(p));\r
ProcessUtil.waitFor(p);\r
- } \r
+ }\r
} finally {\r
//Rename final result to dst\r
if (temp[temp.length-1].exists()) {\r
temp[temp.length-1].renameTo(dstF);\r
}\r
- \r
+\r
//Delete temp files\r
for (File file : temp) {\r
if (!file.equals(srcF) && !file.equals(dstF)) {\r
}\r
}\r
}\r
- \r
+\r
return dstF;\r
}\r
- \r
+\r
// Getters\r
public boolean isLogging() { return log; }\r
public ConvertType getMode() { return mode; }\r
public boolean getConvertNameToUpperCase() { return nameToUpperCase; }\r
public int getMaxThreads() { return maxThreads; }\r
public String getWorkingDir() { return workingDir; }\r
- \r
+\r
// Setters\r
public void setMode(ConvertType mode) { this.mode = mode; }\r
public void setVorbisQuality(int quality) { this.vorbis_quality = quality; }\r
public void setConvertNameToUpperCase(boolean nameToUpperCase) { this.nameToUpperCase = nameToUpperCase; }\r
public void setMaxThreads(int mt) { this.maxThreads = mt; }\r
public void setWorkingDir(String dir) { this.workingDir = dir; }\r
- \r
+\r
}\r