Added python project; fixed spelling of folder
BIN
Java Projects/Bookshelf/bin/Bookshelf.class
Normal file
BIN
Java Projects/Bookshelf/bin/CleanPath.class
Normal file
BIN
Java Projects/Bookshelf/bin/Controller$1.class
Normal file
BIN
Java Projects/Bookshelf/bin/Controller$2.class
Normal file
BIN
Java Projects/Bookshelf/bin/Controller.class
Normal file
BIN
Java Projects/Bookshelf/bin/Opener.class
Normal file
1009
Java Projects/Bookshelf/bin/hs_err_pid14779.log
Normal file
BIN
Java Projects/Bookshelf/bin/resources/bin/32/clit
Executable file
@@ -0,0 +1,41 @@
|
||||
|
||||
This is the source code package of ConvertLIT 1.8
|
||||
|
||||
In this version, most of the outstanding bug reports should be resolved:
|
||||
- Cope with buggy generators which write out truncated files
|
||||
- Added dictionary support (disable with -d flag)
|
||||
- Handle duplicated filenames
|
||||
- Completely changed whitespace expansion
|
||||
(Thanks to all the help from people who reported this problem)
|
||||
- Fixed very rare LZX Decompression buffer overruns
|
||||
- Should work on MAC OS/X and other big endian hardware
|
||||
(If not, send a bug report)
|
||||
- Better non-english filename support
|
||||
|
||||
DRM5 support is enhanced in this version
|
||||
|
||||
DRM5 support is now implemented through a "keys.txt" file which should
|
||||
contain the private keys from your activation components. You create this
|
||||
file once with "ReaderKeyRecoveryTool.exe" from the Windows binary package
|
||||
and from then on you can use those keys under any environment.
|
||||
|
||||
Build changes:
|
||||
|
||||
This package now requires "LIBTOMMATH" from math.libtomcrypt.org
|
||||
|
||||
To build under Linux:
|
||||
cd lib
|
||||
make
|
||||
cd ../clit16
|
||||
make
|
||||
|
||||
To build under Windows, Visual C*.
|
||||
cd lib
|
||||
nmake -f win32.mak
|
||||
cd ..\clit16
|
||||
nmake -f win32.mak
|
||||
|
||||
Now that VC++ 7 is available for "free", supporting MINGW is left as an
|
||||
exercise for the interested reader. Note that getting the free VC++ to the
|
||||
point where it can compile ConvertLIT isn't trivial and no support
|
||||
will be provided.
|
@@ -0,0 +1,6 @@
|
||||
convlit (1.8-1) unstable; urgency=low
|
||||
|
||||
* Initial release, closes: #472586.
|
||||
* Added manpage clit(1).
|
||||
|
||||
-- Joe Nahmias <jello@debian.org> Thu, 29 May 2008 00:59:04 -0400
|
@@ -0,0 +1,87 @@
|
||||
This package was debianized by Joe Nahmias <jello@debian.org> on
|
||||
Sun, 23 Mar 2008 23:55:42 -0400.
|
||||
|
||||
It was downloaded from <http://www.kyzer.me.uk/pack/convlit/>
|
||||
|
||||
Upstream Authors:
|
||||
|
||||
lib/des/*:
|
||||
|
||||
Richard Outerbridge
|
||||
|
||||
lib/newlzx/lzx.h, lib/newlzx/lxzd.c, lib/newlzx/mspack.h, lib/newlzx/system.h:
|
||||
|
||||
Stuart Caie <kyzer@4u.net>
|
||||
|
||||
lib/sha/*:
|
||||
|
||||
Steve Reid <steve@edmweb.com>
|
||||
Aaron D. Gifford <agifford@infowest.com>
|
||||
|
||||
All others:
|
||||
|
||||
Dan A. Jackson <drs@convertlit.com>, <djs@jacko.demon.co.uk>
|
||||
|
||||
Copyright:
|
||||
|
||||
lib/des/*:
|
||||
|
||||
Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
|
||||
|
||||
lib/newlzx/lzx.h, lib/newlzx/lxzd.c, lib/newlzx/mspack.h, lib/newlzx/system.h:
|
||||
|
||||
Copyright (C) 2003-2004 Stuart Caie
|
||||
|
||||
lib/sha/*:
|
||||
|
||||
NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN
|
||||
|
||||
All others:
|
||||
|
||||
Copyright (C) 2004 Digital Rights Software
|
||||
Copyright (C) 2002 - 2004 Dan A. Jackson
|
||||
|
||||
License:
|
||||
|
||||
clit18/hexdump.c:
|
||||
|
||||
Public Domain
|
||||
|
||||
lib/des/*:
|
||||
|
||||
THIS SOFTWARE PLACED IN THE PUBLIC DOMAIN BY THE AUTHOUR
|
||||
920825 19:42 EDST
|
||||
|
||||
lib/newlzx/lzx.h, lib/newlzx/lxzd.c, lib/newlzx/mspack.h, lib/newlzx/system.h:
|
||||
|
||||
libmspack is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License (LGPL) version 2.1
|
||||
|
||||
On Debian systems, the complete text of the GNU Lesser General
|
||||
Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
|
||||
|
||||
lib/sha/*:
|
||||
|
||||
NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN
|
||||
|
||||
All others:
|
||||
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this package; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License can be found in `/usr/share/common-licenses/GPL'.
|
||||
|
||||
The Debian packaging is (C) 2008, Joe Nahmias <jello@debian.org> and
|
||||
is licensed under the GPL, see above.
|
@@ -0,0 +1,52 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH CLIT 1 "March 23, 2008"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.SH NAME
|
||||
clit \- program to manipulate Microsoft Reader .LIT files
|
||||
.SH SYNOPSIS
|
||||
.RB "1. " clit
|
||||
.RI [ options ] " litfile.lit" " " subdir/
|
||||
.br
|
||||
.RB "2. " clit
|
||||
.RI [ options ] " oldfile.lit" " " newfile.lit
|
||||
.br
|
||||
.RB "3. " clit
|
||||
.RI [ options ] " oldfile.lit" " " newfile.lit " " inscription
|
||||
.SH DESCRIPTION
|
||||
This manual page documents briefly the \fBclit\fP command.
|
||||
.PP
|
||||
\fBConvLIT\fP is a program to convert Microsoft Reader format eBooks
|
||||
[\fILIT\fP] into an open format. \fBclit\fP supports three modes
|
||||
of operation:
|
||||
.IP 1.
|
||||
\fBExplosion\fP: expanding a \fILIT\fP file into an OEBPS compliant package,
|
||||
that is the original \fIXML\fP / \fIHTML\fP source.
|
||||
.IP 2.
|
||||
\fBDownconverting\fP: converts to \fIDRM1\fP format, also known as
|
||||
\fBsealing\fP the \fILIT\fP file.
|
||||
.IP 3.
|
||||
\fBInscribing\fP: similar to #2, this adds a label, or \fIinscription\fP,
|
||||
to the ebook to mark that it belongs to you.
|
||||
.SH OPTIONS
|
||||
A summary of options is included below.
|
||||
For a complete description, run the program without any arguments.
|
||||
.TP
|
||||
.B \-d
|
||||
Disable creating multiple subdirectories.
|
||||
.TP
|
||||
.B \-h
|
||||
Show summary of options.
|
||||
.TP
|
||||
.B \-k " " \fI/path/to/keys.txt\fP
|
||||
The \fIDRM5\fP key file is assumed to be called \fIkeys.txt\fP and located in
|
||||
the current directory. If not, use this option to instruct \fBConvLIT\fP
|
||||
where to find the key file.
|
||||
.SH AUTHOR
|
||||
\fBConvLIT\fP was written by Dan A. Jackson <drs@convertlit.com>.
|
||||
.PP
|
||||
This manual page was written by Joe Nahmias <jello@debian.org>,
|
||||
for the Debian project (but may be used by others).
|
BIN
Java Projects/Bookshelf/bin/resources/bin/64/clit
Executable file
@@ -0,0 +1,41 @@
|
||||
|
||||
This is the source code package of ConvertLIT 1.8
|
||||
|
||||
In this version, most of the outstanding bug reports should be resolved:
|
||||
- Cope with buggy generators which write out truncated files
|
||||
- Added dictionary support (disable with -d flag)
|
||||
- Handle duplicated filenames
|
||||
- Completely changed whitespace expansion
|
||||
(Thanks to all the help from people who reported this problem)
|
||||
- Fixed very rare LZX Decompression buffer overruns
|
||||
- Should work on MAC OS/X and other big endian hardware
|
||||
(If not, send a bug report)
|
||||
- Better non-english filename support
|
||||
|
||||
DRM5 support is enhanced in this version
|
||||
|
||||
DRM5 support is now implemented through a "keys.txt" file which should
|
||||
contain the private keys from your activation components. You create this
|
||||
file once with "ReaderKeyRecoveryTool.exe" from the Windows binary package
|
||||
and from then on you can use those keys under any environment.
|
||||
|
||||
Build changes:
|
||||
|
||||
This package now requires "LIBTOMMATH" from math.libtomcrypt.org
|
||||
|
||||
To build under Linux:
|
||||
cd lib
|
||||
make
|
||||
cd ../clit16
|
||||
make
|
||||
|
||||
To build under Windows, Visual C*.
|
||||
cd lib
|
||||
nmake -f win32.mak
|
||||
cd ..\clit16
|
||||
nmake -f win32.mak
|
||||
|
||||
Now that VC++ 7 is available for "free", supporting MINGW is left as an
|
||||
exercise for the interested reader. Note that getting the free VC++ to the
|
||||
point where it can compile ConvertLIT isn't trivial and no support
|
||||
will be provided.
|
@@ -0,0 +1,6 @@
|
||||
convlit (1.8-1) unstable; urgency=low
|
||||
|
||||
* Initial release, closes: #472586.
|
||||
* Added manpage clit(1).
|
||||
|
||||
-- Joe Nahmias <jello@debian.org> Thu, 29 May 2008 00:59:04 -0400
|
@@ -0,0 +1,87 @@
|
||||
This package was debianized by Joe Nahmias <jello@debian.org> on
|
||||
Sun, 23 Mar 2008 23:55:42 -0400.
|
||||
|
||||
It was downloaded from <http://www.kyzer.me.uk/pack/convlit/>
|
||||
|
||||
Upstream Authors:
|
||||
|
||||
lib/des/*:
|
||||
|
||||
Richard Outerbridge
|
||||
|
||||
lib/newlzx/lzx.h, lib/newlzx/lxzd.c, lib/newlzx/mspack.h, lib/newlzx/system.h:
|
||||
|
||||
Stuart Caie <kyzer@4u.net>
|
||||
|
||||
lib/sha/*:
|
||||
|
||||
Steve Reid <steve@edmweb.com>
|
||||
Aaron D. Gifford <agifford@infowest.com>
|
||||
|
||||
All others:
|
||||
|
||||
Dan A. Jackson <drs@convertlit.com>, <djs@jacko.demon.co.uk>
|
||||
|
||||
Copyright:
|
||||
|
||||
lib/des/*:
|
||||
|
||||
Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
|
||||
|
||||
lib/newlzx/lzx.h, lib/newlzx/lxzd.c, lib/newlzx/mspack.h, lib/newlzx/system.h:
|
||||
|
||||
Copyright (C) 2003-2004 Stuart Caie
|
||||
|
||||
lib/sha/*:
|
||||
|
||||
NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN
|
||||
|
||||
All others:
|
||||
|
||||
Copyright (C) 2004 Digital Rights Software
|
||||
Copyright (C) 2002 - 2004 Dan A. Jackson
|
||||
|
||||
License:
|
||||
|
||||
clit18/hexdump.c:
|
||||
|
||||
Public Domain
|
||||
|
||||
lib/des/*:
|
||||
|
||||
THIS SOFTWARE PLACED IN THE PUBLIC DOMAIN BY THE AUTHOUR
|
||||
920825 19:42 EDST
|
||||
|
||||
lib/newlzx/lzx.h, lib/newlzx/lxzd.c, lib/newlzx/mspack.h, lib/newlzx/system.h:
|
||||
|
||||
libmspack is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License (LGPL) version 2.1
|
||||
|
||||
On Debian systems, the complete text of the GNU Lesser General
|
||||
Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
|
||||
|
||||
lib/sha/*:
|
||||
|
||||
NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN
|
||||
|
||||
All others:
|
||||
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this package; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License can be found in `/usr/share/common-licenses/GPL'.
|
||||
|
||||
The Debian packaging is (C) 2008, Joe Nahmias <jello@debian.org> and
|
||||
is licensed under the GPL, see above.
|
@@ -0,0 +1,52 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH CLIT 1 "March 23, 2008"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.SH NAME
|
||||
clit \- program to manipulate Microsoft Reader .LIT files
|
||||
.SH SYNOPSIS
|
||||
.RB "1. " clit
|
||||
.RI [ options ] " litfile.lit" " " subdir/
|
||||
.br
|
||||
.RB "2. " clit
|
||||
.RI [ options ] " oldfile.lit" " " newfile.lit
|
||||
.br
|
||||
.RB "3. " clit
|
||||
.RI [ options ] " oldfile.lit" " " newfile.lit " " inscription
|
||||
.SH DESCRIPTION
|
||||
This manual page documents briefly the \fBclit\fP command.
|
||||
.PP
|
||||
\fBConvLIT\fP is a program to convert Microsoft Reader format eBooks
|
||||
[\fILIT\fP] into an open format. \fBclit\fP supports three modes
|
||||
of operation:
|
||||
.IP 1.
|
||||
\fBExplosion\fP: expanding a \fILIT\fP file into an OEBPS compliant package,
|
||||
that is the original \fIXML\fP / \fIHTML\fP source.
|
||||
.IP 2.
|
||||
\fBDownconverting\fP: converts to \fIDRM1\fP format, also known as
|
||||
\fBsealing\fP the \fILIT\fP file.
|
||||
.IP 3.
|
||||
\fBInscribing\fP: similar to #2, this adds a label, or \fIinscription\fP,
|
||||
to the ebook to mark that it belongs to you.
|
||||
.SH OPTIONS
|
||||
A summary of options is included below.
|
||||
For a complete description, run the program without any arguments.
|
||||
.TP
|
||||
.B \-d
|
||||
Disable creating multiple subdirectories.
|
||||
.TP
|
||||
.B \-h
|
||||
Show summary of options.
|
||||
.TP
|
||||
.B \-k " " \fI/path/to/keys.txt\fP
|
||||
The \fIDRM5\fP key file is assumed to be called \fIkeys.txt\fP and located in
|
||||
the current directory. If not, use this option to instruct \fBConvLIT\fP
|
||||
where to find the key file.
|
||||
.SH AUTHOR
|
||||
\fBConvLIT\fP was written by Dan A. Jackson <drs@convertlit.com>.
|
||||
.PP
|
||||
This manual page was written by Joe Nahmias <jello@debian.org>,
|
||||
for the Debian project (but may be used by others).
|
BIN
Java Projects/Bookshelf/bin/resources/bin/SumatraPDF.exe
Normal file
115
Java Projects/Bookshelf/bin/resources/bin/epub-thumbnailer
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Author: Mariano Simone (http://marianosimone.com)
|
||||
# Version: 1.0
|
||||
# Name: epub-thumbnailer
|
||||
# Description: An implementation of a cover thumbnailer for epub files
|
||||
# Installation: see README
|
||||
|
||||
import os
|
||||
import re
|
||||
from io import BytesIO
|
||||
import sys
|
||||
from xml.dom import minidom
|
||||
from StringIO import StringIO
|
||||
import urllib
|
||||
import zipfile
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
import Image
|
||||
|
||||
img_ext_regex = re.compile(r'^.*\.(jpg|jpeg|png)$', flags=re.IGNORECASE)
|
||||
cover_regex = re.compile(r'.*cover.*\.(jpg|jpeg|png)', flags=re.IGNORECASE)
|
||||
|
||||
def get_cover_from_manifest(epub):
|
||||
|
||||
# open the main container
|
||||
container = epub.open("META-INF/container.xml")
|
||||
container_root = minidom.parseString(container.read())
|
||||
|
||||
# locate the rootfile
|
||||
elem = container_root.getElementsByTagName("rootfile")[0]
|
||||
rootfile_path = elem.getAttribute("full-path")
|
||||
|
||||
# open the rootfile
|
||||
rootfile = epub.open(rootfile_path)
|
||||
rootfile_root = minidom.parseString(rootfile.read())
|
||||
|
||||
# find possible cover in meta
|
||||
cover_id = None
|
||||
for meta in rootfile_root.getElementsByTagName("meta"):
|
||||
if meta.getAttribute("name") == "cover":
|
||||
cover_id = meta.getAttribute("content")
|
||||
break
|
||||
|
||||
# find the manifest element
|
||||
manifest = rootfile_root.getElementsByTagName("manifest")[0]
|
||||
for item in manifest.getElementsByTagName("item"):
|
||||
item_id = item.getAttribute("id")
|
||||
item_href = item.getAttribute("href")
|
||||
if (item_id == cover_id) or ("cover" in item_id and img_ext_regex.match(item_href.lower())):
|
||||
return os.path.join(os.path.dirname(rootfile_path), item_href)
|
||||
|
||||
return None
|
||||
|
||||
def get_cover_by_filename(epub):
|
||||
no_matching_images = []
|
||||
for fileinfo in epub.filelist:
|
||||
if cover_regex.match(fileinfo.filename):
|
||||
return fileinfo.filename
|
||||
if img_ext_regex.match(fileinfo.filename):
|
||||
no_matching_images.append(fileinfo)
|
||||
return _choose_best_image(no_matching_images)
|
||||
|
||||
def _choose_best_image(images):
|
||||
if images:
|
||||
return max(images, key=lambda f: f.file_size)
|
||||
return None
|
||||
|
||||
def extract_cover(cover_path):
|
||||
if cover_path:
|
||||
cover = epub.open(cover_path)
|
||||
im = Image.open(BytesIO(cover.read()))
|
||||
im.thumbnail((size, size), Image.ANTIALIAS)
|
||||
if im.mode == "CMYK":
|
||||
im = im.convert("RGB")
|
||||
im.save(output_file, "PNG")
|
||||
return True
|
||||
return False
|
||||
|
||||
# Which file are we working with?
|
||||
input_file = sys.argv[1]
|
||||
# Where do does the file have to be saved?
|
||||
output_file = sys.argv[2]
|
||||
# Required size?
|
||||
size = int(sys.argv[3])
|
||||
|
||||
# An epub is just a zip
|
||||
file_url = urllib.urlopen(input_file)
|
||||
epub = zipfile.ZipFile(StringIO(file_url.read()), "r")
|
||||
|
||||
extraction_strategies = [get_cover_from_manifest, get_cover_by_filename]
|
||||
|
||||
for strategy in extraction_strategies:
|
||||
try:
|
||||
cover_path = strategy(epub)
|
||||
if extract_cover(cover_path):
|
||||
exit(0)
|
||||
except Exception as ex:
|
||||
print("Error getting cover using %s: " % strategy.__name__, ex)
|
||||
|
||||
exit(1)
|
BIN
Java Projects/Bookshelf/bin/resources/bin/pdf-thumbnailer
Executable file
BIN
Java Projects/Bookshelf/bin/resources/jars/EpubViewer.jar
Executable file
BIN
Java Projects/Bookshelf/bin/resources/jars/PdfViewer.jar
Executable file
1
Java Projects/Bookshelf/bin/resources/jars/tmp/.version
Normal file
@@ -0,0 +1 @@
|
||||
0.97-rc11-20100715-0959
|
@@ -0,0 +1,5 @@
|
||||
Manifest-Version: 1.0
|
||||
ImplementationVersion: 3.1
|
||||
Main-Class: com.simontuffs.onejar.Boot
|
||||
One-Jar-Main-Class: nl.siegmann.epublib.viewer.Viewer
|
||||
|
BIN
Java Projects/Bookshelf/bin/resources/jars/tmp/OneJar.class
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* One-JAR(TM) (http://www.simontuffs.com/one-jar). Copyright (c) 2004-2010,
|
||||
* P. Simon Tuffs (simon@simontuffs.com). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of P. Simon Tuffs, nor the names of any contributors,
|
||||
* nor the name One-JAR may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Including this file inside the built One-JAR file conforms with these terms.
|
||||
*/
|
||||
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2004-2010, P. Simon Tuffs (simon@simontuffs.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the full license at http://one-jar.sourceforge.net/one-jar-license.html
|
||||
* This license is also included in the distributions of this software
|
||||
* under doc/one-jar-license.txt
|
||||
*/
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.simontuffs.onejar.Boot;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* One-JAR Jar files are intended to be executed using the following kind of command:
|
||||
* <pre>
|
||||
* java -jar <one-jar.jar> [args]
|
||||
* </pre>
|
||||
* This class allows a One-JAR jar-file to be executed using the alternative command:
|
||||
* <pre>
|
||||
* java -cp <one-jar.jar> OneJar [args]
|
||||
* </pre>
|
||||
* Its main role is in testing the behaviour of OneJar on platforms which mangle the classpath
|
||||
* when running with the first kind of command, but it can also be a useful alternative
|
||||
* execution mechanism.
|
||||
* <p>Note: the same effect can be obtained by using the Boot class, albeit with more
|
||||
* typing:
|
||||
* <pre>
|
||||
* java -cp <one-jar.jar> com.simontuffs.onejar.Boot [args]
|
||||
* @author simon
|
||||
*
|
||||
*/
|
||||
public class OneJar {
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("OneJar" + Arrays.asList(args).toString().replace('[','(').replace(']',')'));
|
||||
new OneJar().run(args);
|
||||
|
||||
}
|
||||
|
||||
public void run(String[] args) throws Exception {
|
||||
Boot.run(args);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
* Copyright (c) 2004-2010, P. Simon Tuffs (simon@simontuffs.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the full license at http://one-jar.sourceforge.net/one-jar-license.html
|
||||
* This license is also included in the distributions of this software
|
||||
* under doc/one-jar-license.txt
|
||||
*/
|
||||
|
||||
package com.simontuffs.onejar;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Run a java application which requires multiple support jars from inside
|
||||
* a single jar file.
|
||||
*
|
||||
* <p>
|
||||
* Developer time JVM properties:
|
||||
* <pre>
|
||||
* -Done-jar.main.class={name} Use named class as main class to run.
|
||||
* -Done-jar.record[=recording] Record loaded classes into "recording" directory.
|
||||
* Flatten jar.names into directory tree suitable
|
||||
* for use as a classpath.
|
||||
* -Done-jar.jar.names Record loaded classes, preserve jar structure
|
||||
* -Done-jar.verbose Run the JarClassLoader in verbose mode.
|
||||
* </pre>
|
||||
* @author simon@simontuffs.com (<a href="http://www.simontuffs.com">http://www.simontuffs.com</a>)
|
||||
*/
|
||||
public class Boot {
|
||||
|
||||
/**
|
||||
* The name of the manifest attribute which controls which class
|
||||
* to bootstrap from the jar file. The boot class can
|
||||
* be in any of the contained jar files.
|
||||
*/
|
||||
public final static String BOOT_CLASS = "Boot-Class";
|
||||
public final static String ONE_JAR_CLASSLOADER = "One-Jar-Class-Loader";
|
||||
public final static String ONE_JAR_MAIN_CLASS = "One-Jar-Main-Class";
|
||||
public final static String ONE_JAR_DEFAULT_MAIN_JAR = "One-Jar-Default-Main-Jar";
|
||||
public final static String ONE_JAR_MAIN_ARGS = "One-Jar-Main-Args";
|
||||
public final static String ONE_JAR_URL_FACTORY = "One-Jar-URL-Factory";
|
||||
|
||||
public final static String MANIFEST = "META-INF/MANIFEST.MF";
|
||||
public final static String MAIN_JAR = "main/main.jar";
|
||||
|
||||
public final static String WRAP_CLASS_LOADER = "Wrap-Class-Loader";
|
||||
public final static String WRAP_DIR = "wrap";
|
||||
public final static String WRAP_JAR = "/" + WRAP_DIR + "/wraploader.jar";
|
||||
|
||||
// System properties.
|
||||
public final static String PROPERTY_PREFIX = "one-jar.";
|
||||
public final static String P_MAIN_CLASS = PROPERTY_PREFIX + "main.class";
|
||||
public final static String P_MAIN_JAR = PROPERTY_PREFIX + "main.jar";
|
||||
public final static String P_MAIN_APP = PROPERTY_PREFIX + "main.app";
|
||||
public final static String P_RECORD = PROPERTY_PREFIX + "record";
|
||||
public final static String P_JARNAMES = PROPERTY_PREFIX + "jar.names";
|
||||
public final static String P_VERBOSE = PROPERTY_PREFIX + "verbose";
|
||||
public final static String P_INFO = PROPERTY_PREFIX + "info";
|
||||
public final static String P_STATISTICS = PROPERTY_PREFIX + "statistics";
|
||||
public final static String P_SHOW_PROPERTIES = PROPERTY_PREFIX + "show.properties";
|
||||
public final static String P_JARPATH = PROPERTY_PREFIX + "jar.path";
|
||||
public final static String P_ONE_JAR_CLASS_PATH = PROPERTY_PREFIX + "class.path";
|
||||
public final static String P_JAVA_CLASS_PATH = "java.class.path";
|
||||
public final static String P_PATH_SEPARATOR = "|";
|
||||
public final static String P_EXPAND_DIR = PROPERTY_PREFIX + "expand.dir";
|
||||
|
||||
// Command-line arguments
|
||||
public final static String A_HELP = "--one-jar-help";
|
||||
public final static String A_VERSION = "--one-jar-version";
|
||||
|
||||
public final static String[] HELP_PROPERTIES = {
|
||||
P_MAIN_CLASS, "Specifies the name of the class which should be executed \n(via public static void main(String[])",
|
||||
P_MAIN_APP, "Specifies the name of the main/<app>.jar to be executed",
|
||||
P_RECORD, "true: Enables recording of the classes loaded by the application",
|
||||
P_JARNAMES, "true: Recorded classes are kept in directories corresponding to their jar names.\n" +
|
||||
"false: Recorded classes are flattened into a single directory. \nDuplicates are ignored (first wins)",
|
||||
P_VERBOSE, "true: Print verbose classloading information",
|
||||
P_INFO, "true: Print informative classloading information",
|
||||
P_STATISTICS, "true: Shows statistics about the One-Jar Classloader",
|
||||
P_JARPATH, "Full path of the one-jar file being executed. \nOnly needed if java.class.path does not contain the path to the jar, e.g. on Max OS/X.",
|
||||
P_ONE_JAR_CLASS_PATH, "Extra classpaths to be added to the execution environment. \nUse platform independent path separator '" + P_PATH_SEPARATOR + "'",
|
||||
P_EXPAND_DIR, "Directory to use for expanded files.",
|
||||
P_SHOW_PROPERTIES, "true: Shows the JVM system properties.",
|
||||
};
|
||||
|
||||
public final static String[] HELP_ARGUMENTS = {
|
||||
A_HELP, "Shows this message, then exits.",
|
||||
A_VERSION, "Shows the version of One-JAR, then exits.",
|
||||
};
|
||||
|
||||
protected static String mainJar;
|
||||
|
||||
protected static boolean info, verbose, statistics;
|
||||
protected static String myJarPath;
|
||||
|
||||
protected static long startTime = System.currentTimeMillis();
|
||||
protected static long endTime = 0;
|
||||
|
||||
|
||||
// Singleton loader. This must not be changed once it is set, otherwise all
|
||||
// sorts of nasty class-cast exceptions will ensue. Hence we control
|
||||
// access to it strongly.
|
||||
private static JarClassLoader loader = null;
|
||||
|
||||
|
||||
/**
|
||||
* This method provides access to the bootstrap One-JAR classloader which
|
||||
* is needed in the URL connection Handler when opening streams relative
|
||||
* to classes.
|
||||
* @return
|
||||
*/
|
||||
public synchronized static JarClassLoader getClassLoader() {
|
||||
return loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the single point of entry for setting the "loader" member. It checks to
|
||||
* make sure programming errors don't call it more than once.
|
||||
* @param $loader
|
||||
*/
|
||||
public synchronized static void setClassLoader(JarClassLoader $loader) {
|
||||
if (loader != null) throw new RuntimeException("Attempt to set a second Boot loader");
|
||||
loader = $loader;
|
||||
}
|
||||
|
||||
protected static void VERBOSE(String message) {
|
||||
if (verbose) System.out.println("Boot: " + message);
|
||||
}
|
||||
|
||||
protected static void WARNING(String message) {
|
||||
System.err.println("Boot: Warning: " + message);
|
||||
}
|
||||
|
||||
protected static void INFO(String message) {
|
||||
if (info) System.out.println("Boot: Info: " + message);
|
||||
}
|
||||
|
||||
protected static void PRINTLN(String message) {
|
||||
System.out.println("Boot: " + message);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
run(args);
|
||||
}
|
||||
|
||||
public static void run(String args[]) throws Exception {
|
||||
|
||||
args = processArgs(args);
|
||||
|
||||
// Is the main class specified on the command line? If so, boot it.
|
||||
// Otherwise, read the main class out of the manifest.
|
||||
String mainClass = null;
|
||||
|
||||
{
|
||||
// Default properties are in resource 'one-jar.properties'.
|
||||
Properties properties = new Properties();
|
||||
String props = "one-jar.properties";
|
||||
InputStream is = Boot.class.getResourceAsStream("/" + props);
|
||||
if (is != null) {
|
||||
INFO("loading properties from " + props);
|
||||
properties.load(is);
|
||||
}
|
||||
|
||||
// Merge in anything in a local file with the same name.
|
||||
if (new File(props).exists()) {
|
||||
is = new FileInputStream(props);
|
||||
if (is != null) {
|
||||
INFO("merging properties from " + props);
|
||||
properties.load(is);
|
||||
}
|
||||
}
|
||||
|
||||
// Set system properties only if not already specified.
|
||||
Enumeration _enum = properties.propertyNames();
|
||||
while (_enum.hasMoreElements()) {
|
||||
String name = (String)_enum.nextElement();
|
||||
if (System.getProperty(name) == null) {
|
||||
System.setProperty(name, properties.getProperty(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Boolean.valueOf(System.getProperty(P_SHOW_PROPERTIES, "false")).booleanValue()) {
|
||||
// What are the system properties.
|
||||
Properties props = System.getProperties();
|
||||
String keys[] = (String[])props.keySet().toArray(new String[]{});
|
||||
Arrays.sort(keys);
|
||||
|
||||
for (int i=0; i<keys.length; i++) {
|
||||
String key = keys[i];
|
||||
System.out.println(key + "=" + props.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
// Process developer properties:
|
||||
if (mainClass == null) {
|
||||
mainClass = System.getProperty(P_MAIN_CLASS);
|
||||
}
|
||||
|
||||
if (mainJar == null) {
|
||||
String app = System.getProperty(P_MAIN_APP);
|
||||
if (app != null) {
|
||||
mainJar = "main/" + app + ".jar";
|
||||
} else {
|
||||
mainJar = System.getProperty(P_MAIN_JAR, MAIN_JAR);
|
||||
}
|
||||
}
|
||||
|
||||
// Pick some things out of the top-level JAR file.
|
||||
String jar = getMyJarPath();
|
||||
JarFile jarFile = new JarFile(jar);
|
||||
Manifest manifest = jarFile.getManifest();
|
||||
Attributes attributes = manifest.getMainAttributes();
|
||||
String bootLoaderName = attributes.getValue(ONE_JAR_CLASSLOADER);
|
||||
|
||||
if (mainJar == null) {
|
||||
mainJar = attributes.getValue(ONE_JAR_DEFAULT_MAIN_JAR);
|
||||
}
|
||||
|
||||
String mainargs = attributes.getValue(ONE_JAR_MAIN_ARGS);
|
||||
if (mainargs != null && args.length == 0) {
|
||||
// Replace the args with built-in. Support escaped whitespace.
|
||||
args = mainargs.split("[^\\\\]\\s");
|
||||
for (int i=0; i<args.length; i++) {
|
||||
args[i] = args[i].replaceAll("\\\\(\\s)", "$1");
|
||||
}
|
||||
}
|
||||
|
||||
// If no main-class specified, check the manifest of the main jar for
|
||||
// a Boot-Class attribute.
|
||||
if (mainClass == null) {
|
||||
mainClass = attributes.getValue(ONE_JAR_MAIN_CLASS);
|
||||
if (mainClass == null) {
|
||||
mainClass = attributes.getValue(BOOT_CLASS);
|
||||
if (mainClass != null) {
|
||||
WARNING("The manifest attribute " + BOOT_CLASS + " is deprecated in favor of the attribute " + ONE_JAR_MAIN_CLASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mainClass == null) {
|
||||
// Still don't have one (default). One final try: look for a jar file in a
|
||||
// main directory. There should be only one, and it's manifest
|
||||
// Main-Class attribute is the main class. The JarClassLoader will take
|
||||
// care of finding it.
|
||||
InputStream is = Boot.class.getResourceAsStream("/" + mainJar);
|
||||
if (is != null) {
|
||||
JarInputStream jis = new JarInputStream(is);
|
||||
Manifest mainmanifest = jis.getManifest();
|
||||
jis.close();
|
||||
mainClass = mainmanifest.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
|
||||
} else {
|
||||
// There is no main jar. Info unless mainJar is empty string.
|
||||
// The load(mainClass) will scan for main jars anyway.
|
||||
if (!"".equals(mainJar)){
|
||||
INFO("Unable to locate main jar '" + mainJar + "' in the JAR file " + getMyJarPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do we need to create a wrapping classloader? Check for the
|
||||
// presence of a "wrap" directory at the top of the jar file.
|
||||
URL url = Boot.class.getResource(WRAP_JAR);
|
||||
|
||||
if (url != null) {
|
||||
// Wrap class loaders.
|
||||
final JarClassLoader bootLoader = getBootLoader(bootLoaderName);
|
||||
bootLoader.load(null);
|
||||
|
||||
// Read the "Wrap-Class-Loader" property from the wraploader jar file.
|
||||
// This is the class to use as a wrapping class-loader.
|
||||
InputStream is = Boot.class.getResourceAsStream(WRAP_JAR);
|
||||
if (is != null) {
|
||||
JarInputStream jis = new JarInputStream(is);
|
||||
final String wrapLoader = jis.getManifest().getMainAttributes().getValue(WRAP_CLASS_LOADER);
|
||||
jis.close();
|
||||
if (wrapLoader == null) {
|
||||
WARNING(url + " did not contain a " + WRAP_CLASS_LOADER + " attribute, unable to load wrapping classloader");
|
||||
} else {
|
||||
INFO("using " + wrapLoader);
|
||||
JarClassLoader wrapped = getWrapLoader(bootLoader, wrapLoader);
|
||||
if (wrapped == null) {
|
||||
WARNING("Unable to instantiate " + wrapLoader + " from " + WRAP_DIR + ": using default JarClassLoader");
|
||||
wrapped = getBootLoader(null);
|
||||
}
|
||||
setClassLoader(wrapped);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setClassLoader(getBootLoader(bootLoaderName, Boot.class.getClassLoader()));
|
||||
INFO("using JarClassLoader: " + getClassLoader().getClass().getName());
|
||||
}
|
||||
|
||||
// Allow injection of the URL factory.
|
||||
String urlfactory = attributes.getValue(ONE_JAR_URL_FACTORY);
|
||||
if (urlfactory != null) {
|
||||
loader.setURLFactory(urlfactory);
|
||||
}
|
||||
|
||||
mainClass = loader.load(mainClass);
|
||||
|
||||
if (mainClass == null && !loader.isExpanded())
|
||||
throw new Exception(getMyJarName() + " main class was not found (fix: add main/main.jar with a Main-Class manifest attribute, or specify -D" + P_MAIN_CLASS + "=<your.class.name>), or use " + ONE_JAR_MAIN_CLASS + " in the manifest");
|
||||
|
||||
if (mainClass != null) {
|
||||
// Guard against the main.jar pointing back to this
|
||||
// class, and causing an infinite recursion.
|
||||
String bootClass = Boot.class.getName();
|
||||
if (bootClass.equals(mainClass))
|
||||
throw new Exception(getMyJarName() + " main class (" + mainClass + ") would cause infinite recursion: check main.jar/META-INF/MANIFEST.MF/Main-Class attribute: " + mainClass);
|
||||
|
||||
Class cls = loader.loadClass(mainClass);
|
||||
|
||||
endTime = System.currentTimeMillis();
|
||||
showTime();
|
||||
|
||||
Method main = cls.getMethod("main", new Class[]{String[].class});
|
||||
main.invoke(null, new Object[]{args});
|
||||
}
|
||||
}
|
||||
|
||||
public static void showTime() {
|
||||
long endtime = System.currentTimeMillis();
|
||||
if (statistics) {
|
||||
PRINTLN("Elapsed time: " + (endtime - startTime) + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setProperties(IProperties jarloader) {
|
||||
INFO("setProperties(" + jarloader + ")");
|
||||
if (getProperty(P_RECORD)) {
|
||||
jarloader.setRecord(true);
|
||||
jarloader.setRecording(System.getProperty(P_RECORD));
|
||||
}
|
||||
if (getProperty(P_JARNAMES)) {
|
||||
jarloader.setRecord(true);
|
||||
jarloader.setFlatten(false);
|
||||
}
|
||||
if (getProperty(P_VERBOSE)) {
|
||||
jarloader.setVerbose(true);
|
||||
jarloader.setInfo(true);
|
||||
verbose = true;
|
||||
}
|
||||
if (getProperty(P_INFO)) {
|
||||
jarloader.setInfo(true);
|
||||
info = true;
|
||||
}
|
||||
|
||||
statistics = getProperty(P_STATISTICS);
|
||||
}
|
||||
|
||||
public static boolean getProperty(String key) {
|
||||
return Boolean.valueOf(System.getProperty(key, "false")).booleanValue();
|
||||
}
|
||||
|
||||
public static String getMyJarName() {
|
||||
String name = getMyJarPath();
|
||||
int last = name.lastIndexOf("/");
|
||||
if (last >= 0) {
|
||||
name = name.substring(last+1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static String getMyJarPath() {
|
||||
if (myJarPath != null) {
|
||||
return myJarPath;
|
||||
}
|
||||
myJarPath = System.getProperty(P_JARPATH);
|
||||
if (myJarPath == null) {
|
||||
try {
|
||||
// Hack to obtain the name of this jar file.
|
||||
String jarname = System.getProperty(P_JAVA_CLASS_PATH);
|
||||
// Open each Jar file looking for this class name. This allows for
|
||||
// JVM's that place more than the jar file on the classpath.
|
||||
String jars[] =jarname.split(System.getProperty("path.separator"));
|
||||
for (int i=0; i<jars.length; i++) {
|
||||
jarname = jars[i];
|
||||
VERBOSE("Checking " + jarname + " as One-Jar file");
|
||||
// Allow for URL based paths, as well as file-based paths. File
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new URL(jarname).openStream();
|
||||
} catch (MalformedURLException mux) {
|
||||
// Try a local file.
|
||||
try {
|
||||
is = new FileInputStream(jarname);
|
||||
} catch (IOException iox) {
|
||||
// Ignore..., but it isn't good to have bad entries on the classpath.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ZipEntry entry = findJarEntry(new JarInputStream(is), Boot.class.getName().replace('.', '/') + ".class");
|
||||
if (entry != null) {
|
||||
myJarPath = jarname;
|
||||
break;
|
||||
} else {
|
||||
// One more try as a Zip file: supports launch4j on Windows.
|
||||
entry = findZipEntry(new ZipFile(jarname), Boot.class.getName().replace('.', '/') + ".class");
|
||||
if (entry != null) {
|
||||
myJarPath = jarname;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace();
|
||||
WARNING("jar=" + myJarPath + " loaded from " + P_JAVA_CLASS_PATH + " (" + System.getProperty(P_JAVA_CLASS_PATH) + ")");
|
||||
}
|
||||
}
|
||||
if (myJarPath == null) {
|
||||
throw new IllegalArgumentException("Unable to locate " + Boot.class.getName() + " in the java.class.path: consider using -D" + P_JARPATH + " to specify the one-jar filename.");
|
||||
}
|
||||
// Normalize those annoying DOS backslashes.
|
||||
myJarPath = myJarPath.replace('\\', '/');
|
||||
return myJarPath;
|
||||
}
|
||||
|
||||
public static JarEntry findJarEntry(JarInputStream jis, String name) throws IOException {
|
||||
JarEntry entry;
|
||||
while ((entry = jis.getNextJarEntry()) != null) {
|
||||
if (entry.getName().equals(name)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ZipEntry findZipEntry(ZipFile zip, String name) throws IOException {
|
||||
Enumeration entries = zip.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = (ZipEntry) entries.nextElement();
|
||||
VERBOSE(("findZipEntry(): entry=" + entry.getName()));
|
||||
if (entry.getName().equals(name))
|
||||
return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int firstWidth(String[] table) {
|
||||
int width = 0;
|
||||
for (int i=0; i<table.length; i+=2) {
|
||||
if (table[i].length() > width) width = table[i].length();
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
public static String pad(String indent, String string, int width) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append(indent);
|
||||
buf.append(string);
|
||||
for (int i=0; i<width-string.length(); i++) {
|
||||
buf.append(" ");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String wrap(String indent, String string, int width) {
|
||||
String padding = pad(indent, "", width);
|
||||
string = string.replaceAll("\n", "\n" + padding);
|
||||
return string;
|
||||
}
|
||||
|
||||
public static String[] processArgs(String args[]) throws Exception {
|
||||
// Check for arguments which matter to us, and strip them.
|
||||
VERBOSE("processArgs(" + Arrays.asList(args) + ")");
|
||||
ArrayList list = new ArrayList();
|
||||
for (int a=0; a<args.length; a++) {
|
||||
String argument = args[a];
|
||||
if (argument.startsWith(A_HELP)) {
|
||||
int width = firstWidth(HELP_ARGUMENTS);
|
||||
// Width of first column
|
||||
|
||||
System.out.println("One-Jar uses the following command-line arguments");
|
||||
for (int i=0; i<HELP_ARGUMENTS.length; i++) {
|
||||
System.out.print(pad(" ", HELP_ARGUMENTS[i++], width+1));
|
||||
System.out.println(wrap(" ", HELP_ARGUMENTS[i], width+1));
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
width = firstWidth(HELP_PROPERTIES);
|
||||
System.out.println("One-Jar uses the following VM properties (-D<property>=<true|false|string>)");
|
||||
for (int i=0; i<HELP_PROPERTIES.length; i++) {
|
||||
System.out.print(pad(" ", HELP_PROPERTIES[i++], width+1));
|
||||
System.out.println(wrap(" ", HELP_PROPERTIES[i], width+1));
|
||||
}
|
||||
System.out.println();
|
||||
System.exit(0);
|
||||
} else if (argument.startsWith(A_VERSION)) {
|
||||
InputStream is = Boot.class.getResourceAsStream("/.version");
|
||||
if (is != null) {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
String version = br.readLine();
|
||||
br.close();
|
||||
System.out.println("One-JAR version " + version);
|
||||
} else {
|
||||
System.out.println("Unable to determine One-JAR version (missing /.version resource in One-JAR archive)");
|
||||
}
|
||||
System.exit(0);
|
||||
} else {
|
||||
list.add(argument);
|
||||
}
|
||||
}
|
||||
return (String[])list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
protected static JarClassLoader getBootLoader(final String loader) {
|
||||
JarClassLoader bootLoader = (JarClassLoader)AccessController.doPrivileged(
|
||||
new PrivilegedAction() {
|
||||
public Object run() {
|
||||
if (loader != null) {
|
||||
try {
|
||||
Class cls = Class.forName(loader);
|
||||
Constructor ctor = cls.getConstructor(new Class[]{String.class});
|
||||
return ctor.newInstance(new Object[]{WRAP_DIR});
|
||||
} catch (Exception x) {
|
||||
WARNING("Unable to instantiate " + loader + ": " + x + " continuing using default " + JarClassLoader.class.getName());
|
||||
}
|
||||
}
|
||||
return new JarClassLoader(WRAP_DIR);
|
||||
}
|
||||
}
|
||||
);
|
||||
return bootLoader;
|
||||
}
|
||||
|
||||
protected static JarClassLoader getBootLoader(final String loader, ClassLoader parent) {
|
||||
return (JarClassLoader)AccessController.doPrivileged(
|
||||
new PrivilegedAction() {
|
||||
public Object run() {
|
||||
if (loader != null) {
|
||||
try {
|
||||
Class cls = Class.forName(loader);
|
||||
Constructor ctor = cls.getConstructor(new Class[]{ClassLoader.class});
|
||||
return ctor.newInstance(new Object[]{Boot.class.getClassLoader()});
|
||||
} catch (Exception x) {
|
||||
WARNING("Unable to instantiate " + loader + ": " + x + " continuing using default " + JarClassLoader.class.getName());
|
||||
}
|
||||
}
|
||||
return new JarClassLoader(Boot.class.getClassLoader());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected static JarClassLoader getWrapLoader(final ClassLoader bootLoader, final String wrapLoader) {
|
||||
return ((JarClassLoader)AccessController.doPrivileged(
|
||||
new PrivilegedAction() {
|
||||
public Object run() {
|
||||
try {
|
||||
Class jarLoaderClass = bootLoader.loadClass(wrapLoader);
|
||||
Constructor ctor = jarLoaderClass.getConstructor(new Class[]{ClassLoader.class});
|
||||
return ctor.newInstance(new Object[]{bootLoader});
|
||||
} catch (Throwable t) {
|
||||
WARNING(t.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static long getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public static long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2004-2010, P. Simon Tuffs (simon@simontuffs.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the full license at http://one-jar.sourceforge.net/one-jar-license.html
|
||||
* This license is also included in the distributions of this software
|
||||
* under doc/one-jar-license.txt
|
||||
*/
|
||||
|
||||
package com.simontuffs.onejar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.FileNameMap;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
|
||||
/**
|
||||
* @author simon@simontuffs.com
|
||||
*
|
||||
*/
|
||||
public class Handler extends URLStreamHandler {
|
||||
|
||||
/**
|
||||
* This protocol name must match the name of the package in which this class
|
||||
* lives.
|
||||
*/
|
||||
public static String PROTOCOL = "onejar";
|
||||
|
||||
/**
|
||||
* @see java.net.URLStreamHandler#openConnection(java.net.URL)
|
||||
*/
|
||||
protected URLConnection openConnection(final URL u) throws IOException {
|
||||
final String resource = u.getPath();
|
||||
return new URLConnection(u) {
|
||||
public void connect() {
|
||||
}
|
||||
public String getContentType() {
|
||||
FileNameMap fileNameMap = java.net.URLConnection.getFileNameMap();
|
||||
String contentType = fileNameMap.getContentTypeFor(resource);
|
||||
if (contentType == null)
|
||||
contentType = "text/plain";
|
||||
return contentType;
|
||||
}
|
||||
public InputStream getInputStream() throws IOException {
|
||||
// Use the Boot classloader to get the resource. There
|
||||
// is only one per one-jar.
|
||||
JarClassLoader cl = Boot.getClassLoader();
|
||||
InputStream is = cl.getByteStream(resource);
|
||||
// sun.awt image loading does not like null input streams returned here.
|
||||
// Throw IOException (probably better anyway).
|
||||
if (is == null)
|
||||
throw new IOException("cl.getByteStream() returned null for " + resource);
|
||||
return is;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.simontuffs.onejar;
|
||||
|
||||
/**
|
||||
* Interface to the controlling properties for a JarClassLoader.
|
||||
* @author simon
|
||||
*
|
||||
*/
|
||||
public interface IProperties {
|
||||
|
||||
public void setVerbose(boolean verbose);
|
||||
public void setInfo(boolean info);
|
||||
public void setRecord(boolean record);
|
||||
public void setFlatten(boolean flatten);
|
||||
public void setRecording(String recording);
|
||||
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2004-2010, P. Simon Tuffs (simon@simontuffs.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the full license at http://one-jar.sourceforge.net/one-jar-license.html
|
||||
* This license is also included in the distributions of this software
|
||||
* under doc/one-jar-license.txt
|
||||
*
|
||||
* Many thanks to the following for their contributions to One-Jar:
|
||||
* Contributor: sebastian : http://code.google.com/u/@WBZRRlBYBxZHXQl9/
|
||||
* Original creator of the OneJarFile/OneJarUrlConnecion solution to resource location
|
||||
* using jar protocols.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.simontuffs.onejar;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class OneJarFile extends JarFile {
|
||||
|
||||
protected final String jarFilename;
|
||||
protected final String filename;
|
||||
protected final JarEntry wrappedJarFile;
|
||||
|
||||
public OneJarFile(String myJarPath, String jarFilename, String filename) throws IOException {
|
||||
super(myJarPath);
|
||||
this.jarFilename = jarFilename;
|
||||
this.filename = filename;
|
||||
wrappedJarFile = super.getJarEntry(this.jarFilename);
|
||||
}
|
||||
|
||||
public JarEntry getJarEntry(String name) {
|
||||
String filename = name.substring(name.indexOf("!/") + 2);
|
||||
if (filename.equals(MANIFEST_NAME)) {
|
||||
// Synthesize a JarEntry.
|
||||
return new JarEntry(filename) {
|
||||
};
|
||||
}
|
||||
try {
|
||||
JarInputStream is = new JarInputStream(super.getInputStream(wrappedJarFile));
|
||||
try {
|
||||
JarEntry entry;
|
||||
while ((entry = is.getNextJarEntry()) != null) {
|
||||
if (entry.getName().equals(filename)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Undefined Error", e);
|
||||
}
|
||||
return null;
|
||||
// throw new RuntimeException("Entry not found : " + name);
|
||||
}
|
||||
|
||||
public Enumeration entries() {
|
||||
try {
|
||||
final JarInputStream is = new JarInputStream(super.getInputStream(wrappedJarFile));
|
||||
return new Enumeration() {
|
||||
|
||||
protected JarEntry next;
|
||||
|
||||
public Object nextElement() {
|
||||
if (next != null) {
|
||||
JarEntry tmp = next;
|
||||
next = null;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
try {
|
||||
return is.getNextJarEntry();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Undefined Error", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
if (next != null) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
next = is.getNextJarEntry();
|
||||
if (next == null) {
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Undefined Error", e);
|
||||
}
|
||||
return next != null;
|
||||
}
|
||||
};
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Undefined Error", e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized InputStream getInputStream(ZipEntry ze) throws IOException {
|
||||
if (ze == null)
|
||||
return null;
|
||||
try {
|
||||
JarInputStream is = new JarInputStream(super.getInputStream(wrappedJarFile));
|
||||
if (filename.equals(MANIFEST_NAME)) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
is.getManifest().write(baos);
|
||||
return new ByteArrayInputStream(baos.toByteArray());
|
||||
}
|
||||
try {
|
||||
JarEntry entry;
|
||||
while ((entry = is.getNextJarEntry()) != null) {
|
||||
if (entry.getName().equals(ze.getName())) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
copy(is, baos);
|
||||
return new ByteArrayInputStream(baos.toByteArray());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Undefined Error", e);
|
||||
}
|
||||
|
||||
throw new RuntimeException("Entry not found : " + ze.getName());
|
||||
}
|
||||
|
||||
protected void copy(InputStream in, OutputStream out) throws IOException {
|
||||
byte[] buf = new byte[1024];
|
||||
while (true) {
|
||||
int len = in.read(buf);
|
||||
if (len < 0)
|
||||
break;
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2004-2010, P. Simon Tuffs (simon@simontuffs.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the full license at http://one-jar.sourceforge.net/one-jar-license.html
|
||||
* This license is also included in the distributions of this software
|
||||
* under doc/one-jar-license.txt
|
||||
*
|
||||
* Contributor: sebastian : http://code.google.com/u/@WBZRRlBYBxZHXQl9/
|
||||
* Original creator of the OneJarFile/OneJarUrlConnecion solution to resource location
|
||||
* using jar protocols.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.simontuffs.onejar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.jar.JarFile;
|
||||
public class OneJarURLConnection extends JarURLConnection {
|
||||
|
||||
private JarFile jarFile;
|
||||
|
||||
public OneJarURLConnection(URL url) throws MalformedURLException {
|
||||
super(url);
|
||||
}
|
||||
|
||||
public JarFile getJarFile() throws IOException {
|
||||
return jarFile;
|
||||
}
|
||||
|
||||
public void connect() throws IOException {
|
||||
String jarWithContent = getEntryName();
|
||||
int separator = jarWithContent.indexOf("!/");
|
||||
// Handle the case where a URL points to the top-level jar file, i.e. no '!/' separator.
|
||||
if (separator >= 0) {
|
||||
String jarFilename = jarWithContent, filename = null;
|
||||
jarFilename = jarWithContent.substring(0, separator++);
|
||||
filename = jarWithContent.substring(++separator);
|
||||
jarFile = new OneJarFile(Boot.getMyJarPath(), jarFilename, filename);
|
||||
} else {
|
||||
// Entry in the top-level One-JAR.
|
||||
jarFile = new JarFile(Boot.getMyJarPath());
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return jarFile.getInputStream(jarFile.getJarEntry(getEntryName()));
|
||||
}
|
||||
|
||||
}
|
44
Java Projects/Bookshelf/bin/resources/stylesheet.css
Normal file
@@ -0,0 +1,44 @@
|
||||
.darkPane,
|
||||
.root {
|
||||
-fx-background-color: rgba(68,68,68, 0.8);/*#444444;*/
|
||||
}
|
||||
.toggle-button,
|
||||
.button {
|
||||
-fx-background-color:
|
||||
#c3c4c4,
|
||||
linear-gradient(#d6d6d6 50%, white 100%),
|
||||
radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%);
|
||||
-fx-background-radius: 30;
|
||||
-fx-background-insets: 0,1,1;
|
||||
-fx-text-fill: black;
|
||||
-fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 3, 0.0 , 0 , 1 );
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
-fx-cursor: hand;
|
||||
-fx-background-color:
|
||||
linear-gradient(from 0% 93% to 0% 100%, #a34313 0%, #903b12 100%),
|
||||
#9d4024,
|
||||
#d86e3a,
|
||||
radial-gradient(center 50% 50%, radius 100%, #ea7f4b, #c54e2c);
|
||||
}
|
||||
|
||||
.toggle-button:hover {
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
.toggle-button:selected {
|
||||
-fx-background-color:
|
||||
linear-gradient(from 0% 93% to 0% 100%, #a34313 0%, #903b12 100%),
|
||||
#9d4024,
|
||||
#d86e3a,
|
||||
radial-gradient(center 50% 50%, radius 100%, #ea7f4b, #c54e2c);
|
||||
}
|
||||
|
||||
.toggle-button:selected:focused {
|
||||
-fx-background-color:
|
||||
linear-gradient(from 0% 93% to 0% 100%, #a34313 0%, #903b12 100%),
|
||||
#9d4024,
|
||||
#d86e3a,
|
||||
radial-gradient(center 50% 50%, radius 100%, #ea7f4b, #c54e2c);
|
||||
}
|
38
Java Projects/Bookshelf/bin/resources/window.fxml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.ToggleButton?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.TilePane?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" styleClass="darkPane" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller">
|
||||
<children>
|
||||
<ScrollPane fx:id="stackPane" layoutY="32.0" prefHeight="568.0" prefWidth="800.0" styleClass="darkPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="32.0">
|
||||
<content>
|
||||
<TilePane fx:id="tilePane" hgap="15.0" prefHeight="566.0" prefWidth="797.0" styleClass="darkPane" vgap="15.0">
|
||||
<children>
|
||||
<Label fx:id="dir" contentDisplay="CENTER" onMouseClicked="#setNewDir" prefHeight="566.0" prefWidth="798.0" text="Choose Dir" textAlignment="CENTER" textFill="#dfdfdf" textOverrun="CLIP" underline="false" wrapText="true">
|
||||
<font>
|
||||
<Font name="System Bold" size="32.0" />
|
||||
</font>
|
||||
</Label>
|
||||
</children>
|
||||
</TilePane>
|
||||
</content>
|
||||
</ScrollPane>
|
||||
<HBox AnchorPane.bottomAnchor="568.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
<Button mnemonicParsing="false" onAction="#close" prefHeight="32.0" prefWidth="50.0" text="X" />
|
||||
<ToggleButton mnemonicParsing="false" onAction="#setJarUse" prefHeight="32.0" prefWidth="140.0" text="Windows: Use Jars" />
|
||||
<TextField fx:id="pathTxt" onKeyReleased="#Enter" prefHeight="32.0" prefWidth="442.0" promptText="File/Dir Path..." HBox.hgrow="ALWAYS" />
|
||||
<Button fx:id="fileBttn" mnemonicParsing="false" onAction="#openFile" prefHeight="32.0" prefWidth="79.0" text="File" />
|
||||
<Button fx:id="clearBttn" mnemonicParsing="false" onAction="#clearBttnClick" prefHeight="32.0" prefWidth="79.0" text="Clear" />
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
</AnchorPane>
|
BIN
Java Projects/Bookshelf/bin/thumbs/BaudolinoUmbertoEco(doxyerfre).png
Executable file
After Width: | Height: | Size: 310 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/BurningEmpires.png
Executable file
After Width: | Height: | Size: 284 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/BurningWheel.png
Executable file
After Width: | Height: | Size: 335 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/ClassAGuideThroughtheAmericanStatusSystem.png
Executable file
After Width: | Height: | Size: 23 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/DD5ePremadeCharacterSheets.png
Executable file
After Width: | Height: | Size: 218 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/Dark_Matter_History.png
Executable file
After Width: | Height: | Size: 20 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/DavidFosterWallaceInfiniteJest.png
Executable file
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 205 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/DnD_5e_Players_Handbook_BnW_OCR.png
Executable file
After Width: | Height: | Size: 398 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/EckhartTolleThePowerofNow.png
Executable file
After Width: | Height: | Size: 16 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/EffectiveJava2ndEdition.png
Executable file
After Width: | Height: | Size: 220 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/EricBerneGamesPeoplePlay.png
Executable file
After Width: | Height: | Size: 15 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/EverydayZen_loveandwork.png
Executable file
After Width: | Height: | Size: 380 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/GoodOldNeon.png
Executable file
After Width: | Height: | Size: 35 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/HeartofDarknes.png
Executable file
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 43 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/LaPutaDeBabilonia.png
Executable file
After Width: | Height: | Size: 71 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/MariusTheEpicurean.png
Executable file
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 39 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/NASAProgrammingGuideBook.png
Executable file
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 129 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/PlatosTheApology.png
Executable file
After Width: | Height: | Size: 27 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/Seveneves.png
Executable file
After Width: | Height: | Size: 264 KiB |
BIN
Java Projects/Bookshelf/bin/thumbs/ShitMyDadSaysHalpernJJustin.png
Executable file
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 119 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 130 KiB |