/*
* titl - Tools for iTunes Libraries
* Copyright (C) 2008-2011 Joseph Walton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kafsemo.titl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
/**
* A processor that can be configured with callbacks to modify any particular string field.
*
* @author Joseph
*/
public class ProcessLibrary
{
private final Map<Integer, StringConverter> converters = new HashMap<Integer, StringConverter>();
public void process(File inFile, OutputStream outStr) throws IOException, ItlException
{
/* Read the original library in */
Hdfm hdfm;
InputStream inStr = new FileInputStream(inFile);
try {
Input di = new InputImpl(inStr);
hdfm = Hdfm.read(di, inFile.length());
} finally {
inStr.close();
}
/* Modify... */
ByteArrayOutputStream dto = new ByteArrayOutputStream();
process(new DataInputStream(new ByteArrayInputStream(hdfm.fileData)), hdfm.fileData.length, new DataOutputStream(dto));
/* ...and write out */
DataOutput out = new DataOutputStream(outStr);
hdfm.write(out, dto.toByteArray());
}
void process(DataInput di, int totalLength, DataOutput out) throws IOException, ItlException
{
int remaining = totalLength;
boolean going = true;
while(going)
{
int consumed = 0;
String type = Util.toString(di.readInt());
consumed += 4;
int length = di.readInt();
consumed += 4;
if(type.equals("hohm")) {
int recLength = di.readInt();
consumed += 4;
Integer hohmType = Integer.valueOf(di.readInt());
consumed += 4;
byte[] ba = new byte[recLength - consumed];
di.readFully(ba);
StringConverter sc = converters.get(hohmType);
if (sc != null) {
ba = mapHohm(new DataInputStream(new ByteArrayInputStream(ba)), sc);
}
/* Write out again */
out.writeInt(Util.fromString(type));
out.writeInt(length);
out.writeInt(ba.length + consumed);
out.writeInt(hohmType);
out.write(ba);
remaining -= (recLength);
} else {
byte[] ba = new byte[length - consumed];
di.readFully(ba);
/* Did we hit the end? */
if(type.equals("hdsm")) {
going = !ParseLibrary.readHdsm(new InputImpl(new ByteArrayInputStream(ba)), ba.length);
}
remaining -= length;
/* Write out again */
out.writeInt(Util.fromString(type));
out.writeInt(ba.length + 8);
out.write(ba);
}
}
byte[] footerBytes = new byte[remaining];
di.readFully(footerBytes);
// String footer = new String(footerBytes, "iso-8859-1");
// System.out.println("Footer: " + footer);
out.write(footerBytes);
}
static byte[] mapHohm(DataInput di, StringConverter sc) throws IOException, ItlException
{
/* Read in */
byte[] unknown = new byte[12];
di.readFully(unknown);
int dataLength = di.readInt();
byte[] alsoUnknown = new byte[8];
di.readFully(alsoUnknown);
byte[] data = new byte[dataLength];
di.readFully(data);
String s = ParseLibrary.toString(data, unknown[11]);
s = sc.convert(s);
/* Write out */
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutput out = new DataOutputStream(baos);
/* Choose the necessary character encoding */
Encoding enc = chooseEncoding(s);
unknown[11] = enc.code;
byte[] newData = s.getBytes(enc.name);
out.write(unknown);
out.writeInt(newData.length);
out.write(alsoUnknown);
out.write(newData);
return baos.toByteArray();
}
static Encoding chooseEncoding(String s)
{
for (char c : s.toCharArray()) {
if (c > 0xff) {
return Encoding.UTF16BE;
}
}
return Encoding.ISO88591;
}
/**
* Register a converter for a particular HOHM type.
*
* <ul>
* <li>0x02 - Track title
* <li>0x0d - Location
* </ul>
*/
public void register(int hohmType, StringConverter cnvtr)
{
converters.put(Integer.valueOf(hohmType), cnvtr);
}
public enum Encoding
{
UNSPECIFIED((byte) 0, "iso-8859-1"),
UTF16BE((byte) 1, "utf-16be"),
ISO88591((byte) 3, "iso-8859-1");
private final byte code;
private final String name;
Encoding(byte c, String n)
{
this.code = c;
this.name = n;
}
}
public interface StringConverter
{
String convert(String s);
}
}