mirror of https://github.com/dnomd343/mobi-meta
Kin-Wai Koo
13 years ago
commit
3641e2586a
24 changed files with 3478 additions and 0 deletions
@ -0,0 +1,22 @@ |
|||||
|
Copyright (c) 2012 Kin-Wai Koo |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person |
||||
|
obtaining a copy of this software and associated documentation |
||||
|
files (the ÒSoftwareÓ), to deal in the Software without |
||||
|
restriction, including without limitation the rights to use, |
||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the |
||||
|
Software is furnished to do so, subject to the following |
||||
|
conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be |
||||
|
included in all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED ÒAS ISÓ, WITHOUT WARRANTY OF ANY KIND, |
||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||||
|
OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,41 @@ |
|||||
|
You will need to have Java (JDK or JRE) 1.5 or above to execute this program. |
||||
|
|
||||
|
To start the program, just double-click on the MobiMetaEditorV0.15.jar file. |
||||
|
|
||||
|
If that doesn't work, type this from the command-line: |
||||
|
java -jar MobiMetaEditorV0.15.jar |
||||
|
|
||||
|
This was written based on the MOBI file format describe in: |
||||
|
http://wiki.mobileread.com/wiki/MOBI |
||||
|
|
||||
|
This application is licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php). |
||||
|
|
||||
|
|
||||
|
ChangeLog |
||||
|
|
||||
|
v0.15 |
||||
|
- added the ability to add/edit the TTS EXTH record |
||||
|
|
||||
|
v0.14 |
||||
|
- does not pack the header if the full name field and EXTH records remain unchanged |
||||
|
- added Open and Save menu items |
||||
|
- lets the user specify the target filename |
||||
|
|
||||
|
v0.13 |
||||
|
- added a Header Info dialog, which details various fields in the PDB header, PalmDOC header, and MOBI header |
||||
|
- added in provisions to invoke debug and safe modes from the command line |
||||
|
- added in WhisperPrep, which lets users set ASINs and sets the CDEType to EBOK |
||||
|
(to invoke, type "java -cp MobiMetaEditorV0.13.jar cli.WhisperPrep |
||||
|
<input directory> <output directory>") |
||||
|
|
||||
|
v0.12 |
||||
|
- changed the GUI to use FileDialog instead of JFileChooser for a more native look and feel |
||||
|
- added support for window modified indicator on OS X |
||||
|
- lets the user specify an input filename on the command line |
||||
|
|
||||
|
v0.11 |
||||
|
- fixed some MobiHeader size calculation bugs |
||||
|
- added facilities to edit the language fields |
||||
|
|
||||
|
v0.10 |
||||
|
- initial release |
@ -0,0 +1,175 @@ |
|||||
|
package cli; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.io.BufferedReader; |
||||
|
import java.io.File; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStreamReader; |
||||
|
import java.util.LinkedList; |
||||
|
|
||||
|
import mobimeta.*; |
||||
|
|
||||
|
public class WhisperPrep |
||||
|
{ |
||||
|
private File inDir; |
||||
|
private File outDir; |
||||
|
private BufferedReader in; |
||||
|
|
||||
|
public final static void main(String[] args) |
||||
|
{ |
||||
|
if (args.length != 2) printUsage(); |
||||
|
|
||||
|
File inDir = new File(args[0]); |
||||
|
File outDir = new File(args[1]); |
||||
|
|
||||
|
if (!inDir.exists() || !inDir.isDirectory()) |
||||
|
printUsage("Input directory " + args[0] + " does not exist."); |
||||
|
|
||||
|
if (!outDir.exists() || !outDir.isDirectory()) |
||||
|
printUsage("Output directory " + args[1] + " does not exist."); |
||||
|
|
||||
|
if (inDir.getAbsolutePath().equals(outDir.getAbsolutePath())) |
||||
|
printUsage("Input and output directories cannot be the same."); |
||||
|
|
||||
|
WhisperPrep wp = new WhisperPrep(inDir, outDir); |
||||
|
wp.start(); |
||||
|
} |
||||
|
|
||||
|
private static void printUsage() |
||||
|
{ |
||||
|
printUsage(null); |
||||
|
} |
||||
|
|
||||
|
private static void printUsage(String message) |
||||
|
{ |
||||
|
if (message != null) System.err.println(message); |
||||
|
System.err.println("Usage: WhisperPrep <input directory> <output directory>"); |
||||
|
System.exit(0); |
||||
|
} |
||||
|
|
||||
|
public WhisperPrep(File inDir, File outDir) |
||||
|
{ |
||||
|
this.inDir = inDir; |
||||
|
this.outDir = outDir; |
||||
|
} |
||||
|
|
||||
|
public void start() |
||||
|
{ |
||||
|
LinkedList<File> fileList = new LinkedList<File>(); |
||||
|
for (File f : inDir.listFiles()) |
||||
|
{ |
||||
|
if (f.isFile() && f.getName().toLowerCase().endsWith(".mobi")) |
||||
|
fileList.add(f); |
||||
|
} |
||||
|
|
||||
|
in = new BufferedReader(new InputStreamReader(System.in)); |
||||
|
|
||||
|
int fileCount = fileList.size(); |
||||
|
int current = 1; |
||||
|
for (File f : fileList) |
||||
|
{ |
||||
|
log("********************"); |
||||
|
log("File " + (current++) + "/" + fileCount); |
||||
|
log("Filename: " + f.getName()); |
||||
|
MobiMeta meta = null; |
||||
|
try |
||||
|
{ |
||||
|
meta = new MobiMeta(f); |
||||
|
} |
||||
|
catch (MobiMetaException e) |
||||
|
{ |
||||
|
log("Error: " + e.getMessage()); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
log("Fullname: " + meta.getFullName()); |
||||
|
List<EXTHRecord> exthList = meta.getEXTHRecords(); |
||||
|
String encoding = meta.getCharacterEncoding(); |
||||
|
String author = null; |
||||
|
String isbn = null; |
||||
|
String oldasin = null; |
||||
|
for (EXTHRecord rec : exthList) |
||||
|
{ |
||||
|
switch (rec.getRecordType()) |
||||
|
{ |
||||
|
case 100: |
||||
|
author = StreamUtils.byteArrayToString(rec.getData(), encoding); |
||||
|
break; |
||||
|
|
||||
|
case 104: |
||||
|
isbn = StreamUtils.byteArrayToString(rec.getData(), encoding); |
||||
|
break; |
||||
|
|
||||
|
case 113: |
||||
|
oldasin = StreamUtils.byteArrayToString(rec.getData(), encoding); |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (author != null) log("Author: " + author); |
||||
|
if (isbn != null) log("ISBN: " + isbn); |
||||
|
String asin = null; |
||||
|
if (oldasin == null) |
||||
|
asin = getInput("ASIN: "); |
||||
|
else |
||||
|
asin = getInput("ASIN [" + oldasin + "]: "); |
||||
|
|
||||
|
if (asin.length() == 0) |
||||
|
{ |
||||
|
if (oldasin != null) |
||||
|
asin = oldasin; |
||||
|
else |
||||
|
asin = null; |
||||
|
} |
||||
|
|
||||
|
for (EXTHRecord rec : exthList) |
||||
|
{ |
||||
|
int recType = rec.getRecordType(); |
||||
|
if (recType == 113) |
||||
|
{ |
||||
|
if (asin != null) exthList.remove(rec); |
||||
|
} |
||||
|
else if (recType == 501) |
||||
|
exthList.remove(rec); |
||||
|
} |
||||
|
|
||||
|
if (asin != null) exthList.add(new EXTHRecord(113, asin, encoding)); |
||||
|
exthList.add(new EXTHRecord(501, "EBOK", encoding)); |
||||
|
meta.setEXTHRecords(); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
meta.saveToNewFile(new File(outDir, f.getName())); |
||||
|
} |
||||
|
catch (MobiMetaException e) |
||||
|
{ |
||||
|
log("Error saving file: " + e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
log(""); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void log(String message) |
||||
|
{ |
||||
|
System.out.println(message); |
||||
|
} |
||||
|
|
||||
|
private String getInput(String message) |
||||
|
{ |
||||
|
System.out.print(message); |
||||
|
String value = null; |
||||
|
try |
||||
|
{ |
||||
|
value = in.readLine(); |
||||
|
} |
||||
|
catch (IOException e) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
return (value == null)?"":value; |
||||
|
} |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
import javax.swing.JTable; |
||||
|
import javax.swing.table.TableCellEditor; |
||||
|
import javax.swing.table.TableCellRenderer; |
||||
|
import javax.swing.table.TableColumn; |
||||
|
import javax.swing.table.TableModel; |
||||
|
|
||||
|
public class CustomJTable extends JTable |
||||
|
{ |
||||
|
/** |
||||
|
* This code was adapted from |
||||
|
* http://www.javalobby.org/java/forums/t84905.html
|
||||
|
* |
||||
|
* A traditional JTable lets you specify custom renderers and editors on a |
||||
|
* column by column basis. This class lets you have different renderers and |
||||
|
* editors in the same column (but different rows). |
||||
|
*/ |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
public CustomJTable(TableModel model) |
||||
|
{ |
||||
|
super(model); |
||||
|
} |
||||
|
|
||||
|
public TableCellRenderer getCellRenderer(int row, int column) |
||||
|
{ |
||||
|
TableColumn tableColumn = getColumnModel().getColumn(column); |
||||
|
TableCellRenderer renderer = tableColumn.getCellRenderer(); |
||||
|
if (renderer != null) return renderer; |
||||
|
if (getValueAt(row, column) != null) |
||||
|
{ |
||||
|
return getDefaultRenderer(getValueAt(row, column).getClass()); |
||||
|
} |
||||
|
return getDefaultRenderer(getColumnClass(column)); |
||||
|
} |
||||
|
|
||||
|
public TableCellEditor getCellEditor(int row, int column) |
||||
|
{ |
||||
|
TableColumn tableColumn = getColumnModel().getColumn(column); |
||||
|
TableCellEditor editor = tableColumn.getCellEditor(); |
||||
|
if (editor != null) return editor; |
||||
|
if (getValueAt(row, column) != null) |
||||
|
{ |
||||
|
return getDefaultEditor(getValueAt(row, column).getClass()); |
||||
|
} |
||||
|
return getDefaultEditor(getColumnClass(column)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
import mobimeta.EXTHRecord; |
||||
|
|
||||
|
public interface EXTHAddRecordListener |
||||
|
{ |
||||
|
public void addEXTHRecord(EXTHRecord rec); |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
public class GuiException extends Exception |
||||
|
{ |
||||
|
/** |
||||
|
* |
||||
|
*/ |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
public GuiException(String message) |
||||
|
{ |
||||
|
super(message); |
||||
|
} |
||||
|
} |
@ -0,0 +1,201 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
import mobimeta.*; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import javax.swing.table.AbstractTableModel; |
||||
|
|
||||
|
class GuiModel extends AbstractTableModel implements EXTHAddRecordListener, LanguageModel, MetaInfoProvider |
||||
|
{ |
||||
|
/** |
||||
|
* This is essentially a proxy to the MobiMeta object. In addition, it |
||||
|
* serves as a TableModel for the JTable |
||||
|
*/ |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
private MobiMeta model = null; |
||||
|
|
||||
|
// static globals for convenience
|
||||
|
//
|
||||
|
private static String fullName = null; |
||||
|
private static List<EXTHRecord> exthRecords = null; |
||||
|
private static String characterEncoding = null; |
||||
|
|
||||
|
|
||||
|
public GuiModel() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void setModel(File f) throws GuiException |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
model = new MobiMeta(f); |
||||
|
} |
||||
|
catch (MobiMetaException e) |
||||
|
{ |
||||
|
throw new GuiException(e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
fullName = model.getFullName(); |
||||
|
exthRecords = model.getEXTHRecords(); |
||||
|
characterEncoding = model.getCharacterEncoding(); |
||||
|
} |
||||
|
|
||||
|
public String getColumnName(int col) |
||||
|
{ |
||||
|
if (col == 0) |
||||
|
return "Record Type"; |
||||
|
else |
||||
|
return "Value"; |
||||
|
} |
||||
|
|
||||
|
public int getColumnCount() |
||||
|
{ |
||||
|
return 2; |
||||
|
} |
||||
|
|
||||
|
public int getRowCount() |
||||
|
{ |
||||
|
return (exthRecords == null)?0:exthRecords.size(); |
||||
|
} |
||||
|
|
||||
|
public Object getValueAt(int row, int col) |
||||
|
{ |
||||
|
EXTHRecord rec = exthRecords.get(row); |
||||
|
int type = rec.getRecordType(); |
||||
|
String typeDesc = rec.getTypeDescription(); |
||||
|
|
||||
|
if (col == 0) |
||||
|
{ |
||||
|
if (typeDesc == null) |
||||
|
return Integer.toString(type); |
||||
|
else |
||||
|
return type + " (" + typeDesc + ")"; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
byte[] data = rec.getData(); |
||||
|
|
||||
|
if (typeDesc == null) |
||||
|
{ |
||||
|
StringBuffer sb = new StringBuffer(); |
||||
|
sb.append("{ "); |
||||
|
int len = data.length; |
||||
|
for (int i=0; i<len; i++) |
||||
|
{ |
||||
|
if (i > 0) sb.append(", "); |
||||
|
sb.append(data[i] & 0xff); |
||||
|
} |
||||
|
sb.append(" }"); |
||||
|
return sb.toString(); |
||||
|
} |
||||
|
else if (EXTHRecord.isBooleanType(type)) |
||||
|
{ |
||||
|
int value = StreamUtils.byteArrayToInt(data); |
||||
|
if (value == 0) |
||||
|
return Boolean.FALSE; |
||||
|
else |
||||
|
return Boolean.TRUE; |
||||
|
} |
||||
|
else |
||||
|
return StreamUtils.byteArrayToString(data, characterEncoding); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public boolean isCellEditable(int row, int col) |
||||
|
{ |
||||
|
if ((col == 0) || MobiCommon.safeMode) return false; |
||||
|
|
||||
|
return exthRecords.get(row).isKnownType(); |
||||
|
} |
||||
|
|
||||
|
public void setValueAt(Object value, int row, int column) |
||||
|
{ |
||||
|
if (column != 1) return; |
||||
|
|
||||
|
if (value instanceof String) |
||||
|
exthRecords.get(row).setData((String)value, characterEncoding); |
||||
|
else if (value instanceof Boolean) |
||||
|
exthRecords.get(row).setData(((Boolean)value).booleanValue()); |
||||
|
else |
||||
|
return; |
||||
|
|
||||
|
fireTableCellUpdated(row, column); |
||||
|
} |
||||
|
|
||||
|
public String getFullName() |
||||
|
{ |
||||
|
return fullName; |
||||
|
} |
||||
|
|
||||
|
public void setFullName(String s) |
||||
|
{ |
||||
|
fullName = s; |
||||
|
} |
||||
|
|
||||
|
public void removeRecordAtRow(int row) |
||||
|
{ |
||||
|
exthRecords.remove(row); |
||||
|
fireTableRowsDeleted(row, row); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public void addEXTHRecord(EXTHRecord rec) |
||||
|
{ |
||||
|
exthRecords.add(rec); |
||||
|
int lastIndex = exthRecords.size() - 1; |
||||
|
fireTableRowsInserted(lastIndex, lastIndex); |
||||
|
} |
||||
|
|
||||
|
public static String getCharacterEncoding() |
||||
|
{ |
||||
|
return characterEncoding; |
||||
|
} |
||||
|
|
||||
|
public void save(File outputFile, boolean packHeader) throws GuiException |
||||
|
{ |
||||
|
if (packHeader) |
||||
|
{ |
||||
|
model.setFullName(fullName); |
||||
|
model.setEXTHRecords(); |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
model.saveToNewFile(outputFile, packHeader); |
||||
|
} |
||||
|
catch (MobiMetaException e) |
||||
|
{ |
||||
|
throw new GuiException("Problems encountered while saving file: " |
||||
|
+ e.getMessage()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int getLocale() |
||||
|
{ |
||||
|
return model.getLocale(); |
||||
|
} |
||||
|
|
||||
|
public int getDictInput() |
||||
|
{ |
||||
|
return model.getDictInput(); |
||||
|
} |
||||
|
|
||||
|
public int getDictOutput() |
||||
|
{ |
||||
|
return model.getDictOutput(); |
||||
|
} |
||||
|
|
||||
|
public void setLanguages(int locale, int dictInput, int dictOutput) |
||||
|
{ |
||||
|
model.setLanguages(locale, dictInput, dictOutput); |
||||
|
} |
||||
|
|
||||
|
public String getMetaInfo() |
||||
|
{ |
||||
|
return model.getMetaInfo(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,73 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
import java.awt.BorderLayout; |
||||
|
import java.awt.FlowLayout; |
||||
|
|
||||
|
import javax.swing.JButton; |
||||
|
import javax.swing.JDialog; |
||||
|
import javax.swing.JFrame; |
||||
|
import javax.swing.JPanel; |
||||
|
import javax.swing.border.EmptyBorder; |
||||
|
import java.awt.event.ActionListener; |
||||
|
import java.awt.event.ActionEvent; |
||||
|
import javax.swing.JTextArea; |
||||
|
import javax.swing.JScrollPane; |
||||
|
|
||||
|
public class HeaderInfoDialog extends JDialog implements ActionListener |
||||
|
{ |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
*/ |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
private final JPanel contentPanel = new JPanel(); |
||||
|
private JButton closeButton; |
||||
|
private JScrollPane scrollPane; |
||||
|
private JTextArea textArea; |
||||
|
|
||||
|
/** |
||||
|
* Create the dialog. |
||||
|
*/ |
||||
|
public HeaderInfoDialog(JFrame parent, MetaInfoProvider infoProvider) |
||||
|
{ |
||||
|
super(parent, "Header Info", true); |
||||
|
setBounds(100, 100, 450, 300); |
||||
|
getContentPane().setLayout(new BorderLayout()); |
||||
|
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); |
||||
|
getContentPane().add(contentPanel, BorderLayout.CENTER); |
||||
|
contentPanel.setLayout(new BorderLayout(0, 0)); |
||||
|
{ |
||||
|
scrollPane = new JScrollPane(); |
||||
|
contentPanel.add(scrollPane, BorderLayout.CENTER); |
||||
|
{ |
||||
|
textArea = new JTextArea(infoProvider.getMetaInfo()); |
||||
|
textArea.setEditable(false); |
||||
|
scrollPane.setViewportView(textArea); |
||||
|
} |
||||
|
} |
||||
|
{ |
||||
|
JPanel buttonPane = new JPanel(); |
||||
|
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); |
||||
|
getContentPane().add(buttonPane, BorderLayout.SOUTH); |
||||
|
{ |
||||
|
closeButton = new JButton("Close"); |
||||
|
closeButton.addActionListener(this); |
||||
|
closeButton.setActionCommand("Cancel"); |
||||
|
buttonPane.add(closeButton); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected JButton getCloseButton() { |
||||
|
return closeButton; |
||||
|
} |
||||
|
|
||||
|
public void actionPerformed(ActionEvent e) |
||||
|
{ |
||||
|
if (e.getSource() == closeButton) |
||||
|
{ |
||||
|
setVisible(false); |
||||
|
dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
public class LanguageCodes |
||||
|
{ |
||||
|
// these codes are taken from:
|
||||
|
// http://www.autoitscript.com/autoit3/docs/appendix/OSLangCodes.htm
|
||||
|
//
|
||||
|
public final static int[] code = |
||||
|
{ 0, 1078, 1052, 1025, 2049, 3073, 4097, 5121, 6145, 7169, 8193, 9217, |
||||
|
10241, 11265, 12289, 13313, 14337, 15361, 16385, 1067, 1068, 2092, |
||||
|
1069, 1059, 1026, 1027, 4, 1028, 2052, 3076, 4100, 5124, 1050, 1029, |
||||
|
1030, 1043, 2067, 9, 1033, 2057, 3081, 4105, 5129, 6153, 7177, |
||||
|
8201, 9225, 10249, 11273, 12297, 13321, 1061, 1080, 1073, 1035, |
||||
|
1036, 2060, 3084, 4108, 5132, 6156, 1079, 1031, 2055, 3079, 4103, |
||||
|
5127, 1032, 1037, 1081, 1038, 1039, 1057, 1040, 2064, 1041, 1087, |
||||
|
1111, 1042, 1062, 1063, 1071, 1086, 2110, 1102, 1044, 2068, 1045, |
||||
|
1046, 2070, 1048, 1049, 1103, 2074, 3098, 1051, 1060, 1034, 2058, |
||||
|
3082, 4106, 5130, 6154, 7178, 8202, 9226, 10250, 11274, 12298, |
||||
|
13322, 14346, 15370, 16394, 17418, 18442, 19466, 20490, 1089, 1053, |
||||
|
2077, 1097, 1092, 1054, 1055, 1058, 1056, 1091, 2115, 1066 }; |
||||
|
|
||||
|
public final static String[] description = |
||||
|
{ "Unspecified", "Afrikaans", "Albanian", "Arabic - Saudi Arabia", |
||||
|
"Arabic - Iraq", "Arabic - Egypt", "Arabic - Libya", |
||||
|
"Arabic - Algeria", "Arabic - Morocco", "Arabic - Tunisia", |
||||
|
"Arabic - Oman", "Arabic - Yemen", "Arabic - Syria", |
||||
|
"Arabic - Jordan", "Arabic - Lebanon", "Arabic - Kuwait", |
||||
|
"Arabic - UAE", "Arabic - Bahrain", "Arabic - Qatar", "Armenian", |
||||
|
"Azeri - Latin", "Azeri - Cyrillic", "Basque", "Belarusian", |
||||
|
"Bulgarian", "Catalan", "Chinese", "Chinese - Taiwan", "Chinese - PRC", |
||||
|
"Chinese - Hong Kong", "Chinese - Singapore", "Chinese - Macau", |
||||
|
"Croatian", "Czech", "Danish", "Dutch - Standard", |
||||
|
"Dutch - Belgian", "English", "English - United States", |
||||
|
"English Ð United Kingdom", "English - Australian", |
||||
|
"English - Canadian", "English - New Zealand", "English - Irish", |
||||
|
"English - South Africa", "English - Jamaica", |
||||
|
"English - Caribbean", "English - Belize", "English - Trinidad", |
||||
|
"English - Zimbabwe", "English - Philippines", "Estonian", |
||||
|
"Faeroese", "Farsi", "Finnish", "French - Standard", |
||||
|
"French - Belgian", "French - Canadian", "French - Swiss", |
||||
|
"French - Luxembourg", "French - Monaco", "Georgian", |
||||
|
"German - Standard", "German - Swiss", "German - Austrian", |
||||
|
"German - Luxembourg", "German - Liechtenstei", "Greek", "Hebrew", |
||||
|
"Hindi", "Hungarian", "Icelandic", "Indonesian", |
||||
|
"Italian - Standard", "Italian - Swiss", "Japanese", "Kazakh", |
||||
|
"Konkani", "Korean", "Latvian", "Lithuanian", "Macedonian", |
||||
|
"Malay - Malaysia", "Malay - Brunei Darussalam", "Marathi", |
||||
|
"Norwegian - Bokmal", "Norwegian - Nynorsk", "Polish", |
||||
|
"Portuguese - Brazilian", "Portuguese - Standard", "Romanian", |
||||
|
"Russian", "Sanskrit", "Serbian - Latin", "Serbian - Cyrillic", |
||||
|
"Slovak", "Slovenian", "Spanish - Traditional Sort", |
||||
|
"Spanish - Mexican", "Spanish - Modern Sort", |
||||
|
"Spanish - Guatemala", "Spanish - Costa Rica", "Spanish - Panama", |
||||
|
"Spanish - Dominican Republic", "Spanish - Venezuela", |
||||
|
"Spanish - Colombia", "Spanish - Peru", "Spanish - Argentina", |
||||
|
"Spanish - Ecuador", "Spanish - Chile", "Spanish - Uruguay", |
||||
|
"Spanish - Paraguay", "Spanish - Bolivia", "Spanish - El Salvador", |
||||
|
"Spanish - Honduras", "Spanish - Nicaragua", |
||||
|
"Spanish - Puerto Rico", "Swahili", "Swedish", "Swedish - Finland", |
||||
|
"Tamil", "Tatar", "Thai", "Turkish", "Ukrainian", "Urdu", |
||||
|
"Uzbek - Latin", "Uzbek - Cyrillic", "Vietnamese" }; |
||||
|
|
||||
|
|
||||
|
public static int codeToIndex(int key) |
||||
|
{ |
||||
|
int len = code.length; |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
if (code[i] == key) return i; |
||||
|
} |
||||
|
|
||||
|
return -1; |
||||
|
} |
||||
|
} |
@ -0,0 +1,258 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
import java.awt.BorderLayout; |
||||
|
import java.awt.FlowLayout; |
||||
|
|
||||
|
import javax.swing.JButton; |
||||
|
import javax.swing.JDialog; |
||||
|
import javax.swing.JFrame; |
||||
|
import javax.swing.JPanel; |
||||
|
import javax.swing.border.EmptyBorder; |
||||
|
import java.awt.GridBagLayout; |
||||
|
import java.awt.Component; |
||||
|
import javax.swing.Box; |
||||
|
import javax.swing.JLabel; |
||||
|
import java.awt.GridBagConstraints; |
||||
|
import java.awt.Font; |
||||
|
import javax.swing.SwingConstants; |
||||
|
import java.awt.Insets; |
||||
|
import java.awt.event.ActionEvent; |
||||
|
import java.awt.event.ActionListener; |
||||
|
|
||||
|
import javax.swing.JComboBox; |
||||
|
|
||||
|
public class LanguageDialog extends JDialog implements ActionListener |
||||
|
{ |
||||
|
/** |
||||
|
* |
||||
|
*/ |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
private final JPanel contentPanel = new JPanel(); |
||||
|
|
||||
|
private JButton okButton; |
||||
|
private JButton cancelButton; |
||||
|
private JComboBox cbLanguage; |
||||
|
private JComboBox cbDictInput; |
||||
|
private JComboBox cbDictOutput; |
||||
|
|
||||
|
// store original values here
|
||||
|
private int locale; |
||||
|
private int dictInput; |
||||
|
private int dictOutput; |
||||
|
|
||||
|
// these values indicate if they are known values
|
||||
|
private boolean localeExists; |
||||
|
private boolean dictInputExists; |
||||
|
private boolean dictOutputExists; |
||||
|
|
||||
|
private LanguageModel model; |
||||
|
|
||||
|
/** |
||||
|
* Create the dialog. |
||||
|
*/ |
||||
|
public LanguageDialog(JFrame parent, LanguageModel model) |
||||
|
{ |
||||
|
super(parent, true); |
||||
|
this.model = model; |
||||
|
locale = model.getLocale(); |
||||
|
dictInput = model.getDictInput(); |
||||
|
dictOutput = model.getDictOutput(); |
||||
|
localeExists = (LanguageCodes.codeToIndex(locale) != -1); |
||||
|
dictInputExists = (LanguageCodes.codeToIndex(dictInput) != -1); |
||||
|
dictOutputExists = (LanguageCodes.codeToIndex(dictOutput) != -1); |
||||
|
|
||||
|
String[] localeChoices; |
||||
|
String[] dictInputChoices; |
||||
|
String[] dictOutputChoices; |
||||
|
|
||||
|
// assemble choices for combo boxes
|
||||
|
//
|
||||
|
if (localeExists) |
||||
|
localeChoices = LanguageCodes.description; |
||||
|
else |
||||
|
{ |
||||
|
localeChoices = new String[LanguageCodes.description.length + 1]; |
||||
|
System.arraycopy(LanguageCodes.description, 0, localeChoices, 0, |
||||
|
LanguageCodes.description.length); |
||||
|
localeChoices[localeChoices.length - 1] = "Unknown (" + locale |
||||
|
+ ")"; |
||||
|
} |
||||
|
|
||||
|
if (dictInputExists) |
||||
|
dictInputChoices = LanguageCodes.description; |
||||
|
else |
||||
|
{ |
||||
|
dictInputChoices = new String[LanguageCodes.description.length + 1]; |
||||
|
System.arraycopy(LanguageCodes.description, 0, dictInputChoices, 0, |
||||
|
LanguageCodes.description.length); |
||||
|
dictInputChoices[dictInputChoices.length - 1] = "Unknown (" |
||||
|
+ dictInput + ")"; |
||||
|
} |
||||
|
|
||||
|
if (dictOutputExists) |
||||
|
dictOutputChoices = LanguageCodes.description; |
||||
|
else |
||||
|
{ |
||||
|
dictOutputChoices = new String[LanguageCodes.description.length + 1]; |
||||
|
System.arraycopy(LanguageCodes.description, 0, dictOutputChoices, |
||||
|
0, LanguageCodes.description.length); |
||||
|
dictOutputChoices[dictOutputChoices.length - 1] = "Unknown (" |
||||
|
+ dictOutput + ")"; |
||||
|
} |
||||
|
|
||||
|
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); |
||||
|
setBounds(100, 100, 450, 300); |
||||
|
getContentPane().setLayout(new BorderLayout()); |
||||
|
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); |
||||
|
getContentPane().add(contentPanel, BorderLayout.CENTER); |
||||
|
contentPanel.setLayout(new BorderLayout(0, 0)); |
||||
|
{ |
||||
|
Component verticalStrut = Box.createVerticalStrut(20); |
||||
|
contentPanel.add(verticalStrut, BorderLayout.NORTH); |
||||
|
} |
||||
|
{ |
||||
|
Component verticalStrut = Box.createVerticalStrut(20); |
||||
|
contentPanel.add(verticalStrut, BorderLayout.SOUTH); |
||||
|
} |
||||
|
{ |
||||
|
Component horizontalStrut = Box.createHorizontalStrut(20); |
||||
|
contentPanel.add(horizontalStrut, BorderLayout.WEST); |
||||
|
} |
||||
|
{ |
||||
|
Component horizontalStrut = Box.createHorizontalStrut(20); |
||||
|
contentPanel.add(horizontalStrut, BorderLayout.EAST); |
||||
|
} |
||||
|
{ |
||||
|
JPanel panel = new JPanel(); |
||||
|
contentPanel.add(panel, BorderLayout.CENTER); |
||||
|
GridBagLayout gbl_panel = new GridBagLayout(); |
||||
|
gbl_panel.columnWidths = new int[] |
||||
|
{ 0, 0, 0 }; |
||||
|
gbl_panel.rowHeights = new int[] |
||||
|
{ 0, 0, 0, 0 }; |
||||
|
gbl_panel.columnWeights = new double[] |
||||
|
{ 0.0, 1.0, Double.MIN_VALUE }; |
||||
|
gbl_panel.rowWeights = new double[] |
||||
|
{ 0.0, 0.0, 0.0, Double.MIN_VALUE }; |
||||
|
panel.setLayout(gbl_panel); |
||||
|
{ |
||||
|
JLabel lblLanguage = new JLabel("Language"); |
||||
|
lblLanguage.setHorizontalAlignment(SwingConstants.RIGHT); |
||||
|
lblLanguage.setFont(new Font("Lucida Grande", Font.BOLD, 13)); |
||||
|
GridBagConstraints gbc_lblLanguage = new GridBagConstraints(); |
||||
|
gbc_lblLanguage.anchor = GridBagConstraints.EAST; |
||||
|
gbc_lblLanguage.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblLanguage.gridx = 0; |
||||
|
gbc_lblLanguage.gridy = 0; |
||||
|
panel.add(lblLanguage, gbc_lblLanguage); |
||||
|
} |
||||
|
{ |
||||
|
cbLanguage = new JComboBox(localeChoices); |
||||
|
cbLanguage.setSelectedIndex(localeExists ? LanguageCodes |
||||
|
.codeToIndex(locale) : (localeChoices.length - 1)); |
||||
|
GridBagConstraints gbc_cbLanguage = new GridBagConstraints(); |
||||
|
gbc_cbLanguage.insets = new Insets(0, 0, 5, 0); |
||||
|
gbc_cbLanguage.fill = GridBagConstraints.HORIZONTAL; |
||||
|
gbc_cbLanguage.gridx = 1; |
||||
|
gbc_cbLanguage.gridy = 0; |
||||
|
panel.add(cbLanguage, gbc_cbLanguage); |
||||
|
} |
||||
|
{ |
||||
|
JLabel lblDictionaryInput = new JLabel("Dictionary Input"); |
||||
|
lblDictionaryInput.setHorizontalAlignment(SwingConstants.RIGHT); |
||||
|
lblDictionaryInput.setFont(new Font("Lucida Grande", Font.BOLD, |
||||
|
13)); |
||||
|
GridBagConstraints gbc_lblDictionaryInput = new GridBagConstraints(); |
||||
|
gbc_lblDictionaryInput.anchor = GridBagConstraints.EAST; |
||||
|
gbc_lblDictionaryInput.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblDictionaryInput.gridx = 0; |
||||
|
gbc_lblDictionaryInput.gridy = 1; |
||||
|
panel.add(lblDictionaryInput, gbc_lblDictionaryInput); |
||||
|
} |
||||
|
{ |
||||
|
cbDictInput = new JComboBox(dictInputChoices); |
||||
|
cbDictInput |
||||
|
.setSelectedIndex(dictInputExists ? LanguageCodes |
||||
|
.codeToIndex(dictInput) |
||||
|
: (dictInputChoices.length - 1)); |
||||
|
GridBagConstraints gbc_cbDictInput = new GridBagConstraints(); |
||||
|
gbc_cbDictInput.insets = new Insets(0, 0, 5, 0); |
||||
|
gbc_cbDictInput.fill = GridBagConstraints.HORIZONTAL; |
||||
|
gbc_cbDictInput.gridx = 1; |
||||
|
gbc_cbDictInput.gridy = 1; |
||||
|
panel.add(cbDictInput, gbc_cbDictInput); |
||||
|
} |
||||
|
{ |
||||
|
JLabel lblDictionaryOutput = new JLabel("Dictionary Output"); |
||||
|
lblDictionaryOutput |
||||
|
.setHorizontalAlignment(SwingConstants.RIGHT); |
||||
|
lblDictionaryOutput.setFont(new Font("Lucida Grande", |
||||
|
Font.BOLD, 13)); |
||||
|
GridBagConstraints gbc_lblDictionaryOutput = new GridBagConstraints(); |
||||
|
gbc_lblDictionaryOutput.anchor = GridBagConstraints.EAST; |
||||
|
gbc_lblDictionaryOutput.insets = new Insets(0, 0, 0, 5); |
||||
|
gbc_lblDictionaryOutput.gridx = 0; |
||||
|
gbc_lblDictionaryOutput.gridy = 2; |
||||
|
panel.add(lblDictionaryOutput, gbc_lblDictionaryOutput); |
||||
|
} |
||||
|
{ |
||||
|
cbDictOutput = new JComboBox(dictOutputChoices); |
||||
|
cbDictOutput.setSelectedIndex(dictOutputExists ? LanguageCodes |
||||
|
.codeToIndex(dictOutput) |
||||
|
: (dictOutputChoices.length - 1)); |
||||
|
GridBagConstraints gbc_cbDictOutput = new GridBagConstraints(); |
||||
|
gbc_cbDictOutput.fill = GridBagConstraints.HORIZONTAL; |
||||
|
gbc_cbDictOutput.gridx = 1; |
||||
|
gbc_cbDictOutput.gridy = 2; |
||||
|
panel.add(cbDictOutput, gbc_cbDictOutput); |
||||
|
} |
||||
|
} |
||||
|
{ |
||||
|
JPanel buttonPane = new JPanel(); |
||||
|
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); |
||||
|
getContentPane().add(buttonPane, BorderLayout.SOUTH); |
||||
|
{ |
||||
|
okButton = new JButton("OK"); |
||||
|
okButton.addActionListener(this); |
||||
|
buttonPane.add(okButton); |
||||
|
getRootPane().setDefaultButton(okButton); |
||||
|
} |
||||
|
{ |
||||
|
cancelButton = new JButton("Cancel"); |
||||
|
cancelButton.addActionListener(this); |
||||
|
buttonPane.add(cancelButton); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void actionPerformed(ActionEvent event) |
||||
|
{ |
||||
|
Object source = event.getSource(); |
||||
|
|
||||
|
if (source == okButton) |
||||
|
{ |
||||
|
int localeIndex = cbLanguage.getSelectedIndex(); |
||||
|
if ((localeIndex >= 0) && (localeIndex < LanguageCodes.code.length)) |
||||
|
locale = LanguageCodes.code[localeIndex]; |
||||
|
|
||||
|
int dictInputIndex = cbDictInput.getSelectedIndex(); |
||||
|
if ((dictInputIndex >= 0) && (dictInputIndex < LanguageCodes.code.length)) |
||||
|
dictInput = LanguageCodes.code[dictInputIndex]; |
||||
|
|
||||
|
int dictOutputIndex = cbDictOutput.getSelectedIndex(); |
||||
|
if ((dictOutputIndex >= 0) && (dictOutputIndex < LanguageCodes.code.length)) |
||||
|
dictOutput = LanguageCodes.code[dictOutputIndex]; |
||||
|
|
||||
|
model.setLanguages(locale, dictInput, dictOutput); |
||||
|
|
||||
|
setVisible(false); |
||||
|
dispose(); |
||||
|
} |
||||
|
else if (source == cancelButton) |
||||
|
{ |
||||
|
setVisible(false); |
||||
|
dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
public interface LanguageModel |
||||
|
{ |
||||
|
public int getLocale(); |
||||
|
|
||||
|
public int getDictInput(); |
||||
|
|
||||
|
public int getDictOutput(); |
||||
|
|
||||
|
public void setLanguages(int locale, int dictInput, int dictOutput); |
||||
|
} |
@ -0,0 +1,667 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
import java.io.*; |
||||
|
import java.util.HashSet; |
||||
|
import java.awt.EventQueue; |
||||
|
|
||||
|
import javax.swing.JFrame; |
||||
|
import javax.swing.JOptionPane; |
||||
|
|
||||
|
import java.awt.GridBagLayout; |
||||
|
import javax.swing.JLabel; |
||||
|
|
||||
|
import java.awt.FileDialog; |
||||
|
import java.awt.GridBagConstraints; |
||||
|
import java.awt.Insets; |
||||
|
import java.awt.Rectangle; |
||||
|
|
||||
|
import javax.swing.JSeparator; |
||||
|
import javax.swing.JPanel; |
||||
|
import java.awt.BorderLayout; |
||||
|
import java.awt.Component; |
||||
|
import javax.swing.Box; |
||||
|
import javax.swing.SwingConstants; |
||||
|
import java.awt.Font; |
||||
|
import javax.swing.JTextField; |
||||
|
import java.awt.Color; |
||||
|
import javax.swing.JScrollPane; |
||||
|
import java.awt.FlowLayout; |
||||
|
import javax.swing.JButton; |
||||
|
import javax.swing.JTable; |
||||
|
import javax.swing.event.DocumentEvent; |
||||
|
import javax.swing.event.DocumentListener; |
||||
|
import javax.swing.event.ListSelectionEvent; |
||||
|
import javax.swing.event.ListSelectionListener; |
||||
|
import javax.swing.event.TableModelEvent; |
||||
|
import javax.swing.event.TableModelListener; |
||||
|
import java.awt.event.ActionListener; |
||||
|
import java.awt.event.ActionEvent; |
||||
|
import java.awt.event.MouseAdapter; |
||||
|
import java.awt.event.MouseEvent; |
||||
|
|
||||
|
import javax.swing.JTextArea; |
||||
|
|
||||
|
import mobimeta.MobiCommon; |
||||
|
import javax.swing.JMenuBar; |
||||
|
import javax.swing.JMenu; |
||||
|
import javax.swing.JMenuItem; |
||||
|
|
||||
|
public class Main implements ListSelectionListener, ActionListener, |
||||
|
TableModelListener, LanguageModel |
||||
|
{ |
||||
|
private FileDialog openFileChooser = null; |
||||
|
private FileDialog saveFileChooser = null; |
||||
|
private JFrame frame; |
||||
|
private JTextArea lblInputFilename; |
||||
|
private JTextArea lblOutputFilename; |
||||
|
private JTextField tfFullName; |
||||
|
private JTable table; |
||||
|
private JButton buttonRemove; |
||||
|
private JButton buttonAdd; |
||||
|
private JButton buttonSave; |
||||
|
private JButton btnLanguage; |
||||
|
private JButton btnHeaderInfo; |
||||
|
private GuiModel model; |
||||
|
private File outputFile; |
||||
|
private boolean packHeader = false; |
||||
|
private JMenuItem mntmOpen; |
||||
|
private JMenuItem mntmSave; |
||||
|
|
||||
|
/** |
||||
|
* Launch the application. |
||||
|
*/ |
||||
|
public static void main(String[] args) |
||||
|
{ |
||||
|
System.setProperty("apple.laf.useScreenMenuBar", "true"); |
||||
|
System.setProperty("com.apple.mrj.application.apple.menu.about.name", |
||||
|
"Mobi Meta Editor"); |
||||
|
|
||||
|
HashSet<String> optionsSet = new HashSet<String>(); |
||||
|
File inputFile = null; |
||||
|
for (int i=0; i<args.length; i++) |
||||
|
{ |
||||
|
String arg = args[i]; |
||||
|
if (arg.startsWith("-")) |
||||
|
optionsSet.add(arg); |
||||
|
else if (inputFile == null) |
||||
|
inputFile = new File(arg); |
||||
|
else |
||||
|
printUsage(); |
||||
|
} |
||||
|
|
||||
|
if ((inputFile != null) && (!inputFile.exists() || !inputFile.isFile())) |
||||
|
{ |
||||
|
System.err.println("Input file " + inputFile.getAbsolutePath() |
||||
|
+ " does not exist or is not a file."); |
||||
|
System.exit(1); |
||||
|
} |
||||
|
|
||||
|
if (optionsSet.contains("-h") || optionsSet.contains("--help")) |
||||
|
printUsage(); |
||||
|
|
||||
|
if (optionsSet.contains("-s")) |
||||
|
MobiCommon.safeMode = true; |
||||
|
|
||||
|
if (optionsSet.contains("-d")) |
||||
|
MobiCommon.debug = true; |
||||
|
|
||||
|
// get around inner class mumbo-jumbo
|
||||
|
//
|
||||
|
final File f = inputFile; |
||||
|
|
||||
|
EventQueue.invokeLater(new Runnable() |
||||
|
{ |
||||
|
public void run() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
Main window = new Main(f); |
||||
|
window.frame.setVisible(true); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private static void printUsage() |
||||
|
{ |
||||
|
System.err.println("Usage: mobimeta [switches] [input file]"); |
||||
|
System.err.println(" -h\tthis message"); |
||||
|
System.err |
||||
|
.println(" -s\tsafe mode - does not change the size of the mobi header record"); |
||||
|
System.err.println(" -d\tdebug mode"); |
||||
|
System.exit(0); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Create the application. |
||||
|
*/ |
||||
|
public Main(File inputFile) |
||||
|
{ |
||||
|
model = new GuiModel(); |
||||
|
model.addTableModelListener(this); |
||||
|
initialize(); |
||||
|
if (inputFile == null) |
||||
|
inputFile = getSourceFile(); |
||||
|
|
||||
|
if (inputFile == null) |
||||
|
{ |
||||
|
showAlert("No mobi file selected."); |
||||
|
System.exit(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public void valueChanged(ListSelectionEvent e) |
||||
|
{ |
||||
|
if (!MobiCommon.safeMode) |
||||
|
buttonRemove.setEnabled(table.getSelectedRow() != -1); |
||||
|
} |
||||
|
|
||||
|
public void pickSaveTarget() |
||||
|
{ |
||||
|
if (saveFileChooser == null) |
||||
|
{ |
||||
|
saveFileChooser = new FileDialog(frame, "Select mobi file", FileDialog.SAVE); |
||||
|
saveFileChooser.setFilenameFilter(new MobiFileFilter()); |
||||
|
} |
||||
|
|
||||
|
if (outputFile != null) |
||||
|
{ |
||||
|
saveFileChooser.setDirectory(outputFile.getParent()); |
||||
|
saveFileChooser.setFile(outputFile.getName()); |
||||
|
} |
||||
|
|
||||
|
saveFileChooser.setVisible(true); |
||||
|
|
||||
|
if (saveFileChooser.getFile() != null) |
||||
|
{ |
||||
|
outputFile = new File(saveFileChooser.getDirectory(), saveFileChooser.getFile()); |
||||
|
lblOutputFilename.setText(outputFile.getAbsolutePath()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void actionPerformed(ActionEvent event) |
||||
|
{ |
||||
|
Object source = event.getSource(); |
||||
|
|
||||
|
if (source == buttonAdd) |
||||
|
{ |
||||
|
NewRecordDialog dialog = new NewRecordDialog(frame, model); |
||||
|
dialog.setVisible(true); |
||||
|
} |
||||
|
else if (source == buttonRemove) |
||||
|
{ |
||||
|
int row = table.getSelectedRow(); |
||||
|
if (row == -1) return; |
||||
|
model.removeRecordAtRow(row); |
||||
|
} |
||||
|
else if ((source == buttonSave) || (source == mntmSave)) |
||||
|
{ |
||||
|
if (packHeader) model.setFullName(tfFullName.getText()); |
||||
|
try |
||||
|
{ |
||||
|
File tmpOutput = null; |
||||
|
if (lblInputFilename.getText().equals(lblOutputFilename.getText())) |
||||
|
tmpOutput = File.createTempFile("mobimeta", ".mobi"); |
||||
|
else |
||||
|
tmpOutput = outputFile; |
||||
|
|
||||
|
model.save(tmpOutput, packHeader); |
||||
|
if (!tmpOutput.equals(outputFile)) |
||||
|
{ |
||||
|
if (!tmpOutput.renameTo(outputFile)) |
||||
|
{ |
||||
|
showAlert("Error renaming temp file to " + outputFile.getAbsolutePath()); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
setWindowChangedStatus(false); |
||||
|
showAlert("File saved."); |
||||
|
} |
||||
|
catch (GuiException e) |
||||
|
{ |
||||
|
showAlert(e.getMessage()); |
||||
|
} |
||||
|
catch (IOException e) |
||||
|
{ |
||||
|
showAlert("Could not create temp file for writing: " + e.getMessage()); |
||||
|
} |
||||
|
} |
||||
|
else if (source == btnLanguage) |
||||
|
{ |
||||
|
LanguageDialog dialog = new LanguageDialog(frame, this); |
||||
|
dialog.setVisible(true); |
||||
|
} |
||||
|
else if (source == btnHeaderInfo) |
||||
|
{ |
||||
|
HeaderInfoDialog dialog = new HeaderInfoDialog(frame, model); |
||||
|
dialog.setVisible(true); |
||||
|
} |
||||
|
else if (source == mntmOpen) |
||||
|
{ |
||||
|
getSourceFile(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Initialize the contents of the frame. |
||||
|
*/ |
||||
|
private void initialize() |
||||
|
{ |
||||
|
frame = new JFrame(); |
||||
|
frame.setTitle("Mobi Meta Editor"); |
||||
|
frame.setBounds(50, 50, 800, 700); |
||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
||||
|
frame.getContentPane().setLayout(new BorderLayout(0, 0)); |
||||
|
|
||||
|
Component horizontalStrut = Box.createHorizontalStrut(20); |
||||
|
frame.getContentPane().add(horizontalStrut, BorderLayout.WEST); |
||||
|
|
||||
|
Component horizontalStrut_1 = Box.createHorizontalStrut(20); |
||||
|
frame.getContentPane().add(horizontalStrut_1, BorderLayout.EAST); |
||||
|
|
||||
|
Component verticalStrut = Box.createVerticalStrut(20); |
||||
|
frame.getContentPane().add(verticalStrut, BorderLayout.NORTH); |
||||
|
|
||||
|
Component verticalStrut_1 = Box.createVerticalStrut(20); |
||||
|
frame.getContentPane().add(verticalStrut_1, BorderLayout.SOUTH); |
||||
|
|
||||
|
JPanel panel = new JPanel(); |
||||
|
frame.getContentPane().add(panel, BorderLayout.CENTER); |
||||
|
GridBagLayout gbl_panel = new GridBagLayout(); |
||||
|
gbl_panel.columnWidths = new int[] |
||||
|
{ 0, 0, 0, 0, 0 }; |
||||
|
gbl_panel.rowHeights = new int[] |
||||
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
||||
|
gbl_panel.columnWeights = new double[] |
||||
|
{ 1.0, 0.0, 1.0, 0.0, Double.MIN_VALUE }; |
||||
|
gbl_panel.rowWeights = new double[] |
||||
|
{ 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, Double.MIN_VALUE }; |
||||
|
panel.setLayout(gbl_panel); |
||||
|
|
||||
|
JLabel lblInput = new JLabel("Input Filename"); |
||||
|
lblInput.setFont(new Font("Lucida Grande", Font.BOLD, 13)); |
||||
|
lblInput.setVerticalAlignment(SwingConstants.TOP); |
||||
|
GridBagConstraints gbc_lblInput = new GridBagConstraints(); |
||||
|
gbc_lblInput.anchor = GridBagConstraints.NORTHEAST; |
||||
|
gbc_lblInput.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblInput.gridx = 0; |
||||
|
gbc_lblInput.gridy = 0; |
||||
|
panel.add(lblInput, gbc_lblInput); |
||||
|
|
||||
|
Component horizontalStrut_2 = Box.createHorizontalStrut(20); |
||||
|
GridBagConstraints gbc_horizontalStrut_2 = new GridBagConstraints(); |
||||
|
gbc_horizontalStrut_2.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_horizontalStrut_2.gridx = 1; |
||||
|
gbc_horizontalStrut_2.gridy = 0; |
||||
|
panel.add(horizontalStrut_2, gbc_horizontalStrut_2); |
||||
|
|
||||
|
lblInputFilename = new JTextArea(0, 40); |
||||
|
lblInputFilename.setEditable(false); |
||||
|
lblInputFilename.setLineWrap(true); |
||||
|
GridBagConstraints gbc_lblInputFilename = new GridBagConstraints(); |
||||
|
gbc_lblInputFilename.fill = GridBagConstraints.BOTH; |
||||
|
gbc_lblInputFilename.anchor = GridBagConstraints.NORTHWEST; |
||||
|
gbc_lblInputFilename.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblInputFilename.gridx = 2; |
||||
|
gbc_lblInputFilename.gridy = 0; |
||||
|
panel.add(lblInputFilename, gbc_lblInputFilename); |
||||
|
|
||||
|
JLabel lblOutput = new JLabel("Output Filename"); |
||||
|
lblOutput.setFont(new Font("Lucida Grande", Font.BOLD, 13)); |
||||
|
lblOutput.setVerticalAlignment(SwingConstants.TOP); |
||||
|
lblOutput.setHorizontalAlignment(SwingConstants.RIGHT); |
||||
|
GridBagConstraints gbc_lblOutput = new GridBagConstraints(); |
||||
|
gbc_lblOutput.anchor = GridBagConstraints.NORTHEAST; |
||||
|
gbc_lblOutput.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblOutput.gridx = 0; |
||||
|
gbc_lblOutput.gridy = 1; |
||||
|
panel.add(lblOutput, gbc_lblOutput); |
||||
|
|
||||
|
lblOutputFilename = new JTextArea(0, 40); |
||||
|
lblOutputFilename.setEditable(false); |
||||
|
lblOutputFilename.setLineWrap(true); |
||||
|
lblOutputFilename.addMouseListener(new MouseAdapter() |
||||
|
{ |
||||
|
public void mouseClicked(MouseEvent e) |
||||
|
{ |
||||
|
pickSaveTarget(); |
||||
|
} |
||||
|
}); |
||||
|
GridBagConstraints gbc_lblOutputFilename = new GridBagConstraints(); |
||||
|
gbc_lblOutputFilename.fill = GridBagConstraints.BOTH; |
||||
|
gbc_lblOutputFilename.anchor = GridBagConstraints.NORTHWEST; |
||||
|
gbc_lblOutputFilename.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblOutputFilename.gridx = 2; |
||||
|
gbc_lblOutputFilename.gridy = 1; |
||||
|
panel.add(lblOutputFilename, gbc_lblOutputFilename); |
||||
|
|
||||
|
JSeparator separator = new JSeparator(); |
||||
|
separator.setForeground(Color.DARK_GRAY); |
||||
|
GridBagConstraints gbc_separator = new GridBagConstraints(); |
||||
|
gbc_separator.fill = GridBagConstraints.BOTH; |
||||
|
gbc_separator.gridwidth = 3; |
||||
|
gbc_separator.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_separator.gridx = 0; |
||||
|
gbc_separator.gridy = 2; |
||||
|
panel.add(separator, gbc_separator); |
||||
|
|
||||
|
Component separatorStrut1 = Box.createVerticalStrut(10); |
||||
|
GridBagConstraints gbc_strut1 = new GridBagConstraints(); |
||||
|
gbc_strut1.fill = GridBagConstraints.VERTICAL; |
||||
|
gbc_strut1.gridwidth = 1; |
||||
|
gbc_strut1.insets = new Insets(0, 0, 5, 0); |
||||
|
gbc_strut1.gridx = 3; |
||||
|
gbc_strut1.gridy = 2; |
||||
|
panel.add(separatorStrut1, gbc_strut1); |
||||
|
|
||||
|
JLabel lblTitle = new JLabel("Full Name"); |
||||
|
lblTitle.setFont(new Font("Lucida Grande", Font.BOLD, 13)); |
||||
|
GridBagConstraints gbc_lblTitle = new GridBagConstraints(); |
||||
|
gbc_lblTitle.anchor = GridBagConstraints.NORTHEAST; |
||||
|
gbc_lblTitle.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblTitle.gridx = 0; |
||||
|
gbc_lblTitle.gridy = 3; |
||||
|
panel.add(lblTitle, gbc_lblTitle); |
||||
|
|
||||
|
tfFullName = new JTextField(15); |
||||
|
if (MobiCommon.safeMode) |
||||
|
tfFullName.setEditable(false); |
||||
|
else |
||||
|
{ |
||||
|
tfFullName.getDocument().addDocumentListener(new DocumentListener() |
||||
|
{ |
||||
|
public void changedUpdate(DocumentEvent arg0) |
||||
|
{ |
||||
|
packHeader = true; |
||||
|
setWindowChangedStatus(true); |
||||
|
} |
||||
|
|
||||
|
public void insertUpdate(DocumentEvent arg0) |
||||
|
{ |
||||
|
packHeader = true; |
||||
|
setWindowChangedStatus(true); |
||||
|
} |
||||
|
|
||||
|
public void removeUpdate(DocumentEvent arg0) |
||||
|
{ |
||||
|
packHeader = true; |
||||
|
setWindowChangedStatus(true); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
GridBagConstraints gbc_tfFullName = new GridBagConstraints(); |
||||
|
gbc_tfFullName.fill = GridBagConstraints.HORIZONTAL; |
||||
|
gbc_tfFullName.anchor = GridBagConstraints.NORTHWEST; |
||||
|
gbc_tfFullName.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_tfFullName.gridx = 2; |
||||
|
gbc_tfFullName.gridy = 3; |
||||
|
panel.add(tfFullName, gbc_tfFullName); |
||||
|
|
||||
|
JPanel panel_3 = new JPanel(); |
||||
|
GridBagConstraints gbc_panel_3 = new GridBagConstraints(); |
||||
|
gbc_panel_3.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_panel_3.fill = GridBagConstraints.BOTH; |
||||
|
gbc_panel_3.gridx = 2; |
||||
|
gbc_panel_3.gridy = 4; |
||||
|
panel.add(panel_3, gbc_panel_3); |
||||
|
|
||||
|
btnLanguage = new JButton("Language..."); |
||||
|
btnLanguage.addActionListener(this); |
||||
|
btnHeaderInfo = new JButton("Header Info..."); |
||||
|
btnHeaderInfo.addActionListener(this); |
||||
|
panel_3.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5)); |
||||
|
panel_3.add(btnLanguage); |
||||
|
panel_3.add(btnHeaderInfo); |
||||
|
|
||||
|
JLabel lblExthRecords = new JLabel("EXTH Records"); |
||||
|
lblExthRecords.setFont(new Font("Lucida Grande", Font.BOLD, 13)); |
||||
|
GridBagConstraints gbc_lblExthRecords = new GridBagConstraints(); |
||||
|
gbc_lblExthRecords.anchor = GridBagConstraints.NORTHEAST; |
||||
|
gbc_lblExthRecords.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblExthRecords.gridx = 0; |
||||
|
gbc_lblExthRecords.gridy = 5; |
||||
|
panel.add(lblExthRecords, gbc_lblExthRecords); |
||||
|
|
||||
|
JScrollPane scrollPane = new JScrollPane(); |
||||
|
GridBagConstraints gbc_scrollPane = new GridBagConstraints(); |
||||
|
gbc_scrollPane.fill = GridBagConstraints.BOTH; |
||||
|
gbc_scrollPane.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_scrollPane.gridx = 2; |
||||
|
gbc_scrollPane.gridy = 5; |
||||
|
panel.add(scrollPane, gbc_scrollPane); |
||||
|
|
||||
|
table = new CustomJTable(model); |
||||
|
table.getColumnModel().getColumn(0).setPreferredWidth(100); |
||||
|
table.getSelectionModel().addListSelectionListener(this); |
||||
|
scrollPane.setViewportView(table); |
||||
|
|
||||
|
JPanel panel_1 = new JPanel(); |
||||
|
GridBagConstraints gbc_panel_1 = new GridBagConstraints(); |
||||
|
gbc_panel_1.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_panel_1.anchor = GridBagConstraints.EAST; |
||||
|
gbc_panel_1.gridx = 2; |
||||
|
gbc_panel_1.gridy = 6; |
||||
|
panel.add(panel_1, gbc_panel_1); |
||||
|
panel_1.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); |
||||
|
|
||||
|
buttonAdd = new JButton("+"); |
||||
|
buttonAdd.addActionListener(this); |
||||
|
panel_1.add(buttonAdd); |
||||
|
|
||||
|
buttonRemove = new JButton("-"); |
||||
|
buttonRemove.addActionListener(this); |
||||
|
buttonRemove.setEnabled(false); |
||||
|
panel_1.add(buttonRemove); |
||||
|
|
||||
|
if (MobiCommon.safeMode) |
||||
|
{ |
||||
|
buttonAdd.setEnabled(false); |
||||
|
buttonRemove.setEnabled(false); |
||||
|
} |
||||
|
|
||||
|
JSeparator separator_1 = new JSeparator(); |
||||
|
separator_1.setForeground(Color.DARK_GRAY); |
||||
|
GridBagConstraints gbc_separator_1 = new GridBagConstraints(); |
||||
|
gbc_separator_1.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_separator_1.fill = GridBagConstraints.HORIZONTAL; |
||||
|
gbc_separator_1.gridwidth = 3; |
||||
|
gbc_separator_1.gridx = 0; |
||||
|
gbc_separator_1.gridy = 7; |
||||
|
panel.add(separator_1, gbc_separator_1); |
||||
|
|
||||
|
Component separatorStrut2 = Box.createVerticalStrut(10); |
||||
|
GridBagConstraints gbc_strut2 = new GridBagConstraints(); |
||||
|
gbc_strut2.fill = GridBagConstraints.VERTICAL; |
||||
|
gbc_strut2.gridwidth = 1; |
||||
|
gbc_strut2.insets = new Insets(0, 0, 5, 0); |
||||
|
gbc_strut2.gridx = 3; |
||||
|
gbc_strut2.gridy = 7; |
||||
|
panel.add(separatorStrut2, gbc_strut2); |
||||
|
|
||||
|
JPanel panel_2 = new JPanel(); |
||||
|
GridBagConstraints gbc_panel_2 = new GridBagConstraints(); |
||||
|
gbc_panel_2.insets = new Insets(0, 0, 0, 5); |
||||
|
gbc_panel_2.anchor = GridBagConstraints.WEST; |
||||
|
gbc_panel_2.gridwidth = 3; |
||||
|
gbc_panel_2.fill = GridBagConstraints.VERTICAL; |
||||
|
gbc_panel_2.gridx = 0; |
||||
|
gbc_panel_2.gridy = 8; |
||||
|
panel.add(panel_2, gbc_panel_2); |
||||
|
|
||||
|
buttonSave = new JButton("Save"); |
||||
|
buttonSave.addActionListener(this); |
||||
|
panel_2.add(buttonSave); |
||||
|
|
||||
|
JMenuBar menuBar = new JMenuBar(); |
||||
|
frame.setJMenuBar(menuBar); |
||||
|
|
||||
|
JMenu mnFile = new JMenu("File"); |
||||
|
menuBar.add(mnFile); |
||||
|
|
||||
|
mntmOpen = new JMenuItem("Open..."); |
||||
|
mntmOpen.addActionListener(this); |
||||
|
mnFile.add(mntmOpen); |
||||
|
|
||||
|
mntmSave = new JMenuItem("Save"); |
||||
|
mntmSave.addActionListener(this); |
||||
|
mnFile.add(mntmSave); |
||||
|
} |
||||
|
|
||||
|
private File getSourceFile() |
||||
|
{ |
||||
|
if (openFileChooser == null) |
||||
|
{ |
||||
|
openFileChooser = new FileDialog(frame, "Select mobi file", FileDialog.LOAD); |
||||
|
openFileChooser.setFilenameFilter(new MobiFileFilter()); |
||||
|
} |
||||
|
|
||||
|
openFileChooser.setVisible(true); |
||||
|
|
||||
|
File source = null; |
||||
|
String dir = openFileChooser.getDirectory(); |
||||
|
String file = openFileChooser.getFile(); |
||||
|
if ((dir != null) && (file != null)) |
||||
|
source = new File(dir, file); |
||||
|
|
||||
|
if (source != null) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
model.setModel(source); |
||||
|
} |
||||
|
catch (GuiException e) |
||||
|
{ |
||||
|
showAlert("Could not parse mobi file: " + e.getMessage()); |
||||
|
System.exit(0); |
||||
|
} |
||||
|
|
||||
|
lblInputFilename.setText(source.getAbsolutePath()); |
||||
|
tfFullName.setText(model.getFullName()); |
||||
|
|
||||
|
outputFile = getOutputFile(source); |
||||
|
lblOutputFilename.setText(outputFile.getAbsolutePath()); |
||||
|
|
||||
|
// we trigger the window modified indicator when we set tfFullName
|
||||
|
//
|
||||
|
setWindowChangedStatus(false); |
||||
|
packHeader = false; |
||||
|
} |
||||
|
|
||||
|
return source; |
||||
|
} |
||||
|
|
||||
|
private File getOutputFile(File inputFile) |
||||
|
{ |
||||
|
File parent = inputFile.getParentFile(); |
||||
|
String inputName = inputFile.getName(); |
||||
|
int dot = inputName.lastIndexOf('.'); |
||||
|
String outputName; |
||||
|
|
||||
|
if (dot == -1) |
||||
|
outputName = inputName + "_new"; |
||||
|
else |
||||
|
outputName = inputName.substring(0, dot) + "_new" |
||||
|
+ inputName.substring(dot); |
||||
|
|
||||
|
return new File(parent, outputName); |
||||
|
} |
||||
|
|
||||
|
private void showAlert(String message) |
||||
|
{ |
||||
|
JOptionPane.showMessageDialog(frame, message); |
||||
|
} |
||||
|
|
||||
|
// scroll to the newly added/deleted row
|
||||
|
//
|
||||
|
public void tableChanged(TableModelEvent event) |
||||
|
{ |
||||
|
int eventType = event.getType(); |
||||
|
int row = event.getLastRow(); |
||||
|
if (eventType == TableModelEvent.INSERT) |
||||
|
{ |
||||
|
table.getSelectionModel().setSelectionInterval(row, row); |
||||
|
table.scrollRectToVisible(new Rectangle(table.getCellRect(row, 0, |
||||
|
true))); |
||||
|
packHeader = true; |
||||
|
setWindowChangedStatus(true); |
||||
|
} |
||||
|
else if (eventType == TableModelEvent.DELETE) |
||||
|
{ |
||||
|
boolean select = false; |
||||
|
int numRows = model.getRowCount(); |
||||
|
if (numRows > row) |
||||
|
{ |
||||
|
select = true; |
||||
|
} |
||||
|
else if (numRows > 0) |
||||
|
{ |
||||
|
select = true; |
||||
|
row = numRows - 1; |
||||
|
} |
||||
|
if (select) |
||||
|
{ |
||||
|
table.getSelectionModel().setSelectionInterval(row, row); |
||||
|
table.scrollRectToVisible(new Rectangle(table.getCellRect(row, |
||||
|
0, true))); |
||||
|
} |
||||
|
packHeader = true; |
||||
|
setWindowChangedStatus(true); |
||||
|
} |
||||
|
else if (eventType == TableModelEvent.UPDATE) |
||||
|
{ |
||||
|
packHeader = true; |
||||
|
setWindowChangedStatus(true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void setWindowChangedStatus(boolean status) |
||||
|
{ |
||||
|
frame.getRootPane().putClientProperty("Window.documentModified", |
||||
|
Boolean.valueOf(status)); |
||||
|
} |
||||
|
|
||||
|
// we implement the LanguageModel interface because we want to intercept the
|
||||
|
// setLanguages() call so that we can set the window status changed flag
|
||||
|
//
|
||||
|
public int getLocale() |
||||
|
{ |
||||
|
return model.getLocale(); |
||||
|
} |
||||
|
|
||||
|
// we implement the LanguageModel interface because we want to intercept the
|
||||
|
// setLanguages() call so that we can set the window status changed flag
|
||||
|
//
|
||||
|
public int getDictInput() |
||||
|
{ |
||||
|
return model.getDictInput(); |
||||
|
} |
||||
|
|
||||
|
// we implement the LanguageModel interface because we want to intercept the
|
||||
|
// setLanguages() call so that we can set the window status changed flag
|
||||
|
//
|
||||
|
public int getDictOutput() |
||||
|
{ |
||||
|
return model.getDictOutput(); |
||||
|
} |
||||
|
|
||||
|
// we implement the LanguageModel interface because we want to intercept the
|
||||
|
// setLanguages() call so that we can set the window status changed flag
|
||||
|
//
|
||||
|
public void setLanguages(int locale, int dictInput, int dictOutput) |
||||
|
{ |
||||
|
model.setLanguages(locale, dictInput, dictOutput); |
||||
|
setWindowChangedStatus(true); |
||||
|
} |
||||
|
protected JMenuItem getMntmOpen() { |
||||
|
return mntmOpen; |
||||
|
} |
||||
|
protected JMenuItem getMntmSave() { |
||||
|
return mntmSave; |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
public interface MetaInfoProvider |
||||
|
{ |
||||
|
public String getMetaInfo(); |
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FilenameFilter; |
||||
|
|
||||
|
import javax.swing.filechooser.FileFilter; |
||||
|
|
||||
|
class MobiFileFilter extends FileFilter implements FilenameFilter |
||||
|
{ |
||||
|
|
||||
|
// to make it work with JFileChooser
|
||||
|
//
|
||||
|
public boolean accept(File f) |
||||
|
{ |
||||
|
if (f.isDirectory()) return true; |
||||
|
|
||||
|
return (f.getName().toLowerCase().endsWith(".azw") || f.getName().toLowerCase().endsWith(".mobi")); |
||||
|
} |
||||
|
|
||||
|
public String getDescription() |
||||
|
{ |
||||
|
return "*.azw,*.mobi"; |
||||
|
} |
||||
|
|
||||
|
// to make it work with java.awt.FileDialog
|
||||
|
//
|
||||
|
public boolean accept(File f, String name) |
||||
|
{ |
||||
|
return (name.toLowerCase().endsWith(".azw") || name.toLowerCase().endsWith(".mobi")); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,175 @@ |
|||||
|
package gui; |
||||
|
|
||||
|
import java.awt.BorderLayout; |
||||
|
import java.awt.FlowLayout; |
||||
|
|
||||
|
import javax.swing.JButton; |
||||
|
import javax.swing.JDialog; |
||||
|
import javax.swing.JFrame; |
||||
|
import javax.swing.JPanel; |
||||
|
import javax.swing.border.EmptyBorder; |
||||
|
import javax.swing.JLabel; |
||||
|
import java.awt.GridBagLayout; |
||||
|
import java.awt.GridBagConstraints; |
||||
|
import javax.swing.JComboBox; |
||||
|
import java.awt.Insets; |
||||
|
import java.awt.Font; |
||||
|
import java.awt.event.ActionEvent; |
||||
|
import java.awt.event.ActionListener; |
||||
|
|
||||
|
import javax.swing.JTextField; |
||||
|
|
||||
|
import mobimeta.EXTHRecord; |
||||
|
|
||||
|
public class NewRecordDialog extends JDialog implements ActionListener |
||||
|
{ |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
*/ |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
private final JPanel contentPanel = new JPanel(); |
||||
|
private JTextField tfValue; |
||||
|
private JComboBox typeCombo; |
||||
|
private EXTHAddRecordListener listener; |
||||
|
private JButton addButton; |
||||
|
private JButton cancelButton; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Create the dialog. |
||||
|
*/ |
||||
|
public NewRecordDialog(JFrame parent, EXTHAddRecordListener listener) |
||||
|
{ |
||||
|
super(parent, true); |
||||
|
|
||||
|
this.listener = listener; |
||||
|
String[] comboValues = new String[EXTHRecord.knownTypes.length]; |
||||
|
for (int i=0; i<comboValues.length; i++) |
||||
|
{ |
||||
|
comboValues[i] = EXTHRecord.knownTypes[i] + " (" + EXTHRecord.knownDesc[i] + ")"; |
||||
|
} |
||||
|
|
||||
|
setBounds(100, 100, 450, 300); |
||||
|
getContentPane().setLayout(new BorderLayout()); |
||||
|
contentPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); |
||||
|
getContentPane().add(contentPanel, BorderLayout.CENTER); |
||||
|
GridBagLayout gbl_contentPanel = new GridBagLayout(); |
||||
|
gbl_contentPanel.columnWidths = new int[]{0, 0}; |
||||
|
gbl_contentPanel.rowHeights = new int[]{0, 0, 0}; |
||||
|
gbl_contentPanel.columnWeights = new double[]{0.0, 1.0}; |
||||
|
gbl_contentPanel.rowWeights = new double[]{0.0, 0.0, Double.MIN_VALUE}; |
||||
|
contentPanel.setLayout(gbl_contentPanel); |
||||
|
{ |
||||
|
JLabel lblType = new JLabel("Type"); |
||||
|
lblType.setFont(new Font("Lucida Grande", Font.BOLD, 13)); |
||||
|
GridBagConstraints gbc_lblType = new GridBagConstraints(); |
||||
|
gbc_lblType.anchor = GridBagConstraints.EAST; |
||||
|
gbc_lblType.insets = new Insets(0, 0, 5, 5); |
||||
|
gbc_lblType.gridx = 0; |
||||
|
gbc_lblType.gridy = 0; |
||||
|
contentPanel.add(lblType, gbc_lblType); |
||||
|
} |
||||
|
{ |
||||
|
typeCombo = new JComboBox(comboValues); |
||||
|
GridBagConstraints gbc_typeCombo = new GridBagConstraints(); |
||||
|
gbc_typeCombo.insets = new Insets(0, 0, 5, 0); |
||||
|
gbc_typeCombo.fill = GridBagConstraints.HORIZONTAL; |
||||
|
gbc_typeCombo.gridx = 1; |
||||
|
gbc_typeCombo.gridy = 0; |
||||
|
contentPanel.add(typeCombo, gbc_typeCombo); |
||||
|
} |
||||
|
{ |
||||
|
JLabel lblValue = new JLabel("Value"); |
||||
|
lblValue.setFont(new Font("Lucida Grande", Font.BOLD, 13)); |
||||
|
GridBagConstraints gbc_lblValue = new GridBagConstraints(); |
||||
|
gbc_lblValue.anchor = GridBagConstraints.EAST; |
||||
|
gbc_lblValue.insets = new Insets(0, 0, 0, 5); |
||||
|
gbc_lblValue.gridx = 0; |
||||
|
gbc_lblValue.gridy = 1; |
||||
|
contentPanel.add(lblValue, gbc_lblValue); |
||||
|
} |
||||
|
{ |
||||
|
tfValue = new JTextField(); |
||||
|
GridBagConstraints gbc_tfValue = new GridBagConstraints(); |
||||
|
gbc_tfValue.fill = GridBagConstraints.HORIZONTAL; |
||||
|
gbc_tfValue.gridx = 1; |
||||
|
gbc_tfValue.gridy = 1; |
||||
|
contentPanel.add(tfValue, gbc_tfValue); |
||||
|
tfValue.setColumns(20); |
||||
|
} |
||||
|
{ |
||||
|
JPanel buttonPane = new JPanel(); |
||||
|
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); |
||||
|
getContentPane().add(buttonPane, BorderLayout.SOUTH); |
||||
|
{ |
||||
|
addButton = new JButton("Add"); |
||||
|
addButton.addActionListener(this); |
||||
|
buttonPane.add(addButton); |
||||
|
getRootPane().setDefaultButton(addButton); |
||||
|
} |
||||
|
{ |
||||
|
cancelButton = new JButton("Cancel"); |
||||
|
cancelButton.addActionListener(this); |
||||
|
buttonPane.add(cancelButton); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE); |
||||
|
} |
||||
|
|
||||
|
protected JComboBox getTypeCombo() { |
||||
|
return typeCombo; |
||||
|
} |
||||
|
|
||||
|
public void actionPerformed(ActionEvent event) |
||||
|
{ |
||||
|
Object source = event.getSource(); |
||||
|
|
||||
|
if (source == addButton) |
||||
|
{ |
||||
|
String value = tfValue.getText(); |
||||
|
if (value.length() == 0) return; |
||||
|
|
||||
|
int typeIndex = typeCombo.getSelectedIndex(); |
||||
|
if (typeIndex == -1) return; |
||||
|
|
||||
|
int type = EXTHRecord.knownTypes[typeIndex]; |
||||
|
EXTHRecord rec = null; |
||||
|
if (EXTHRecord.isBooleanType(type)) // this is an ugly hack - we really should present a checkbox to the user
|
||||
|
{ |
||||
|
boolean boolValue = false; |
||||
|
if (value.equals("1") |
||||
|
|| |
||||
|
value.toLowerCase().equals("true") |
||||
|
|| |
||||
|
value.toLowerCase().equals("on") |
||||
|
|| |
||||
|
value.toLowerCase().equals("yes")) |
||||
|
{ |
||||
|
boolValue = true; |
||||
|
} |
||||
|
rec = new EXTHRecord(type, boolValue); |
||||
|
} |
||||
|
else |
||||
|
rec = new EXTHRecord(type, value, GuiModel.getCharacterEncoding()); |
||||
|
listener.addEXTHRecord(rec); |
||||
|
setVisible(false); |
||||
|
dispose(); |
||||
|
} |
||||
|
else if (source == cancelButton) |
||||
|
{ |
||||
|
setVisible(false); |
||||
|
dispose(); |
||||
|
} |
||||
|
} |
||||
|
protected JButton getAddButton() { |
||||
|
return addButton; |
||||
|
} |
||||
|
protected JButton getCancelButton() { |
||||
|
return cancelButton; |
||||
|
} |
||||
|
protected JTextField getTfValue() { |
||||
|
return tfValue; |
||||
|
} |
||||
|
} |
@ -0,0 +1,185 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
import java.io.*; |
||||
|
import java.util.*; |
||||
|
|
||||
|
public class EXTHHeader |
||||
|
{ |
||||
|
private byte[] identifier = { 69, 88, 84, 72 }; |
||||
|
private byte[] headerLength = { 0, 0, 0, 0 }; |
||||
|
private byte[] recordCount = { 0, 0, 0, 0 }; |
||||
|
private List<EXTHRecord> recordList = null; |
||||
|
|
||||
|
public EXTHHeader() |
||||
|
{ |
||||
|
recordList = new LinkedList<EXTHRecord>(); |
||||
|
} |
||||
|
|
||||
|
public EXTHHeader(List<EXTHRecord> list) |
||||
|
{ |
||||
|
setRecordList(list); |
||||
|
} |
||||
|
|
||||
|
public EXTHHeader(InputStream in) throws IOException |
||||
|
{ |
||||
|
MobiCommon.logMessage("*** EXTHHeader ***"); |
||||
|
|
||||
|
StreamUtils.readByteArray(in, identifier); |
||||
|
if ((identifier[0] != 69) |
||||
|
|| |
||||
|
(identifier[1] != 88) |
||||
|
|| |
||||
|
(identifier[2] != 84) |
||||
|
|| |
||||
|
(identifier[3] != 72)) |
||||
|
{ |
||||
|
throw new IOException("Expected to find EXTH header identifier" |
||||
|
+ " EXTH but got something else instead"); |
||||
|
} |
||||
|
|
||||
|
StreamUtils.readByteArray(in, headerLength); |
||||
|
|
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("EXTH header length: " |
||||
|
+ StreamUtils.byteArrayToLong(headerLength)); |
||||
|
} |
||||
|
|
||||
|
StreamUtils.readByteArray(in, recordCount); |
||||
|
int count = StreamUtils.byteArrayToInt(recordCount); |
||||
|
MobiCommon.logMessage("EXTH record count: " + count); |
||||
|
|
||||
|
recordList = new LinkedList<EXTHRecord>(); |
||||
|
for (int i=0; i<count; i++) |
||||
|
{ |
||||
|
recordList.add(new EXTHRecord(in)); |
||||
|
} |
||||
|
|
||||
|
int padding = paddingSize(dataSize()); |
||||
|
MobiCommon.logMessage("padding size: " + padding); |
||||
|
for (int i=0; i<padding; i++) StreamUtils.readByte(in); |
||||
|
} |
||||
|
|
||||
|
public int size() |
||||
|
{ |
||||
|
int dataSize = dataSize(); |
||||
|
return 12 + dataSize + paddingSize(dataSize); |
||||
|
} |
||||
|
|
||||
|
public void recomputeFields() |
||||
|
{ |
||||
|
StreamUtils.intToByteArray(size(), headerLength); |
||||
|
StreamUtils.intToByteArray(recordList.size(), recordCount); |
||||
|
} |
||||
|
|
||||
|
public List<EXTHRecord> getRecordList() |
||||
|
{ |
||||
|
LinkedList<EXTHRecord> list = new LinkedList<EXTHRecord>(); |
||||
|
for (EXTHRecord rec : recordList) |
||||
|
{ |
||||
|
list.add(rec.copy()); |
||||
|
} |
||||
|
|
||||
|
return list; |
||||
|
} |
||||
|
|
||||
|
public void setRecordList(List<EXTHRecord> list) |
||||
|
{ |
||||
|
recordList = new LinkedList<EXTHRecord>(); |
||||
|
if (list != null) |
||||
|
{ |
||||
|
for (EXTHRecord rec : list) |
||||
|
{ |
||||
|
recordList.add(rec.copy()); |
||||
|
} |
||||
|
} |
||||
|
recomputeFields(); |
||||
|
} |
||||
|
|
||||
|
public void removeRecordsWithType(int type) |
||||
|
{ |
||||
|
boolean changed = false; |
||||
|
for (EXTHRecord rec : recordList) |
||||
|
{ |
||||
|
if (rec.getRecordType() == type) |
||||
|
{ |
||||
|
recordList.remove(rec); |
||||
|
changed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (changed) recomputeFields(); |
||||
|
} |
||||
|
|
||||
|
public boolean recordsWithTypeExist(int type) |
||||
|
{ |
||||
|
for (EXTHRecord rec : recordList) |
||||
|
{ |
||||
|
if (rec.getRecordType() == type) return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public void setAllRecordsWithTypeToString(int type, |
||||
|
String s, |
||||
|
String encoding) |
||||
|
{ |
||||
|
boolean changed = false; |
||||
|
for (EXTHRecord rec : recordList) |
||||
|
{ |
||||
|
if (rec.getRecordType() == type) |
||||
|
{ |
||||
|
rec.setData(s, encoding); |
||||
|
changed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (changed) recomputeFields(); |
||||
|
} |
||||
|
|
||||
|
public void addRecord(int recType, String s, String encoding) |
||||
|
{ |
||||
|
EXTHRecord rec = new EXTHRecord |
||||
|
(recType, StreamUtils.stringToByteArray(s, encoding)); |
||||
|
recordList.add(rec); |
||||
|
recomputeFields(); |
||||
|
} |
||||
|
|
||||
|
public void addRecord(int recType, byte[] buffer) |
||||
|
{ |
||||
|
recordList.add(new EXTHRecord(recType, buffer)); |
||||
|
recomputeFields(); |
||||
|
} |
||||
|
|
||||
|
protected int dataSize() |
||||
|
{ |
||||
|
int size = 0; |
||||
|
for (EXTHRecord rec : recordList) |
||||
|
{ |
||||
|
size += rec.size(); |
||||
|
} |
||||
|
|
||||
|
return size; |
||||
|
} |
||||
|
|
||||
|
protected int paddingSize(int dataSize) |
||||
|
{ |
||||
|
int paddingSize = dataSize % 4; |
||||
|
if (paddingSize != 0) paddingSize = 4 - paddingSize; |
||||
|
|
||||
|
return paddingSize; |
||||
|
} |
||||
|
|
||||
|
public void write(OutputStream out) throws IOException |
||||
|
{ |
||||
|
out.write(identifier); |
||||
|
out.write(headerLength); |
||||
|
out.write(recordCount); |
||||
|
for (EXTHRecord rec : recordList) |
||||
|
{ |
||||
|
rec.write(out); |
||||
|
} |
||||
|
int padding = paddingSize(dataSize()); |
||||
|
for (int i=0; i<padding; i++) out.write(0); |
||||
|
} |
||||
|
} |
@ -0,0 +1,251 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
import java.io.*; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.HashSet; |
||||
|
|
||||
|
public class EXTHRecord |
||||
|
{ |
||||
|
// if a type exists in booleanTypes, then it is assumed to have boolean
|
||||
|
// values
|
||||
|
// if a type exists in knownTypes but not in booleanTypes, then it is
|
||||
|
// assumed to have string values
|
||||
|
public final static int[] booleanTypes = |
||||
|
{ 404 }; |
||||
|
public final static int[] knownTypes = |
||||
|
{ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, |
||||
|
114, 118, 119, 200, 404, 501, 503, 504 }; |
||||
|
public final static String[] knownDesc = |
||||
|
{ "author", "publisher", "imprint", "description", "ISBN", "subject", |
||||
|
"publishing date", "review", "contributor", "rights", |
||||
|
"subject code", "type", "source", "ASIN", "version number", |
||||
|
"retail price", "retail price currency", "dictionary short name", |
||||
|
"TTS off", "CDE type", "updated title", "ASIN" }; |
||||
|
private static HashMap<Integer, String> typeHash; |
||||
|
private static HashSet<Integer> booleanTypesSet; |
||||
|
|
||||
|
private byte[] recordType = { 0, 0, 0, 0 }; |
||||
|
private byte[] recordLength = { 0, 0, 0, 0 }; |
||||
|
private byte[] recordData = null; |
||||
|
|
||||
|
static |
||||
|
{ |
||||
|
typeHash = new HashMap<Integer,String>(knownTypes.length); |
||||
|
for (int i=0; i<knownTypes.length; i++) |
||||
|
typeHash.put(Integer.valueOf(knownTypes[i]), knownDesc[i]); |
||||
|
|
||||
|
booleanTypesSet = new HashSet<Integer>(booleanTypes.length); |
||||
|
for (int i=0; i<booleanTypes.length; i++) |
||||
|
booleanTypesSet.add(Integer.valueOf(booleanTypes[i])); |
||||
|
} |
||||
|
|
||||
|
public static boolean isBooleanType(int type) |
||||
|
{ |
||||
|
return booleanTypesSet.contains(Integer.valueOf(type)); |
||||
|
} |
||||
|
|
||||
|
public static boolean isKnownType(int type) |
||||
|
{ |
||||
|
return typeHash.containsKey(Integer.valueOf(type)); |
||||
|
} |
||||
|
|
||||
|
public static String getDescriptionForType(int type) |
||||
|
{ |
||||
|
return typeHash.get(Integer.valueOf(type)); |
||||
|
} |
||||
|
|
||||
|
public EXTHRecord(int recType, String data, String characterEncoding) |
||||
|
{ |
||||
|
this(recType, StreamUtils.stringToByteArray(data, characterEncoding)); |
||||
|
} |
||||
|
|
||||
|
public EXTHRecord(int recType, boolean data) |
||||
|
{ |
||||
|
StreamUtils.intToByteArray(recType, recordType); |
||||
|
recordData = new byte[1]; |
||||
|
recordData[0] = data?(byte)1:0; |
||||
|
StreamUtils.intToByteArray(size(), recordLength); |
||||
|
} |
||||
|
|
||||
|
public EXTHRecord(int recType, byte[] data) |
||||
|
{ |
||||
|
StreamUtils.intToByteArray(recType, recordType); |
||||
|
int len = (data==null)?0:data.length; |
||||
|
StreamUtils.intToByteArray(len + 8, recordLength); |
||||
|
recordData = new byte[len]; |
||||
|
if (len > 0) |
||||
|
{ |
||||
|
System.arraycopy(data, 0, recordData, 0, len); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public EXTHRecord(InputStream in) throws IOException |
||||
|
{ |
||||
|
MobiCommon.logMessage("*** EXTHRecord ***"); |
||||
|
|
||||
|
StreamUtils.readByteArray(in, recordType); |
||||
|
StreamUtils.readByteArray(in, recordLength); |
||||
|
|
||||
|
int len = StreamUtils.byteArrayToInt(recordLength); |
||||
|
if (len < 8) throw new IOException("Invalid EXTH record length"); |
||||
|
|
||||
|
recordData = new byte[len - 8]; |
||||
|
StreamUtils.readByteArray(in, recordData); |
||||
|
|
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
int recType = StreamUtils.byteArrayToInt(recordType); |
||||
|
System.out.print("EXTH record type: "); |
||||
|
switch (recType) |
||||
|
{ |
||||
|
case 100: |
||||
|
MobiCommon.logMessage("author"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 101: |
||||
|
MobiCommon.logMessage("publisher"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 103: |
||||
|
MobiCommon.logMessage("description"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 104: |
||||
|
MobiCommon.logMessage("isbn"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 105: |
||||
|
MobiCommon.logMessage("subject"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 106: |
||||
|
MobiCommon.logMessage("publishingdate"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 109: |
||||
|
MobiCommon.logMessage("rights"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 113: |
||||
|
case 504: |
||||
|
MobiCommon.logMessage("asin"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 118: |
||||
|
MobiCommon.logMessage("retail price"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 119: |
||||
|
MobiCommon.logMessage("retail price currency"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 200: |
||||
|
MobiCommon.logMessage("dictionary short name"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
case 404: |
||||
|
MobiCommon.logMessage("text to speech"); |
||||
|
int ttsflag = StreamUtils.byteArrayToInt(recordData); |
||||
|
MobiCommon.logMessage((ttsflag == 0)?"enabled":"disabled"); |
||||
|
break; |
||||
|
case 501: |
||||
|
MobiCommon.logMessage("cdetype"); |
||||
|
MobiCommon.logMessage |
||||
|
(StreamUtils.byteArrayToString(recordData)); |
||||
|
break; |
||||
|
default: |
||||
|
MobiCommon.logMessage(Integer.toString(recType)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int getRecordType() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToInt(recordType); |
||||
|
} |
||||
|
|
||||
|
public byte[] getData() |
||||
|
{ |
||||
|
return recordData; |
||||
|
} |
||||
|
|
||||
|
public int getDataLength() |
||||
|
{ |
||||
|
return recordData.length; |
||||
|
} |
||||
|
|
||||
|
public int size() |
||||
|
{ |
||||
|
return getDataLength() + 8; |
||||
|
} |
||||
|
|
||||
|
public void setData(String s, String encoding) |
||||
|
{ |
||||
|
recordData = StreamUtils.stringToByteArray(s, encoding); |
||||
|
StreamUtils.intToByteArray(size(), recordLength); |
||||
|
} |
||||
|
|
||||
|
public void setData(int value) |
||||
|
{ |
||||
|
if (recordData == null) |
||||
|
{ |
||||
|
recordData = new byte[4]; |
||||
|
StreamUtils.intToByteArray(size(), recordLength); |
||||
|
} |
||||
|
|
||||
|
StreamUtils.intToByteArray(value, recordData); |
||||
|
} |
||||
|
|
||||
|
public void setData(boolean value) |
||||
|
{ |
||||
|
if (recordData == null) |
||||
|
{ |
||||
|
recordData = new byte[1]; |
||||
|
StreamUtils.intToByteArray(size(), recordLength); |
||||
|
} |
||||
|
|
||||
|
StreamUtils.intToByteArray(value?1:0, recordData); |
||||
|
} |
||||
|
|
||||
|
public EXTHRecord copy() |
||||
|
{ |
||||
|
return new EXTHRecord(StreamUtils.byteArrayToInt(recordType), |
||||
|
recordData); |
||||
|
} |
||||
|
|
||||
|
public boolean isKnownType() |
||||
|
{ |
||||
|
return isKnownType(StreamUtils.byteArrayToInt(recordType)); |
||||
|
} |
||||
|
|
||||
|
public String getTypeDescription() |
||||
|
{ |
||||
|
return getDescriptionForType(StreamUtils.byteArrayToInt(recordType)); |
||||
|
} |
||||
|
|
||||
|
public void write(OutputStream out) throws IOException |
||||
|
{ |
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("*** Write EXTHRecord ***"); |
||||
|
MobiCommon.logMessage(StreamUtils.dumpByteArray(recordType)); |
||||
|
MobiCommon.logMessage(StreamUtils.dumpByteArray(recordLength)); |
||||
|
MobiCommon.logMessage(StreamUtils.dumpByteArray(recordData)); |
||||
|
MobiCommon.logMessage("************************"); |
||||
|
} |
||||
|
out.write(recordType); |
||||
|
out.write(recordLength); |
||||
|
out.write(recordData); |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
public class MobiCommon |
||||
|
{ |
||||
|
public static boolean debug = false; |
||||
|
|
||||
|
// safe mode avoids changing the size of the mobi header
|
||||
|
//
|
||||
|
public static boolean safeMode = false; |
||||
|
|
||||
|
public static void logMessage(String message) |
||||
|
{ |
||||
|
if (debug) System.out.println(message); |
||||
|
} |
||||
|
} |
@ -0,0 +1,526 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
import java.io.*; |
||||
|
import java.util.*; |
||||
|
|
||||
|
public class MobiHeader |
||||
|
{ |
||||
|
private byte[] compression = { 0, 0 }; |
||||
|
private byte[] unused0 = { 0, 0 }; |
||||
|
private byte[] textLength = { 0, 0, 0, 0 }; |
||||
|
private byte[] recordCount = { 0, 0 }; |
||||
|
private byte[] recordSize = { 0, 0 }; |
||||
|
private byte[] encryptionType = { 0, 0 }; |
||||
|
private byte[] unused1 = { 0, 0 }; |
||||
|
private byte[] identifier = { 0, 0, 0, 0 }; |
||||
|
private byte[] headerLength = { 0, 0, 0, 0 }; // from offset 0x10
|
||||
|
private byte[] mobiType = { 0, 0, 0, 0 }; |
||||
|
private byte[] textEncoding = { 0, 0, 0, 0 }; |
||||
|
private byte[] uniqueID = { 0, 0, 0, 0 }; |
||||
|
private byte[] fileVersion = { 0, 0, 0, 0 }; |
||||
|
private byte[] orthographicIndex = { 0, 0, 0, 0 }; |
||||
|
private byte[] inflectionIndex = { 0, 0, 0, 0 }; |
||||
|
private byte[] indexNames = { 0, 0, 0, 0 }; |
||||
|
private byte[] indexKeys = { 0, 0, 0, 0 }; |
||||
|
private byte[] extraIndex0 = { 0, 0, 0, 0 }; |
||||
|
private byte[] extraIndex1 = { 0, 0, 0, 0 }; |
||||
|
private byte[] extraIndex2 = { 0, 0, 0, 0 }; |
||||
|
private byte[] extraIndex3 = { 0, 0, 0, 0 }; |
||||
|
private byte[] extraIndex4 = { 0, 0, 0, 0 }; |
||||
|
private byte[] extraIndex5 = { 0, 0, 0, 0 }; |
||||
|
private byte[] firstNonBookIndex = { 0, 0, 0, 0 }; |
||||
|
private byte[] fullNameOffset = { 0, 0, 0, 0 }; |
||||
|
private byte[] fullNameLength = { 0, 0, 0, 0 }; |
||||
|
private byte[] locale = { 0, 0, 0, 0 }; |
||||
|
private byte[] inputLanguage = { 0, 0, 0, 0 }; |
||||
|
private byte[] outputLanguage = { 0, 0, 0, 0 }; |
||||
|
private byte[] minVersion = { 0, 0, 0, 0 }; |
||||
|
private byte[] firstImageIndex = { 0, 0, 0, 0 }; |
||||
|
private byte[] huffmanRecordOffset = { 0, 0, 0, 0 }; |
||||
|
private byte[] huffmanRecordCount = { 0, 0, 0, 0 }; |
||||
|
private byte[] huffmanTableOffset = { 0, 0, 0, 0 }; |
||||
|
private byte[] huffmanTableLength = { 0, 0, 0, 0 }; |
||||
|
private byte[] exthFlags = { 0, 0, 0, 0 }; |
||||
|
private byte[] restOfMobiHeader = null; |
||||
|
private EXTHHeader exthHeader = null; |
||||
|
private byte[] remainder = null; |
||||
|
// end of useful data
|
||||
|
|
||||
|
|
||||
|
private byte[] fullName = null; |
||||
|
private String characterEncoding = null; |
||||
|
|
||||
|
public MobiHeader(InputStream in, long mobiHeaderSize) throws IOException |
||||
|
{ |
||||
|
MobiCommon.logMessage("*** MobiHeader ***"); |
||||
|
MobiCommon.logMessage("compression"); |
||||
|
StreamUtils.readByteArray(in, compression); |
||||
|
StreamUtils.readByteArray(in, unused0); |
||||
|
StreamUtils.readByteArray(in, textLength); |
||||
|
StreamUtils.readByteArray(in, recordCount); |
||||
|
StreamUtils.readByteArray(in, recordSize); |
||||
|
MobiCommon.logMessage("encryptionType"); |
||||
|
StreamUtils.readByteArray(in, encryptionType); |
||||
|
StreamUtils.readByteArray(in, unused1); |
||||
|
|
||||
|
StreamUtils.readByteArray(in, identifier); |
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("identifier: " |
||||
|
+ StreamUtils.byteArrayToString(identifier)); |
||||
|
} |
||||
|
if ((identifier[0] != 77) |
||||
|
|| |
||||
|
(identifier[1] != 79) |
||||
|
|| |
||||
|
(identifier[2] != 66) |
||||
|
|| |
||||
|
(identifier[3] != 73)) |
||||
|
{ |
||||
|
throw new IOException("Did not get expected MOBI identifier"); |
||||
|
} |
||||
|
|
||||
|
// this value will determine the size of restOfMobiHeader[]
|
||||
|
//
|
||||
|
StreamUtils.readByteArray(in, headerLength); |
||||
|
int headLen = StreamUtils.byteArrayToInt(headerLength); |
||||
|
restOfMobiHeader = new byte[headLen + 16 - 132]; |
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("headerLength: " + headLen); |
||||
|
} |
||||
|
|
||||
|
StreamUtils.readByteArray(in, mobiType); |
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("mobiType: " |
||||
|
+ StreamUtils.byteArrayToInt(mobiType)); |
||||
|
} |
||||
|
|
||||
|
StreamUtils.readByteArray(in, textEncoding); |
||||
|
switch (StreamUtils.byteArrayToInt(textEncoding)) |
||||
|
{ |
||||
|
case 1252: |
||||
|
characterEncoding = "Cp1252"; |
||||
|
break; |
||||
|
case 65001: |
||||
|
characterEncoding = "UTF-8"; |
||||
|
break; |
||||
|
default: |
||||
|
characterEncoding = null; |
||||
|
break; |
||||
|
} |
||||
|
MobiCommon.logMessage("text encoding: " + characterEncoding); |
||||
|
|
||||
|
StreamUtils.readByteArray(in, uniqueID); |
||||
|
StreamUtils.readByteArray(in, fileVersion); |
||||
|
StreamUtils.readByteArray(in, orthographicIndex); |
||||
|
StreamUtils.readByteArray(in, inflectionIndex); |
||||
|
StreamUtils.readByteArray(in, indexNames); |
||||
|
StreamUtils.readByteArray(in, indexKeys); |
||||
|
StreamUtils.readByteArray(in, extraIndex0); |
||||
|
StreamUtils.readByteArray(in, extraIndex1); |
||||
|
StreamUtils.readByteArray(in, extraIndex2); |
||||
|
StreamUtils.readByteArray(in, extraIndex3); |
||||
|
StreamUtils.readByteArray(in, extraIndex4); |
||||
|
StreamUtils.readByteArray(in, extraIndex5); |
||||
|
StreamUtils.readByteArray(in, firstNonBookIndex); |
||||
|
StreamUtils.readByteArray(in, fullNameOffset); |
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("full name offset: " |
||||
|
+ StreamUtils.byteArrayToInt(fullNameOffset)); |
||||
|
} |
||||
|
|
||||
|
StreamUtils.readByteArray(in, fullNameLength); |
||||
|
int fullNameLen = StreamUtils.byteArrayToInt(fullNameLength); |
||||
|
MobiCommon.logMessage("full name length: " + fullNameLen); |
||||
|
StreamUtils.readByteArray(in, locale); |
||||
|
StreamUtils.readByteArray(in, inputLanguage); |
||||
|
StreamUtils.readByteArray(in, outputLanguage); |
||||
|
StreamUtils.readByteArray(in, minVersion); |
||||
|
StreamUtils.readByteArray(in, firstImageIndex); |
||||
|
StreamUtils.readByteArray(in, huffmanRecordOffset); |
||||
|
StreamUtils.readByteArray(in, huffmanRecordCount); |
||||
|
StreamUtils.readByteArray(in, huffmanTableOffset); |
||||
|
StreamUtils.readByteArray(in, huffmanTableLength); |
||||
|
StreamUtils.readByteArray(in, exthFlags); |
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("exthFlags: " |
||||
|
+ StreamUtils.byteArrayToInt(exthFlags)); |
||||
|
} |
||||
|
boolean exthExists = ((StreamUtils.byteArrayToInt(exthFlags) & 0x40) |
||||
|
!= 0); |
||||
|
MobiCommon.logMessage("exthExists: " + exthExists); |
||||
|
StreamUtils.readByteArray(in, restOfMobiHeader); |
||||
|
|
||||
|
if (exthExists) |
||||
|
{ |
||||
|
exthHeader = new EXTHHeader(in); |
||||
|
} |
||||
|
|
||||
|
int currentOffset = 132 + restOfMobiHeader.length + exthHeaderSize(); |
||||
|
|
||||
|
remainder = new byte[(int)(mobiHeaderSize - currentOffset)]; |
||||
|
StreamUtils.readByteArray(in, remainder); |
||||
|
|
||||
|
int fullNameIndexInRemainder |
||||
|
= StreamUtils.byteArrayToInt(fullNameOffset) - currentOffset; |
||||
|
fullName = new byte[fullNameLen]; |
||||
|
MobiCommon.logMessage("fullNameIndexInRemainder: " |
||||
|
+ fullNameIndexInRemainder); |
||||
|
MobiCommon.logMessage("fullNameLen: " + fullNameLen); |
||||
|
|
||||
|
if ((fullNameIndexInRemainder >= 0) |
||||
|
&& |
||||
|
(fullNameIndexInRemainder < remainder.length) |
||||
|
&& |
||||
|
((fullNameIndexInRemainder + fullNameLen) <= remainder.length) |
||||
|
&& |
||||
|
(fullNameLen > 0)) |
||||
|
{ |
||||
|
System.arraycopy(remainder, |
||||
|
fullNameIndexInRemainder, |
||||
|
fullName, |
||||
|
0, |
||||
|
fullNameLen); |
||||
|
} |
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("full name: " |
||||
|
+ StreamUtils.byteArrayToString(fullName)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public String getCharacterEncoding() |
||||
|
{ |
||||
|
return characterEncoding; |
||||
|
} |
||||
|
|
||||
|
public String getFullName() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToString(fullName, characterEncoding); |
||||
|
} |
||||
|
|
||||
|
public void setFullName(String s) |
||||
|
{ |
||||
|
byte[] fullBytes = StreamUtils.stringToByteArray(s, characterEncoding); |
||||
|
int len = fullBytes.length; |
||||
|
StreamUtils.intToByteArray(len, fullNameLength); |
||||
|
|
||||
|
// the string must be terminated by 2 null bytes
|
||||
|
// then this must end in a 4-byte boundary
|
||||
|
//
|
||||
|
int padding = (len + 2) % 4; |
||||
|
if (padding != 0) padding = 4 - padding; |
||||
|
padding += 2; |
||||
|
|
||||
|
byte[] buffer = new byte[len + padding]; |
||||
|
System.arraycopy(fullBytes, 0, buffer, 0, len); |
||||
|
for (int i=len; i<buffer.length; i++) buffer[i] = 0; |
||||
|
|
||||
|
fullName = buffer; |
||||
|
} |
||||
|
|
||||
|
public int getLocale() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToInt(locale); |
||||
|
} |
||||
|
|
||||
|
public void setLocale(int localeInt) |
||||
|
{ |
||||
|
StreamUtils.intToByteArray(localeInt, locale); |
||||
|
} |
||||
|
|
||||
|
public int getInputLanguage() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToInt(inputLanguage); |
||||
|
} |
||||
|
|
||||
|
public void setInputLanguage(int input) |
||||
|
{ |
||||
|
StreamUtils.intToByteArray(input, inputLanguage); |
||||
|
} |
||||
|
|
||||
|
public int getOutputLanguage() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToInt(outputLanguage); |
||||
|
} |
||||
|
|
||||
|
public void setOutputLanguage(int output) |
||||
|
{ |
||||
|
StreamUtils.intToByteArray(output, outputLanguage); |
||||
|
} |
||||
|
|
||||
|
public List<EXTHRecord> getEXTHRecords() |
||||
|
{ |
||||
|
return (exthHeader == null) ? (new LinkedList<EXTHRecord>()) |
||||
|
: exthHeader.getRecordList(); |
||||
|
} |
||||
|
|
||||
|
public void setEXTHRecords(List<EXTHRecord> list) |
||||
|
{ |
||||
|
int flag = StreamUtils.byteArrayToInt(exthFlags) & 0xffffbf; |
||||
|
if ((list == null) || (list.size() == 0)) |
||||
|
{ |
||||
|
exthHeader = null; |
||||
|
StreamUtils.intToByteArray(flag, exthFlags); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (exthHeader == null) |
||||
|
exthHeader = new EXTHHeader(list); |
||||
|
else |
||||
|
exthHeader.setRecordList(list); |
||||
|
|
||||
|
StreamUtils.intToByteArray(flag | 0x40, exthFlags); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void pack() |
||||
|
{ |
||||
|
if (!MobiCommon.safeMode) |
||||
|
{ |
||||
|
// dump existing remainder, set to fullName
|
||||
|
remainder = new byte[fullName.length]; |
||||
|
System.arraycopy(fullName, 0, remainder, 0, remainder.length); |
||||
|
|
||||
|
// adjust fullNameOffset
|
||||
|
StreamUtils.intToByteArray(132 + restOfMobiHeader.length |
||||
|
+ exthHeaderSize(), fullNameOffset); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int size() |
||||
|
{ |
||||
|
return 132 + restOfMobiHeader.length + exthHeaderSize() + remainder.length; |
||||
|
} |
||||
|
|
||||
|
public String getCompression() |
||||
|
{ |
||||
|
int comp = StreamUtils.byteArrayToInt(compression); |
||||
|
switch (comp) |
||||
|
{ |
||||
|
case 1: |
||||
|
return "None"; |
||||
|
case 2: |
||||
|
return "PalmDOC"; |
||||
|
case 17480: |
||||
|
return "HUFF/CDIC"; |
||||
|
default: |
||||
|
return "Unknown (" + comp + ")"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public long getTextLength() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(textLength); |
||||
|
} |
||||
|
|
||||
|
public int getRecordCount() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToInt(recordCount); |
||||
|
} |
||||
|
|
||||
|
public int getRecordSize() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToInt(recordSize); |
||||
|
} |
||||
|
|
||||
|
public String getEncryptionType() |
||||
|
{ |
||||
|
int enc = StreamUtils.byteArrayToInt(encryptionType); |
||||
|
switch (enc) |
||||
|
{ |
||||
|
case 0: return "None"; |
||||
|
case 1: return "Old Mobipocket"; |
||||
|
case 2: return "Mobipocket"; |
||||
|
default: return "Unknown (" + enc + ")"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public long getHeaderLength() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(headerLength); |
||||
|
} |
||||
|
|
||||
|
public String getMobiType() |
||||
|
{ |
||||
|
long type = StreamUtils.byteArrayToLong(mobiType); |
||||
|
if (type == 2) |
||||
|
return "Mobipocket Book"; |
||||
|
else if (type == 3) |
||||
|
return "PalmDoc Book"; |
||||
|
else if (type == 4) |
||||
|
return "Audio"; |
||||
|
else if (type == 257) |
||||
|
return "News"; |
||||
|
else if (type == 258) |
||||
|
return "News Feed"; |
||||
|
else if (type == 259) |
||||
|
return "News Magazine"; |
||||
|
else if (type == 513) |
||||
|
return "PICS"; |
||||
|
else if (type == 514) |
||||
|
return "WORD"; |
||||
|
else if (type == 515) |
||||
|
return "XLS"; |
||||
|
else if (type == 516) |
||||
|
return "PPT"; |
||||
|
else if (type == 517) |
||||
|
return "TEXT"; |
||||
|
else if (type == 518) |
||||
|
return "HTML"; |
||||
|
else |
||||
|
return "Unknown (" + type + ")"; |
||||
|
} |
||||
|
|
||||
|
public long getUniqueID() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(uniqueID); |
||||
|
} |
||||
|
|
||||
|
public long getFileVersion() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(fileVersion); |
||||
|
} |
||||
|
|
||||
|
public long getOrthographicIndex() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(orthographicIndex); |
||||
|
} |
||||
|
|
||||
|
public long getInflectionIndex() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(inflectionIndex); |
||||
|
} |
||||
|
|
||||
|
public long getIndexNames() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(indexNames); |
||||
|
} |
||||
|
|
||||
|
public long getIndexKeys() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(indexKeys); |
||||
|
} |
||||
|
|
||||
|
public long getExtraIndex0() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(extraIndex0); |
||||
|
} |
||||
|
|
||||
|
public long getExtraIndex1() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(extraIndex1); |
||||
|
} |
||||
|
|
||||
|
public long getExtraIndex2() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(extraIndex2); |
||||
|
} |
||||
|
|
||||
|
public long getExtraIndex3() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(extraIndex3); |
||||
|
} |
||||
|
|
||||
|
public long getExtraIndex4() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(extraIndex4); |
||||
|
} |
||||
|
|
||||
|
public long getExtraIndex5() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(extraIndex5); |
||||
|
} |
||||
|
|
||||
|
public long getFirstNonBookIndex() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(firstNonBookIndex); |
||||
|
} |
||||
|
|
||||
|
public long getFullNameOffset() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(fullNameOffset); |
||||
|
} |
||||
|
|
||||
|
public long getFullNameLength() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(fullNameLength); |
||||
|
} |
||||
|
|
||||
|
public long getMinVersion() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(minVersion); |
||||
|
} |
||||
|
|
||||
|
public long getHuffmanRecordOffset() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(huffmanRecordOffset); |
||||
|
} |
||||
|
|
||||
|
public long getHuffmanRecordCount() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(huffmanRecordCount); |
||||
|
} |
||||
|
|
||||
|
public long getHuffmanTableOffset() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(huffmanTableOffset); |
||||
|
} |
||||
|
|
||||
|
public long getHuffmanTableLength() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(huffmanTableLength); |
||||
|
} |
||||
|
|
||||
|
private int exthHeaderSize() |
||||
|
{ |
||||
|
return (exthHeader == null)?0:exthHeader.size(); |
||||
|
} |
||||
|
|
||||
|
public void write(OutputStream out) throws IOException |
||||
|
{ |
||||
|
out.write(compression); |
||||
|
out.write(unused0); |
||||
|
out.write(textLength); |
||||
|
out.write(recordCount); |
||||
|
out.write(recordSize); |
||||
|
out.write(encryptionType); |
||||
|
out.write(unused1); |
||||
|
out.write(identifier); |
||||
|
out.write(headerLength); |
||||
|
out.write(mobiType); |
||||
|
out.write(textEncoding); |
||||
|
out.write(uniqueID); |
||||
|
out.write(fileVersion); |
||||
|
out.write(orthographicIndex); |
||||
|
out.write(inflectionIndex); |
||||
|
out.write(indexNames); |
||||
|
out.write(indexKeys); |
||||
|
out.write(extraIndex0); |
||||
|
out.write(extraIndex1); |
||||
|
out.write(extraIndex2); |
||||
|
out.write(extraIndex3); |
||||
|
out.write(extraIndex4); |
||||
|
out.write(extraIndex5); |
||||
|
out.write(firstNonBookIndex); |
||||
|
out.write(fullNameOffset); |
||||
|
out.write(fullNameLength); |
||||
|
out.write(locale); |
||||
|
out.write(inputLanguage); |
||||
|
out.write(outputLanguage); |
||||
|
out.write(minVersion); |
||||
|
out.write(firstImageIndex); |
||||
|
out.write(huffmanRecordOffset); |
||||
|
out.write(huffmanRecordCount); |
||||
|
out.write(huffmanTableOffset); |
||||
|
out.write(huffmanTableLength); |
||||
|
out.write(exthFlags); |
||||
|
out.write(restOfMobiHeader); |
||||
|
if (exthHeader != null) exthHeader.write(out); |
||||
|
out.write(remainder); |
||||
|
} |
||||
|
} |
@ -0,0 +1,292 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
import java.io.*; |
||||
|
import java.util.*; |
||||
|
|
||||
|
public class MobiMeta |
||||
|
{ |
||||
|
public final static int BUFFER_SIZE = 4096; |
||||
|
|
||||
|
protected PDBHeader pdbHeader; |
||||
|
protected MobiHeader mobiHeader; |
||||
|
protected String characterEncoding; |
||||
|
protected List<EXTHRecord> exthRecords; |
||||
|
private File inputFile; |
||||
|
|
||||
|
|
||||
|
public MobiMeta(File f) throws MobiMetaException |
||||
|
{ |
||||
|
inputFile = f; |
||||
|
FileInputStream in = null; |
||||
|
try |
||||
|
{ |
||||
|
in = new FileInputStream(f); |
||||
|
pdbHeader = new PDBHeader(in); |
||||
|
mobiHeader = new MobiHeader(in, pdbHeader.getMobiHeaderSize()); |
||||
|
exthRecords = mobiHeader.getEXTHRecords(); |
||||
|
characterEncoding = mobiHeader.getCharacterEncoding(); |
||||
|
} |
||||
|
catch (IOException e) |
||||
|
{ |
||||
|
throw new MobiMetaException("Could not parse mobi file " |
||||
|
+ f.getAbsolutePath() |
||||
|
+ ": " |
||||
|
+ e.getMessage()); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
if (in != null) try { in.close(); } catch (IOException e) {} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void saveToNewFile(File outputFile) throws MobiMetaException |
||||
|
{ |
||||
|
saveToNewFile(outputFile, true); |
||||
|
} |
||||
|
|
||||
|
public void saveToNewFile(File outputFile, boolean packHeader) throws MobiMetaException |
||||
|
{ |
||||
|
long readOffset = pdbHeader.getOffsetAfterMobiHeader(); |
||||
|
|
||||
|
if (!MobiCommon.safeMode && packHeader) |
||||
|
{ |
||||
|
mobiHeader.pack(); |
||||
|
pdbHeader.adjustOffsetsAfterMobiHeader(mobiHeader.size()); |
||||
|
} |
||||
|
|
||||
|
FileInputStream in = null; |
||||
|
FileOutputStream out = null; |
||||
|
try |
||||
|
{ |
||||
|
out = new FileOutputStream(outputFile); |
||||
|
pdbHeader.write(out); |
||||
|
mobiHeader.write(out); |
||||
|
|
||||
|
int bytesRead; |
||||
|
byte[] buffer = new byte[BUFFER_SIZE]; |
||||
|
in = new FileInputStream(inputFile); |
||||
|
in.skip(readOffset); |
||||
|
while ((bytesRead = in.read(buffer)) != -1) |
||||
|
{ |
||||
|
out.write(buffer, 0, bytesRead); |
||||
|
} |
||||
|
} |
||||
|
catch (IOException e) |
||||
|
{ |
||||
|
throw new MobiMetaException( |
||||
|
"Problems encountered while writing to " |
||||
|
+ outputFile.getAbsolutePath() + ": " |
||||
|
+ e.getMessage()); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
if (in != null) try { in.close(); } catch (IOException e) {} |
||||
|
if (out != null) try { out.close(); } catch (IOException e) {} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public String getCharacterEncoding() |
||||
|
{ |
||||
|
return mobiHeader.getCharacterEncoding(); |
||||
|
} |
||||
|
|
||||
|
public String getFullName() |
||||
|
{ |
||||
|
return mobiHeader.getFullName(); |
||||
|
} |
||||
|
|
||||
|
public void setFullName(String s) |
||||
|
{ |
||||
|
mobiHeader.setFullName(s); |
||||
|
} |
||||
|
|
||||
|
public List<EXTHRecord> getEXTHRecords() |
||||
|
{ |
||||
|
return exthRecords; |
||||
|
} |
||||
|
|
||||
|
public void setEXTHRecords() |
||||
|
{ |
||||
|
mobiHeader.setEXTHRecords(exthRecords); |
||||
|
} |
||||
|
|
||||
|
public int getLocale() |
||||
|
{ |
||||
|
return mobiHeader.getLocale(); |
||||
|
} |
||||
|
|
||||
|
public int getDictInput() |
||||
|
{ |
||||
|
return mobiHeader.getInputLanguage(); |
||||
|
} |
||||
|
|
||||
|
public int getDictOutput() |
||||
|
{ |
||||
|
return mobiHeader.getOutputLanguage(); |
||||
|
} |
||||
|
|
||||
|
public void setLanguages(int locale, int dictInput, int dictOutput) |
||||
|
{ |
||||
|
mobiHeader.setLocale(locale); |
||||
|
mobiHeader.setInputLanguage(dictInput); |
||||
|
mobiHeader.setOutputLanguage(dictOutput); |
||||
|
} |
||||
|
|
||||
|
public String getMetaInfo() |
||||
|
{ |
||||
|
StringBuffer sb = new StringBuffer(); |
||||
|
sb.append("PDB Header\r\n"); |
||||
|
sb.append("----------\r\n"); |
||||
|
sb.append("Name: "); |
||||
|
sb.append(pdbHeader.getName()); |
||||
|
sb.append("\r\n"); |
||||
|
String[] attributes = getPDBHeaderAttributes(); |
||||
|
if (attributes.length > 0) |
||||
|
{ |
||||
|
sb.append("Attributes: "); |
||||
|
for (int i=0; i<attributes.length; i++) |
||||
|
{ |
||||
|
if (i > 0) sb.append(", "); |
||||
|
sb.append(attributes[i]); |
||||
|
} |
||||
|
sb.append("\r\n"); |
||||
|
} |
||||
|
sb.append("Version: "); |
||||
|
sb.append(pdbHeader.getVersion()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Creation Date: "); |
||||
|
sb.append(pdbHeader.getCreationDate()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Modification Date: "); |
||||
|
sb.append(pdbHeader.getModificationDate()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Last Backup Date: "); |
||||
|
sb.append(pdbHeader.getLastBackupDate()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Modification Number: "); |
||||
|
sb.append(pdbHeader.getModificationNumber()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("App Info ID: "); |
||||
|
sb.append(pdbHeader.getAppInfoID()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Sort Info ID: "); |
||||
|
sb.append(pdbHeader.getSortInfoID()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Type: "); |
||||
|
sb.append(pdbHeader.getType()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Creator: "); |
||||
|
sb.append(pdbHeader.getCreator()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Unique ID Seed: "); |
||||
|
sb.append(pdbHeader.getUniqueIDSeed()); |
||||
|
sb.append("\r\n\r\n"); |
||||
|
|
||||
|
sb.append("PalmDOC Header\r\n"); |
||||
|
sb.append("--------------\r\n"); |
||||
|
sb.append("Compression: "); |
||||
|
sb.append(mobiHeader.getCompression()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Text Length: "); |
||||
|
sb.append(mobiHeader.getTextLength()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Record Count: "); |
||||
|
sb.append(mobiHeader.getRecordCount()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Record Size: "); |
||||
|
sb.append(mobiHeader.getRecordSize()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Encryption Type: "); |
||||
|
sb.append(mobiHeader.getEncryptionType()); |
||||
|
sb.append("\r\n\r\n"); |
||||
|
|
||||
|
sb.append("MOBI Header\r\n"); |
||||
|
sb.append("-----------\r\n"); |
||||
|
sb.append("Header Length: "); |
||||
|
sb.append(mobiHeader.getHeaderLength()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Mobi Type: "); |
||||
|
sb.append(mobiHeader.getMobiType()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Unique ID: "); |
||||
|
sb.append(mobiHeader.getUniqueID()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("File Version: "); |
||||
|
sb.append(mobiHeader.getFileVersion()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Orthographic Index: "); |
||||
|
sb.append(mobiHeader.getOrthographicIndex()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Inflection Index: "); |
||||
|
sb.append(mobiHeader.getInflectionIndex()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Index Names: "); |
||||
|
sb.append(mobiHeader.getIndexNames()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Index Keys: "); |
||||
|
sb.append(mobiHeader.getIndexKeys()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Extra Index 0: "); |
||||
|
sb.append(mobiHeader.getExtraIndex0()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Extra Index 1: "); |
||||
|
sb.append(mobiHeader.getExtraIndex1()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Extra Index 2: "); |
||||
|
sb.append(mobiHeader.getExtraIndex2()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Extra Index 3: "); |
||||
|
sb.append(mobiHeader.getExtraIndex3()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Extra Index 4: "); |
||||
|
sb.append(mobiHeader.getExtraIndex4()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Extra Index 5: "); |
||||
|
sb.append(mobiHeader.getExtraIndex5()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("First Non-Book Index: "); |
||||
|
sb.append(mobiHeader.getFirstNonBookIndex()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Full Name Offset: "); |
||||
|
sb.append(mobiHeader.getFullNameOffset()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Full Name Length: "); |
||||
|
sb.append(mobiHeader.getFullNameLength()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Min Version: "); |
||||
|
sb.append(mobiHeader.getMinVersion()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Huffman Record Offset: "); |
||||
|
sb.append(mobiHeader.getHuffmanRecordOffset()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Huffman Record Count: "); |
||||
|
sb.append(mobiHeader.getHuffmanRecordCount()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Huffman Table Offset: "); |
||||
|
sb.append(mobiHeader.getHuffmanTableOffset()); |
||||
|
sb.append("\r\n"); |
||||
|
sb.append("Huffman Table Length: "); |
||||
|
sb.append(mobiHeader.getHuffmanTableLength()); |
||||
|
sb.append("\r\n"); |
||||
|
|
||||
|
return sb.toString(); |
||||
|
} |
||||
|
|
||||
|
private String[] getPDBHeaderAttributes() |
||||
|
{ |
||||
|
LinkedList<String> list = new LinkedList<String>(); |
||||
|
int attr = pdbHeader.getAttributes(); |
||||
|
if ((attr & 0x02) != 0) list.add("Read-Only"); |
||||
|
if ((attr & 0x04) != 0) list.add("Dirty AppInfoArea"); |
||||
|
if ((attr & 0x08) != 0) list.add("Backup This Database"); |
||||
|
if ((attr & 0x10) != 0) list.add("OK To Install Newer Over Existing Copy"); |
||||
|
if ((attr & 0x20) != 0) list.add("Force The PalmPilot To Reset After This Database Is Installed"); |
||||
|
if ((attr & 0x40) != 0) list.add("Don't Allow Copy Of File To Be Beamed To Other Pilot"); |
||||
|
|
||||
|
String[] ret = new String[list.size()]; |
||||
|
int index = 0; |
||||
|
for (String s : list) ret[index++] = s; |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
public class MobiMetaException extends Exception |
||||
|
{ |
||||
|
/** |
||||
|
* |
||||
|
*/ |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
public MobiMetaException(String message) |
||||
|
{ |
||||
|
super(message); |
||||
|
} |
||||
|
} |
@ -0,0 +1,164 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
import java.io.*; |
||||
|
import java.util.*; |
||||
|
|
||||
|
public class PDBHeader |
||||
|
{ |
||||
|
private byte[] name = { 0, 0, 0, 0, 0, 0, 0, 0, |
||||
|
0, 0, 0, 0, 0, 0, 0, 0, |
||||
|
0, 0, 0, 0, 0, 0, 0, 0, |
||||
|
0, 0, 0, 0, 0, 0, 0, 0 }; |
||||
|
private byte[] attributes = { 0, 0 }; |
||||
|
private byte[] version = { 0, 0 }; |
||||
|
private byte[] creationDate = { 0, 0, 0, 0 }; |
||||
|
private byte[] modificationDate = { 0, 0, 0, 0 }; |
||||
|
private byte[] lastBackupDate = { 0, 0, 0, 0 }; |
||||
|
private byte[] modificationNumber = { 0, 0, 0, 0 }; |
||||
|
private byte[] appInfoID = { 0, 0, 0, 0 }; |
||||
|
private byte[] sortInfoID = { 0, 0, 0, 0 }; |
||||
|
private byte[] type = { 0, 0, 0, 0 }; |
||||
|
private byte[] creator = { 0, 0, 0, 0 }; |
||||
|
private byte[] uniqueIDSeed = { 0, 0, 0, 0 }; |
||||
|
private byte[] nextRecordListID = { 0, 0, 0, 0 }; |
||||
|
private byte[] numRecords = { 0, 0 }; |
||||
|
private List<RecordInfo> recordInfoList; |
||||
|
private byte[] gapToData = { 0, 0 }; |
||||
|
|
||||
|
public PDBHeader(InputStream in) |
||||
|
throws IOException |
||||
|
{ |
||||
|
MobiCommon.logMessage("*** PDBHeader ***"); |
||||
|
StreamUtils.readByteArray(in, name); |
||||
|
StreamUtils.readByteArray(in, attributes); |
||||
|
StreamUtils.readByteArray(in, version); |
||||
|
StreamUtils.readByteArray(in, creationDate); |
||||
|
StreamUtils.readByteArray(in, modificationDate); |
||||
|
StreamUtils.readByteArray(in, lastBackupDate); |
||||
|
StreamUtils.readByteArray(in, modificationNumber); |
||||
|
StreamUtils.readByteArray(in, appInfoID); |
||||
|
StreamUtils.readByteArray(in, sortInfoID); |
||||
|
StreamUtils.readByteArray(in, type); |
||||
|
StreamUtils.readByteArray(in, creator); |
||||
|
StreamUtils.readByteArray(in, uniqueIDSeed); |
||||
|
StreamUtils.readByteArray(in, nextRecordListID); |
||||
|
StreamUtils.readByteArray(in, numRecords); |
||||
|
|
||||
|
int recordCount = StreamUtils.byteArrayToInt(numRecords); |
||||
|
MobiCommon.logMessage("numRecords: " + recordCount); |
||||
|
recordInfoList = new LinkedList<RecordInfo>(); |
||||
|
for (int i=0; i<recordCount; i++) |
||||
|
{ |
||||
|
recordInfoList.add(new RecordInfo(in)); |
||||
|
} |
||||
|
|
||||
|
StreamUtils.readByteArray(in, gapToData); |
||||
|
} |
||||
|
|
||||
|
public long getMobiHeaderSize() |
||||
|
{ |
||||
|
return (recordInfoList.size() > 1) ? (recordInfoList.get(1) |
||||
|
.getRecordDataOffset() - recordInfoList.get(0) |
||||
|
.getRecordDataOffset()) : 0; |
||||
|
} |
||||
|
|
||||
|
public long getOffsetAfterMobiHeader() |
||||
|
{ |
||||
|
return (recordInfoList.size() > 1) ? recordInfoList.get(1) |
||||
|
.getRecordDataOffset() : 0; |
||||
|
} |
||||
|
|
||||
|
public void adjustOffsetsAfterMobiHeader(int newMobiHeaderSize) |
||||
|
{ |
||||
|
if (recordInfoList.size() < 2) return; |
||||
|
|
||||
|
int delta = (int)(newMobiHeaderSize - getMobiHeaderSize()); |
||||
|
int len = recordInfoList.size(); |
||||
|
for (int i=1; i<len; i++) |
||||
|
{ |
||||
|
RecordInfo rec = recordInfoList.get(i); |
||||
|
long oldOffset = rec.getRecordDataOffset(); |
||||
|
rec.setRecordDataOffset(oldOffset + delta); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void write(OutputStream out) throws IOException |
||||
|
{ |
||||
|
out.write(name); |
||||
|
out.write(attributes); |
||||
|
out.write(version); |
||||
|
out.write(creationDate); |
||||
|
out.write(modificationDate); |
||||
|
out.write(lastBackupDate); |
||||
|
out.write(modificationNumber); |
||||
|
out.write(appInfoID); |
||||
|
out.write(sortInfoID); |
||||
|
out.write(type); |
||||
|
out.write(creator); |
||||
|
out.write(uniqueIDSeed); |
||||
|
out.write(nextRecordListID); |
||||
|
out.write(numRecords); |
||||
|
for (RecordInfo rec : recordInfoList) rec.write(out); |
||||
|
out.write(gapToData); |
||||
|
} |
||||
|
|
||||
|
public String getName() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToString(name); |
||||
|
} |
||||
|
|
||||
|
public int getAttributes() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToInt(attributes); |
||||
|
} |
||||
|
|
||||
|
public int getVersion() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToInt(version); |
||||
|
} |
||||
|
|
||||
|
public long getCreationDate() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(creationDate); |
||||
|
} |
||||
|
|
||||
|
public long getModificationDate() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(modificationDate); |
||||
|
} |
||||
|
|
||||
|
public long getLastBackupDate() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(lastBackupDate); |
||||
|
} |
||||
|
|
||||
|
public long getModificationNumber() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(modificationNumber); |
||||
|
} |
||||
|
|
||||
|
public long getAppInfoID() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(appInfoID); |
||||
|
} |
||||
|
|
||||
|
public long getSortInfoID() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(sortInfoID); |
||||
|
} |
||||
|
|
||||
|
public long getType() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(type); |
||||
|
} |
||||
|
|
||||
|
public long getCreator() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(creator); |
||||
|
} |
||||
|
|
||||
|
public long getUniqueIDSeed() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(uniqueIDSeed); |
||||
|
} |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
import java.io.*; |
||||
|
|
||||
|
public class RecordInfo |
||||
|
{ |
||||
|
private byte[] recordDataOffset = { 0, 0, 0, 0 }; |
||||
|
private byte recordAttributes = 0; |
||||
|
private byte[] uniqueID = { 0, 0, 0 }; |
||||
|
|
||||
|
public RecordInfo(InputStream in) |
||||
|
throws IOException |
||||
|
{ |
||||
|
StreamUtils.readByteArray(in, recordDataOffset); |
||||
|
recordAttributes = StreamUtils.readByte(in); |
||||
|
StreamUtils.readByteArray(in, uniqueID); |
||||
|
|
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage("RecordInfo uniqueID: " |
||||
|
+ StreamUtils.byteArrayToInt(uniqueID)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public long getRecordDataOffset() |
||||
|
{ |
||||
|
return StreamUtils.byteArrayToLong(recordDataOffset); |
||||
|
} |
||||
|
|
||||
|
public void setRecordDataOffset(long newOffset) |
||||
|
{ |
||||
|
StreamUtils.longToByteArray(newOffset, recordDataOffset); |
||||
|
} |
||||
|
|
||||
|
public void write(OutputStream out) throws IOException |
||||
|
{ |
||||
|
out.write(recordDataOffset); |
||||
|
out.write(recordAttributes); |
||||
|
out.write(uniqueID); |
||||
|
} |
||||
|
} |
@ -0,0 +1,183 @@ |
|||||
|
package mobimeta; |
||||
|
|
||||
|
import java.io.*; |
||||
|
|
||||
|
public class StreamUtils |
||||
|
{ |
||||
|
public static String readCString(InputStream in, int len) |
||||
|
throws IOException |
||||
|
{ |
||||
|
byte[] buffer = new byte[len]; |
||||
|
int bytesLeft = len; |
||||
|
int offset = 0; |
||||
|
|
||||
|
while (bytesLeft > 0) |
||||
|
{ |
||||
|
int bytesRead = in.read(buffer, offset, bytesLeft); |
||||
|
if (bytesRead == -1) |
||||
|
throw new IOException("Supposed to read a " |
||||
|
+ len |
||||
|
+ " byte C string, but could not"); |
||||
|
offset += bytesRead; |
||||
|
bytesLeft -= bytesRead; |
||||
|
} |
||||
|
|
||||
|
String s = byteArrayToString(buffer); |
||||
|
MobiCommon.logMessage("readCString: " + s); |
||||
|
return s; |
||||
|
} |
||||
|
|
||||
|
public static byte readByte(InputStream in) |
||||
|
throws IOException |
||||
|
{ |
||||
|
int b = in.read(); |
||||
|
if (b == -1) |
||||
|
throw new IOException("Supposed to read a byte, but could not"); |
||||
|
MobiCommon.logMessage("readByte: " + b); |
||||
|
return (byte)(b & 0xff); |
||||
|
} |
||||
|
|
||||
|
public static void readByteArray(InputStream in, byte[] buffer) |
||||
|
throws IOException |
||||
|
{ |
||||
|
int len = buffer.length; |
||||
|
int bytesLeft = len; |
||||
|
int offset = 0; |
||||
|
|
||||
|
while (bytesLeft > 0) |
||||
|
{ |
||||
|
int bytesRead = in.read(buffer, offset, bytesLeft); |
||||
|
if (bytesRead == -1) |
||||
|
throw new IOException("Supposed to read a " |
||||
|
+ len |
||||
|
+ " byte array, but could not"); |
||||
|
offset += bytesRead; |
||||
|
bytesLeft -= bytesRead; |
||||
|
} |
||||
|
|
||||
|
if (MobiCommon.debug) |
||||
|
{ |
||||
|
MobiCommon.logMessage(dumpByteArray(buffer)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static String byteArrayToString(byte[] buffer) |
||||
|
{ |
||||
|
return byteArrayToString(buffer, null); |
||||
|
} |
||||
|
|
||||
|
public static String byteArrayToString(byte[] buffer, String encoding) |
||||
|
{ |
||||
|
int len = buffer.length; |
||||
|
int zeroIndex = -1; |
||||
|
for (int i=0; i<len; i++) |
||||
|
{ |
||||
|
byte b = buffer[i]; |
||||
|
if (b == 0) |
||||
|
{ |
||||
|
zeroIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (encoding != null) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (zeroIndex == -1) |
||||
|
return new String(buffer, encoding); |
||||
|
else |
||||
|
return new String(buffer, 0, zeroIndex, encoding); |
||||
|
} |
||||
|
catch (java.io.UnsupportedEncodingException e) |
||||
|
{ |
||||
|
// let it fall through and use the default encoding
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (zeroIndex == -1) |
||||
|
return new String(buffer); |
||||
|
else |
||||
|
return new String(buffer, 0, zeroIndex); |
||||
|
} |
||||
|
|
||||
|
public static int byteArrayToInt(byte[] buffer) |
||||
|
{ |
||||
|
int total = 0; |
||||
|
int len = buffer.length; |
||||
|
for (int i=0; i<len; i++) |
||||
|
{ |
||||
|
total = (total << 8) + (buffer[i] & 0xff); |
||||
|
} |
||||
|
|
||||
|
return total; |
||||
|
} |
||||
|
|
||||
|
public static long byteArrayToLong(byte[] buffer) |
||||
|
{ |
||||
|
long total = 0; |
||||
|
int len = buffer.length; |
||||
|
for (int i=0; i<len; i++) |
||||
|
{ |
||||
|
total = (total << 8) + (buffer[i] & 0xff); |
||||
|
} |
||||
|
|
||||
|
return total; |
||||
|
} |
||||
|
|
||||
|
public static void intToByteArray(int value, byte[] dest) |
||||
|
{ |
||||
|
int lastIndex = dest.length - 1; |
||||
|
for (int i=lastIndex; i >=0; i--) |
||||
|
{ |
||||
|
dest[i] = (byte)(value & 0xff); |
||||
|
value = value >> 8; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void longToByteArray(long value, byte[] dest) |
||||
|
{ |
||||
|
int lastIndex = dest.length - 1; |
||||
|
for (int i=lastIndex; i >=0; i--) |
||||
|
{ |
||||
|
dest[i] = (byte)(value & 0xff); |
||||
|
value = value >> 8; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static byte[] stringToByteArray(String s) |
||||
|
{ |
||||
|
return stringToByteArray(s, null); |
||||
|
} |
||||
|
|
||||
|
public static byte[] stringToByteArray(String s, String encoding) |
||||
|
{ |
||||
|
if (encoding != null) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
return s.getBytes(encoding); |
||||
|
} |
||||
|
catch (UnsupportedEncodingException e) |
||||
|
{ |
||||
|
// let if fall through to use the default character encoding
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return s.getBytes(); |
||||
|
} |
||||
|
|
||||
|
public static String dumpByteArray(byte[] buffer) |
||||
|
{ |
||||
|
StringBuffer sb = new StringBuffer(); |
||||
|
sb.append("{ "); |
||||
|
int len = buffer.length; |
||||
|
for (int i=0; i<len; i++) |
||||
|
{ |
||||
|
if (i > 0) sb.append(", "); |
||||
|
sb.append(buffer[i] & 0xff); |
||||
|
} |
||||
|
sb.append(" }"); |
||||
|
return sb.toString(); |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue