diff --git a/AUTHORS b/AUTHORS index 13159c61d..fc4921a1c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,27 @@ -nzbget: - Sven Henkel (versions 0.1.0 - ?) - Bo Cordes Petersen (versions ? - 0.2.3) +NZBGet: Andrey Prygunkov (versions 0.3.0 and later) + Bo Cordes Petersen (versions ? - 0.2.3) + Sven Henkel (versions 0.1.0 - ?) + +PAR2: + Peter Brian Clements + +PAR2 library API: + Francois Lesueur + +jQuery: + John Resig + The Dojo Foundation + +Bootstrap: + Twitter, Inc + +RaphaĆ«l: + Dmitry Baranovskiy + Sencha Labs + +Elycharts: + Void Labs s.n.c. + +iconSweets: + Yummygum \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 9b926958f..62f9af060 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ # -# This file if part of nzbget +# This file is part of nzbget # # Copyright (C) 2008-2014 Andrey Prygunkov # @@ -121,6 +121,52 @@ nzbget_SOURCES = \ daemon/util/Util.h \ svn_version.cpp +if WITH_PAR2 +nzbget_SOURCES += \ + lib/par2/commandline.cpp \ + lib/par2/commandline.h \ + lib/par2/crc.cpp \ + lib/par2/crc.h \ + lib/par2/creatorpacket.cpp \ + lib/par2/creatorpacket.h \ + lib/par2/criticalpacket.cpp \ + lib/par2/criticalpacket.h \ + lib/par2/datablock.cpp \ + lib/par2/datablock.h \ + lib/par2/descriptionpacket.cpp \ + lib/par2/descriptionpacket.h \ + lib/par2/diskfile.cpp \ + lib/par2/diskfile.h \ + lib/par2/filechecksummer.cpp \ + lib/par2/filechecksummer.h \ + lib/par2/galois.cpp \ + lib/par2/galois.h \ + lib/par2/letype.h \ + lib/par2/mainpacket.cpp \ + lib/par2/mainpacket.h \ + lib/par2/md5.cpp \ + lib/par2/md5.h \ + lib/par2/par2cmdline.h \ + lib/par2/par2creatorsourcefile.cpp \ + lib/par2/par2creatorsourcefile.h \ + lib/par2/par2fileformat.cpp \ + lib/par2/par2fileformat.h \ + lib/par2/par2repairer.cpp \ + lib/par2/par2repairer.h \ + lib/par2/par2repairersourcefile.cpp \ + lib/par2/par2repairersourcefile.h \ + lib/par2/parheaders.cpp \ + lib/par2/parheaders.h \ + lib/par2/recoverypacket.cpp \ + lib/par2/recoverypacket.h \ + lib/par2/reedsolomon.cpp \ + lib/par2/reedsolomon.h \ + lib/par2/verificationhashtable.cpp \ + lib/par2/verificationhashtable.h \ + lib/par2/verificationpacket.cpp \ + lib/par2/verificationpacket.h +endif + AM_CPPFLAGS = \ -I$(srcdir)/daemon/connect \ -I$(srcdir)/daemon/feed \ @@ -130,7 +176,8 @@ AM_CPPFLAGS = \ -I$(srcdir)/daemon/postprocess \ -I$(srcdir)/daemon/queue \ -I$(srcdir)/daemon/remote \ - -I$(srcdir)/daemon/util + -I$(srcdir)/daemon/util \ + -I$(srcdir)/lib/par2 EXTRA_DIST = \ Makefile.cvs \ @@ -182,7 +229,9 @@ osx_FILES = \ doc_FILES = \ README \ ChangeLog \ - COPYING + COPYING \ + lib/par2/AUTHORS \ + lib/par2/README exampleconf_FILES = \ nzbget.conf @@ -319,4 +368,5 @@ clean-bak: rm *~ dist-hook: find $(distdir)/daemon -type f -print -exec chmod -x {} \; find $(distdir)/webui -type f -print -exec chmod -x {} \; + find $(distdir)/lib -type f -print -exec chmod -x {} \; diff --git a/Makefile.in b/Makefile.in index 6dd6b092c..1ebe46a8e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -15,7 +15,7 @@ @SET_MAKE@ # -# This file if part of nzbget +# This file is part of nzbget # # Copyright (C) 2008-2014 Andrey Prygunkov # @@ -61,6 +61,50 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ bin_PROGRAMS = nzbget$(EXEEXT) +@WITH_PAR2_TRUE@am__append_1 = \ +@WITH_PAR2_TRUE@ lib/par2/commandline.cpp \ +@WITH_PAR2_TRUE@ lib/par2/commandline.h \ +@WITH_PAR2_TRUE@ lib/par2/crc.cpp \ +@WITH_PAR2_TRUE@ lib/par2/crc.h \ +@WITH_PAR2_TRUE@ lib/par2/creatorpacket.cpp \ +@WITH_PAR2_TRUE@ lib/par2/creatorpacket.h \ +@WITH_PAR2_TRUE@ lib/par2/criticalpacket.cpp \ +@WITH_PAR2_TRUE@ lib/par2/criticalpacket.h \ +@WITH_PAR2_TRUE@ lib/par2/datablock.cpp \ +@WITH_PAR2_TRUE@ lib/par2/datablock.h \ +@WITH_PAR2_TRUE@ lib/par2/descriptionpacket.cpp \ +@WITH_PAR2_TRUE@ lib/par2/descriptionpacket.h \ +@WITH_PAR2_TRUE@ lib/par2/diskfile.cpp \ +@WITH_PAR2_TRUE@ lib/par2/diskfile.h \ +@WITH_PAR2_TRUE@ lib/par2/filechecksummer.cpp \ +@WITH_PAR2_TRUE@ lib/par2/filechecksummer.h \ +@WITH_PAR2_TRUE@ lib/par2/galois.cpp \ +@WITH_PAR2_TRUE@ lib/par2/galois.h \ +@WITH_PAR2_TRUE@ lib/par2/letype.h \ +@WITH_PAR2_TRUE@ lib/par2/mainpacket.cpp \ +@WITH_PAR2_TRUE@ lib/par2/mainpacket.h \ +@WITH_PAR2_TRUE@ lib/par2/md5.cpp \ +@WITH_PAR2_TRUE@ lib/par2/md5.h \ +@WITH_PAR2_TRUE@ lib/par2/par2cmdline.h \ +@WITH_PAR2_TRUE@ lib/par2/par2creatorsourcefile.cpp \ +@WITH_PAR2_TRUE@ lib/par2/par2creatorsourcefile.h \ +@WITH_PAR2_TRUE@ lib/par2/par2fileformat.cpp \ +@WITH_PAR2_TRUE@ lib/par2/par2fileformat.h \ +@WITH_PAR2_TRUE@ lib/par2/par2repairer.cpp \ +@WITH_PAR2_TRUE@ lib/par2/par2repairer.h \ +@WITH_PAR2_TRUE@ lib/par2/par2repairersourcefile.cpp \ +@WITH_PAR2_TRUE@ lib/par2/par2repairersourcefile.h \ +@WITH_PAR2_TRUE@ lib/par2/parheaders.cpp \ +@WITH_PAR2_TRUE@ lib/par2/parheaders.h \ +@WITH_PAR2_TRUE@ lib/par2/recoverypacket.cpp \ +@WITH_PAR2_TRUE@ lib/par2/recoverypacket.h \ +@WITH_PAR2_TRUE@ lib/par2/reedsolomon.cpp \ +@WITH_PAR2_TRUE@ lib/par2/reedsolomon.h \ +@WITH_PAR2_TRUE@ lib/par2/verificationhashtable.cpp \ +@WITH_PAR2_TRUE@ lib/par2/verificationhashtable.h \ +@WITH_PAR2_TRUE@ lib/par2/verificationpacket.cpp \ +@WITH_PAR2_TRUE@ lib/par2/verificationpacket.h + DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \ $(dist_exampleconf_DATA) $(nobase_dist_scripts_SCRIPTS) \ $(nobase_dist_webui_DATA) $(srcdir)/Makefile.am \ @@ -82,6 +126,97 @@ am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(scriptsdir)" \ "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)" binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) PROGRAMS = $(bin_PROGRAMS) +am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \ + daemon/connect/Connection.h daemon/connect/TLS.cpp \ + daemon/connect/TLS.h daemon/connect/WebDownloader.cpp \ + daemon/connect/WebDownloader.h daemon/feed/FeedCoordinator.cpp \ + daemon/feed/FeedCoordinator.h daemon/feed/FeedFile.cpp \ + daemon/feed/FeedFile.h daemon/feed/FeedFilter.cpp \ + daemon/feed/FeedFilter.h daemon/feed/FeedInfo.cpp \ + daemon/feed/FeedInfo.h daemon/frontend/ColoredFrontend.cpp \ + daemon/frontend/ColoredFrontend.h daemon/frontend/Frontend.cpp \ + daemon/frontend/Frontend.h \ + daemon/frontend/LoggableFrontend.cpp \ + daemon/frontend/LoggableFrontend.h \ + daemon/frontend/NCursesFrontend.cpp \ + daemon/frontend/NCursesFrontend.h daemon/main/Maintenance.cpp \ + daemon/main/Maintenance.h daemon/main/nzbget.cpp \ + daemon/main/nzbget.h daemon/main/Options.cpp \ + daemon/main/Options.h daemon/main/Scheduler.cpp \ + daemon/main/Scheduler.h daemon/nntp/ArticleDownloader.cpp \ + daemon/nntp/ArticleDownloader.h daemon/nntp/ArticleWriter.cpp \ + daemon/nntp/ArticleWriter.h daemon/nntp/Decoder.cpp \ + daemon/nntp/Decoder.h daemon/nntp/NewsServer.cpp \ + daemon/nntp/NewsServer.h daemon/nntp/NNTPConnection.cpp \ + daemon/nntp/NNTPConnection.h daemon/nntp/ServerPool.cpp \ + daemon/nntp/ServerPool.h daemon/nntp/StatMeter.cpp \ + daemon/nntp/StatMeter.h daemon/postprocess/ParChecker.cpp \ + daemon/postprocess/ParChecker.h \ + daemon/postprocess/ParCoordinator.cpp \ + daemon/postprocess/ParCoordinator.h \ + daemon/postprocess/ParRenamer.cpp \ + daemon/postprocess/ParRenamer.h \ + daemon/postprocess/PostScript.cpp \ + daemon/postprocess/PostScript.h \ + daemon/postprocess/PrePostProcessor.cpp \ + daemon/postprocess/PrePostProcessor.h \ + daemon/postprocess/Unpack.cpp daemon/postprocess/Unpack.h \ + daemon/queue/DiskState.cpp daemon/queue/DiskState.h \ + daemon/queue/DownloadInfo.cpp daemon/queue/DownloadInfo.h \ + daemon/queue/DupeCoordinator.cpp \ + daemon/queue/DupeCoordinator.h \ + daemon/queue/HistoryCoordinator.cpp \ + daemon/queue/HistoryCoordinator.h daemon/queue/NZBFile.cpp \ + daemon/queue/NZBFile.h daemon/queue/QueueCoordinator.cpp \ + daemon/queue/QueueCoordinator.h daemon/queue/QueueEditor.cpp \ + daemon/queue/QueueEditor.h daemon/queue/QueueScript.cpp \ + daemon/queue/QueueScript.h daemon/queue/Scanner.cpp \ + daemon/queue/Scanner.h daemon/queue/UrlCoordinator.cpp \ + daemon/queue/UrlCoordinator.h daemon/remote/BinRpc.cpp \ + daemon/remote/BinRpc.h daemon/remote/MessageBase.h \ + daemon/remote/RemoteClient.cpp daemon/remote/RemoteClient.h \ + daemon/remote/RemoteServer.cpp daemon/remote/RemoteServer.h \ + daemon/remote/WebServer.cpp daemon/remote/WebServer.h \ + daemon/remote/XmlRpc.cpp daemon/remote/XmlRpc.h \ + daemon/util/Log.cpp daemon/util/Log.h daemon/util/Observer.cpp \ + daemon/util/Observer.h daemon/util/Script.cpp \ + daemon/util/Script.h daemon/util/Thread.cpp \ + daemon/util/Thread.h daemon/util/Util.cpp daemon/util/Util.h \ + svn_version.cpp lib/par2/commandline.cpp \ + lib/par2/commandline.h lib/par2/crc.cpp lib/par2/crc.h \ + lib/par2/creatorpacket.cpp lib/par2/creatorpacket.h \ + lib/par2/criticalpacket.cpp lib/par2/criticalpacket.h \ + lib/par2/datablock.cpp lib/par2/datablock.h \ + lib/par2/descriptionpacket.cpp lib/par2/descriptionpacket.h \ + lib/par2/diskfile.cpp lib/par2/diskfile.h \ + lib/par2/filechecksummer.cpp lib/par2/filechecksummer.h \ + lib/par2/galois.cpp lib/par2/galois.h lib/par2/letype.h \ + lib/par2/mainpacket.cpp lib/par2/mainpacket.h lib/par2/md5.cpp \ + lib/par2/md5.h lib/par2/par2cmdline.h \ + lib/par2/par2creatorsourcefile.cpp \ + lib/par2/par2creatorsourcefile.h lib/par2/par2fileformat.cpp \ + lib/par2/par2fileformat.h lib/par2/par2repairer.cpp \ + lib/par2/par2repairer.h lib/par2/par2repairersourcefile.cpp \ + lib/par2/par2repairersourcefile.h lib/par2/parheaders.cpp \ + lib/par2/parheaders.h lib/par2/recoverypacket.cpp \ + lib/par2/recoverypacket.h lib/par2/reedsolomon.cpp \ + lib/par2/reedsolomon.h lib/par2/verificationhashtable.cpp \ + lib/par2/verificationhashtable.h \ + lib/par2/verificationpacket.cpp lib/par2/verificationpacket.h +@WITH_PAR2_TRUE@am__objects_1 = commandline.$(OBJEXT) crc.$(OBJEXT) \ +@WITH_PAR2_TRUE@ creatorpacket.$(OBJEXT) \ +@WITH_PAR2_TRUE@ criticalpacket.$(OBJEXT) datablock.$(OBJEXT) \ +@WITH_PAR2_TRUE@ descriptionpacket.$(OBJEXT) diskfile.$(OBJEXT) \ +@WITH_PAR2_TRUE@ filechecksummer.$(OBJEXT) galois.$(OBJEXT) \ +@WITH_PAR2_TRUE@ mainpacket.$(OBJEXT) md5.$(OBJEXT) \ +@WITH_PAR2_TRUE@ par2creatorsourcefile.$(OBJEXT) \ +@WITH_PAR2_TRUE@ par2fileformat.$(OBJEXT) \ +@WITH_PAR2_TRUE@ par2repairer.$(OBJEXT) \ +@WITH_PAR2_TRUE@ par2repairersourcefile.$(OBJEXT) \ +@WITH_PAR2_TRUE@ parheaders.$(OBJEXT) recoverypacket.$(OBJEXT) \ +@WITH_PAR2_TRUE@ reedsolomon.$(OBJEXT) \ +@WITH_PAR2_TRUE@ verificationhashtable.$(OBJEXT) \ +@WITH_PAR2_TRUE@ verificationpacket.$(OBJEXT) am_nzbget_OBJECTS = Connection.$(OBJEXT) TLS.$(OBJEXT) \ WebDownloader.$(OBJEXT) FeedCoordinator.$(OBJEXT) \ FeedFile.$(OBJEXT) FeedFilter.$(OBJEXT) FeedInfo.$(OBJEXT) \ @@ -102,7 +237,7 @@ am_nzbget_OBJECTS = Connection.$(OBJEXT) TLS.$(OBJEXT) \ RemoteClient.$(OBJEXT) RemoteServer.$(OBJEXT) \ WebServer.$(OBJEXT) XmlRpc.$(OBJEXT) Log.$(OBJEXT) \ Observer.$(OBJEXT) Script.$(OBJEXT) Thread.$(OBJEXT) \ - Util.$(OBJEXT) svn_version.$(OBJEXT) + Util.$(OBJEXT) svn_version.$(OBJEXT) $(am__objects_1) nzbget_OBJECTS = $(am_nzbget_OBJECTS) nzbget_LDADD = $(LDADD) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -127,7 +262,7 @@ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ SOURCES = $(nzbget_SOURCES) -DIST_SOURCES = $(nzbget_SOURCES) +DIST_SOURCES = $(am__nzbget_SOURCES_DIST) dist_docDATA_INSTALL = $(INSTALL_DATA) dist_exampleconfDATA_INSTALL = $(INSTALL_DATA) nobase_dist_webuiDATA_INSTALL = $(install_sh_DATA) @@ -191,6 +326,8 @@ SHELL = @SHELL@ STRIP = @STRIP@ TAR = @TAR@ VERSION = @VERSION@ +WITH_PAR2_FALSE = @WITH_PAR2_FALSE@ +WITH_PAR2_TRUE = @WITH_PAR2_TRUE@ ac_ct_CXX = @ac_ct_CXX@ am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ @@ -221,8 +358,6 @@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ -libsigc_CFLAGS = @libsigc_CFLAGS@ -libsigc_LIBS = @libsigc_LIBS@ libxml2_CFLAGS = @libxml2_CFLAGS@ libxml2_LIBS = @libxml2_LIBS@ localedir = @localedir@ @@ -244,52 +379,31 @@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ -nzbget_SOURCES = \ - daemon/connect/Connection.cpp \ - daemon/connect/Connection.h \ - daemon/connect/TLS.cpp \ - daemon/connect/TLS.h \ - daemon/connect/WebDownloader.cpp \ - daemon/connect/WebDownloader.h \ - daemon/feed/FeedCoordinator.cpp \ - daemon/feed/FeedCoordinator.h \ - daemon/feed/FeedFile.cpp \ - daemon/feed/FeedFile.h \ - daemon/feed/FeedFilter.cpp \ - daemon/feed/FeedFilter.h \ - daemon/feed/FeedInfo.cpp \ - daemon/feed/FeedInfo.h \ - daemon/frontend/ColoredFrontend.cpp \ - daemon/frontend/ColoredFrontend.h \ - daemon/frontend/Frontend.cpp \ +nzbget_SOURCES = daemon/connect/Connection.cpp \ + daemon/connect/Connection.h daemon/connect/TLS.cpp \ + daemon/connect/TLS.h daemon/connect/WebDownloader.cpp \ + daemon/connect/WebDownloader.h daemon/feed/FeedCoordinator.cpp \ + daemon/feed/FeedCoordinator.h daemon/feed/FeedFile.cpp \ + daemon/feed/FeedFile.h daemon/feed/FeedFilter.cpp \ + daemon/feed/FeedFilter.h daemon/feed/FeedInfo.cpp \ + daemon/feed/FeedInfo.h daemon/frontend/ColoredFrontend.cpp \ + daemon/frontend/ColoredFrontend.h daemon/frontend/Frontend.cpp \ daemon/frontend/Frontend.h \ daemon/frontend/LoggableFrontend.cpp \ daemon/frontend/LoggableFrontend.h \ daemon/frontend/NCursesFrontend.cpp \ - daemon/frontend/NCursesFrontend.h \ - daemon/main/Maintenance.cpp \ - daemon/main/Maintenance.h \ - daemon/main/nzbget.cpp \ - daemon/main/nzbget.h \ - daemon/main/Options.cpp \ - daemon/main/Options.h \ - daemon/main/Scheduler.cpp \ - daemon/main/Scheduler.h \ - daemon/nntp/ArticleDownloader.cpp \ - daemon/nntp/ArticleDownloader.h \ - daemon/nntp/ArticleWriter.cpp \ - daemon/nntp/ArticleWriter.h \ - daemon/nntp/Decoder.cpp \ - daemon/nntp/Decoder.h \ - daemon/nntp/NewsServer.cpp \ - daemon/nntp/NewsServer.h \ - daemon/nntp/NNTPConnection.cpp \ - daemon/nntp/NNTPConnection.h \ - daemon/nntp/ServerPool.cpp \ - daemon/nntp/ServerPool.h \ - daemon/nntp/StatMeter.cpp \ - daemon/nntp/StatMeter.h \ - daemon/postprocess/ParChecker.cpp \ + daemon/frontend/NCursesFrontend.h daemon/main/Maintenance.cpp \ + daemon/main/Maintenance.h daemon/main/nzbget.cpp \ + daemon/main/nzbget.h daemon/main/Options.cpp \ + daemon/main/Options.h daemon/main/Scheduler.cpp \ + daemon/main/Scheduler.h daemon/nntp/ArticleDownloader.cpp \ + daemon/nntp/ArticleDownloader.h daemon/nntp/ArticleWriter.cpp \ + daemon/nntp/ArticleWriter.h daemon/nntp/Decoder.cpp \ + daemon/nntp/Decoder.h daemon/nntp/NewsServer.cpp \ + daemon/nntp/NewsServer.h daemon/nntp/NNTPConnection.cpp \ + daemon/nntp/NNTPConnection.h daemon/nntp/ServerPool.cpp \ + daemon/nntp/ServerPool.h daemon/nntp/StatMeter.cpp \ + daemon/nntp/StatMeter.h daemon/postprocess/ParChecker.cpp \ daemon/postprocess/ParChecker.h \ daemon/postprocess/ParCoordinator.cpp \ daemon/postprocess/ParCoordinator.h \ @@ -299,51 +413,29 @@ nzbget_SOURCES = \ daemon/postprocess/PostScript.h \ daemon/postprocess/PrePostProcessor.cpp \ daemon/postprocess/PrePostProcessor.h \ - daemon/postprocess/Unpack.cpp \ - daemon/postprocess/Unpack.h \ - daemon/queue/DiskState.cpp \ - daemon/queue/DiskState.h \ - daemon/queue/DownloadInfo.cpp \ - daemon/queue/DownloadInfo.h \ + daemon/postprocess/Unpack.cpp daemon/postprocess/Unpack.h \ + daemon/queue/DiskState.cpp daemon/queue/DiskState.h \ + daemon/queue/DownloadInfo.cpp daemon/queue/DownloadInfo.h \ daemon/queue/DupeCoordinator.cpp \ daemon/queue/DupeCoordinator.h \ daemon/queue/HistoryCoordinator.cpp \ - daemon/queue/HistoryCoordinator.h \ - daemon/queue/NZBFile.cpp \ - daemon/queue/NZBFile.h \ - daemon/queue/QueueCoordinator.cpp \ - daemon/queue/QueueCoordinator.h \ - daemon/queue/QueueEditor.cpp \ - daemon/queue/QueueEditor.h \ - daemon/queue/QueueScript.cpp \ - daemon/queue/QueueScript.h \ - daemon/queue/Scanner.cpp \ - daemon/queue/Scanner.h \ - daemon/queue/UrlCoordinator.cpp \ - daemon/queue/UrlCoordinator.h \ - daemon/remote/BinRpc.cpp \ - daemon/remote/BinRpc.h \ - daemon/remote/MessageBase.h \ - daemon/remote/RemoteClient.cpp \ - daemon/remote/RemoteClient.h \ - daemon/remote/RemoteServer.cpp \ - daemon/remote/RemoteServer.h \ - daemon/remote/WebServer.cpp \ - daemon/remote/WebServer.h \ - daemon/remote/XmlRpc.cpp \ - daemon/remote/XmlRpc.h \ - daemon/util/Log.cpp \ - daemon/util/Log.h \ - daemon/util/Observer.cpp \ - daemon/util/Observer.h \ - daemon/util/Script.cpp \ - daemon/util/Script.h \ - daemon/util/Thread.cpp \ - daemon/util/Thread.h \ - daemon/util/Util.cpp \ - daemon/util/Util.h \ - svn_version.cpp - + daemon/queue/HistoryCoordinator.h daemon/queue/NZBFile.cpp \ + daemon/queue/NZBFile.h daemon/queue/QueueCoordinator.cpp \ + daemon/queue/QueueCoordinator.h daemon/queue/QueueEditor.cpp \ + daemon/queue/QueueEditor.h daemon/queue/QueueScript.cpp \ + daemon/queue/QueueScript.h daemon/queue/Scanner.cpp \ + daemon/queue/Scanner.h daemon/queue/UrlCoordinator.cpp \ + daemon/queue/UrlCoordinator.h daemon/remote/BinRpc.cpp \ + daemon/remote/BinRpc.h daemon/remote/MessageBase.h \ + daemon/remote/RemoteClient.cpp daemon/remote/RemoteClient.h \ + daemon/remote/RemoteServer.cpp daemon/remote/RemoteServer.h \ + daemon/remote/WebServer.cpp daemon/remote/WebServer.h \ + daemon/remote/XmlRpc.cpp daemon/remote/XmlRpc.h \ + daemon/util/Log.cpp daemon/util/Log.h daemon/util/Observer.cpp \ + daemon/util/Observer.h daemon/util/Script.cpp \ + daemon/util/Script.h daemon/util/Thread.cpp \ + daemon/util/Thread.h daemon/util/Util.cpp daemon/util/Util.h \ + svn_version.cpp $(am__append_1) AM_CPPFLAGS = \ -I$(srcdir)/daemon/connect \ -I$(srcdir)/daemon/feed \ @@ -353,7 +445,8 @@ AM_CPPFLAGS = \ -I$(srcdir)/daemon/postprocess \ -I$(srcdir)/daemon/queue \ -I$(srcdir)/daemon/remote \ - -I$(srcdir)/daemon/util + -I$(srcdir)/daemon/util \ + -I$(srcdir)/lib/par2 EXTRA_DIST = \ Makefile.cvs \ @@ -405,7 +498,9 @@ osx_FILES = \ doc_FILES = \ README \ ChangeLog \ - COPYING + COPYING \ + lib/par2/AUTHORS \ + lib/par2/README exampleconf_FILES = \ nzbget.conf @@ -641,8 +736,28 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebDownloader.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebServer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commandline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/creatorpacket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/criticalpacket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datablock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/descriptionpacket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diskfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filechecksummer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/galois.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainpacket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nzbget.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par2creatorsourcefile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par2fileformat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par2repairer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par2repairersourcefile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parheaders.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/recoverypacket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reedsolomon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svn_version.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verificationhashtable.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verificationpacket.Po@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ @@ -1329,6 +1444,286 @@ Util.obj: daemon/util/Util.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/util/Util.cpp' object='Util.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Util.obj `if test -f 'daemon/util/Util.cpp'; then $(CYGPATH_W) 'daemon/util/Util.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/util/Util.cpp'; fi` + +commandline.o: lib/par2/commandline.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT commandline.o -MD -MP -MF "$(DEPDIR)/commandline.Tpo" -c -o commandline.o `test -f 'lib/par2/commandline.cpp' || echo '$(srcdir)/'`lib/par2/commandline.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/commandline.Tpo" "$(DEPDIR)/commandline.Po"; else rm -f "$(DEPDIR)/commandline.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/commandline.cpp' object='commandline.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o commandline.o `test -f 'lib/par2/commandline.cpp' || echo '$(srcdir)/'`lib/par2/commandline.cpp + +commandline.obj: lib/par2/commandline.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT commandline.obj -MD -MP -MF "$(DEPDIR)/commandline.Tpo" -c -o commandline.obj `if test -f 'lib/par2/commandline.cpp'; then $(CYGPATH_W) 'lib/par2/commandline.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/commandline.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/commandline.Tpo" "$(DEPDIR)/commandline.Po"; else rm -f "$(DEPDIR)/commandline.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/commandline.cpp' object='commandline.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o commandline.obj `if test -f 'lib/par2/commandline.cpp'; then $(CYGPATH_W) 'lib/par2/commandline.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/commandline.cpp'; fi` + +crc.o: lib/par2/crc.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc.o -MD -MP -MF "$(DEPDIR)/crc.Tpo" -c -o crc.o `test -f 'lib/par2/crc.cpp' || echo '$(srcdir)/'`lib/par2/crc.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/crc.Tpo" "$(DEPDIR)/crc.Po"; else rm -f "$(DEPDIR)/crc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/crc.cpp' object='crc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc.o `test -f 'lib/par2/crc.cpp' || echo '$(srcdir)/'`lib/par2/crc.cpp + +crc.obj: lib/par2/crc.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc.obj -MD -MP -MF "$(DEPDIR)/crc.Tpo" -c -o crc.obj `if test -f 'lib/par2/crc.cpp'; then $(CYGPATH_W) 'lib/par2/crc.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/crc.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/crc.Tpo" "$(DEPDIR)/crc.Po"; else rm -f "$(DEPDIR)/crc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/crc.cpp' object='crc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc.obj `if test -f 'lib/par2/crc.cpp'; then $(CYGPATH_W) 'lib/par2/crc.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/crc.cpp'; fi` + +creatorpacket.o: lib/par2/creatorpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT creatorpacket.o -MD -MP -MF "$(DEPDIR)/creatorpacket.Tpo" -c -o creatorpacket.o `test -f 'lib/par2/creatorpacket.cpp' || echo '$(srcdir)/'`lib/par2/creatorpacket.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/creatorpacket.Tpo" "$(DEPDIR)/creatorpacket.Po"; else rm -f "$(DEPDIR)/creatorpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/creatorpacket.cpp' object='creatorpacket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o creatorpacket.o `test -f 'lib/par2/creatorpacket.cpp' || echo '$(srcdir)/'`lib/par2/creatorpacket.cpp + +creatorpacket.obj: lib/par2/creatorpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT creatorpacket.obj -MD -MP -MF "$(DEPDIR)/creatorpacket.Tpo" -c -o creatorpacket.obj `if test -f 'lib/par2/creatorpacket.cpp'; then $(CYGPATH_W) 'lib/par2/creatorpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/creatorpacket.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/creatorpacket.Tpo" "$(DEPDIR)/creatorpacket.Po"; else rm -f "$(DEPDIR)/creatorpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/creatorpacket.cpp' object='creatorpacket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o creatorpacket.obj `if test -f 'lib/par2/creatorpacket.cpp'; then $(CYGPATH_W) 'lib/par2/creatorpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/creatorpacket.cpp'; fi` + +criticalpacket.o: lib/par2/criticalpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT criticalpacket.o -MD -MP -MF "$(DEPDIR)/criticalpacket.Tpo" -c -o criticalpacket.o `test -f 'lib/par2/criticalpacket.cpp' || echo '$(srcdir)/'`lib/par2/criticalpacket.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/criticalpacket.Tpo" "$(DEPDIR)/criticalpacket.Po"; else rm -f "$(DEPDIR)/criticalpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/criticalpacket.cpp' object='criticalpacket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o criticalpacket.o `test -f 'lib/par2/criticalpacket.cpp' || echo '$(srcdir)/'`lib/par2/criticalpacket.cpp + +criticalpacket.obj: lib/par2/criticalpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT criticalpacket.obj -MD -MP -MF "$(DEPDIR)/criticalpacket.Tpo" -c -o criticalpacket.obj `if test -f 'lib/par2/criticalpacket.cpp'; then $(CYGPATH_W) 'lib/par2/criticalpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/criticalpacket.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/criticalpacket.Tpo" "$(DEPDIR)/criticalpacket.Po"; else rm -f "$(DEPDIR)/criticalpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/criticalpacket.cpp' object='criticalpacket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o criticalpacket.obj `if test -f 'lib/par2/criticalpacket.cpp'; then $(CYGPATH_W) 'lib/par2/criticalpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/criticalpacket.cpp'; fi` + +datablock.o: lib/par2/datablock.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT datablock.o -MD -MP -MF "$(DEPDIR)/datablock.Tpo" -c -o datablock.o `test -f 'lib/par2/datablock.cpp' || echo '$(srcdir)/'`lib/par2/datablock.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/datablock.Tpo" "$(DEPDIR)/datablock.Po"; else rm -f "$(DEPDIR)/datablock.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/datablock.cpp' object='datablock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o datablock.o `test -f 'lib/par2/datablock.cpp' || echo '$(srcdir)/'`lib/par2/datablock.cpp + +datablock.obj: lib/par2/datablock.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT datablock.obj -MD -MP -MF "$(DEPDIR)/datablock.Tpo" -c -o datablock.obj `if test -f 'lib/par2/datablock.cpp'; then $(CYGPATH_W) 'lib/par2/datablock.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/datablock.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/datablock.Tpo" "$(DEPDIR)/datablock.Po"; else rm -f "$(DEPDIR)/datablock.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/datablock.cpp' object='datablock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o datablock.obj `if test -f 'lib/par2/datablock.cpp'; then $(CYGPATH_W) 'lib/par2/datablock.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/datablock.cpp'; fi` + +descriptionpacket.o: lib/par2/descriptionpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT descriptionpacket.o -MD -MP -MF "$(DEPDIR)/descriptionpacket.Tpo" -c -o descriptionpacket.o `test -f 'lib/par2/descriptionpacket.cpp' || echo '$(srcdir)/'`lib/par2/descriptionpacket.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/descriptionpacket.Tpo" "$(DEPDIR)/descriptionpacket.Po"; else rm -f "$(DEPDIR)/descriptionpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/descriptionpacket.cpp' object='descriptionpacket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o descriptionpacket.o `test -f 'lib/par2/descriptionpacket.cpp' || echo '$(srcdir)/'`lib/par2/descriptionpacket.cpp + +descriptionpacket.obj: lib/par2/descriptionpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT descriptionpacket.obj -MD -MP -MF "$(DEPDIR)/descriptionpacket.Tpo" -c -o descriptionpacket.obj `if test -f 'lib/par2/descriptionpacket.cpp'; then $(CYGPATH_W) 'lib/par2/descriptionpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/descriptionpacket.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/descriptionpacket.Tpo" "$(DEPDIR)/descriptionpacket.Po"; else rm -f "$(DEPDIR)/descriptionpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/descriptionpacket.cpp' object='descriptionpacket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o descriptionpacket.obj `if test -f 'lib/par2/descriptionpacket.cpp'; then $(CYGPATH_W) 'lib/par2/descriptionpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/descriptionpacket.cpp'; fi` + +diskfile.o: lib/par2/diskfile.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT diskfile.o -MD -MP -MF "$(DEPDIR)/diskfile.Tpo" -c -o diskfile.o `test -f 'lib/par2/diskfile.cpp' || echo '$(srcdir)/'`lib/par2/diskfile.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/diskfile.Tpo" "$(DEPDIR)/diskfile.Po"; else rm -f "$(DEPDIR)/diskfile.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/diskfile.cpp' object='diskfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o diskfile.o `test -f 'lib/par2/diskfile.cpp' || echo '$(srcdir)/'`lib/par2/diskfile.cpp + +diskfile.obj: lib/par2/diskfile.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT diskfile.obj -MD -MP -MF "$(DEPDIR)/diskfile.Tpo" -c -o diskfile.obj `if test -f 'lib/par2/diskfile.cpp'; then $(CYGPATH_W) 'lib/par2/diskfile.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/diskfile.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/diskfile.Tpo" "$(DEPDIR)/diskfile.Po"; else rm -f "$(DEPDIR)/diskfile.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/diskfile.cpp' object='diskfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o diskfile.obj `if test -f 'lib/par2/diskfile.cpp'; then $(CYGPATH_W) 'lib/par2/diskfile.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/diskfile.cpp'; fi` + +filechecksummer.o: lib/par2/filechecksummer.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT filechecksummer.o -MD -MP -MF "$(DEPDIR)/filechecksummer.Tpo" -c -o filechecksummer.o `test -f 'lib/par2/filechecksummer.cpp' || echo '$(srcdir)/'`lib/par2/filechecksummer.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/filechecksummer.Tpo" "$(DEPDIR)/filechecksummer.Po"; else rm -f "$(DEPDIR)/filechecksummer.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/filechecksummer.cpp' object='filechecksummer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o filechecksummer.o `test -f 'lib/par2/filechecksummer.cpp' || echo '$(srcdir)/'`lib/par2/filechecksummer.cpp + +filechecksummer.obj: lib/par2/filechecksummer.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT filechecksummer.obj -MD -MP -MF "$(DEPDIR)/filechecksummer.Tpo" -c -o filechecksummer.obj `if test -f 'lib/par2/filechecksummer.cpp'; then $(CYGPATH_W) 'lib/par2/filechecksummer.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/filechecksummer.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/filechecksummer.Tpo" "$(DEPDIR)/filechecksummer.Po"; else rm -f "$(DEPDIR)/filechecksummer.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/filechecksummer.cpp' object='filechecksummer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o filechecksummer.obj `if test -f 'lib/par2/filechecksummer.cpp'; then $(CYGPATH_W) 'lib/par2/filechecksummer.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/filechecksummer.cpp'; fi` + +galois.o: lib/par2/galois.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT galois.o -MD -MP -MF "$(DEPDIR)/galois.Tpo" -c -o galois.o `test -f 'lib/par2/galois.cpp' || echo '$(srcdir)/'`lib/par2/galois.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/galois.Tpo" "$(DEPDIR)/galois.Po"; else rm -f "$(DEPDIR)/galois.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/galois.cpp' object='galois.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o galois.o `test -f 'lib/par2/galois.cpp' || echo '$(srcdir)/'`lib/par2/galois.cpp + +galois.obj: lib/par2/galois.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT galois.obj -MD -MP -MF "$(DEPDIR)/galois.Tpo" -c -o galois.obj `if test -f 'lib/par2/galois.cpp'; then $(CYGPATH_W) 'lib/par2/galois.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/galois.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/galois.Tpo" "$(DEPDIR)/galois.Po"; else rm -f "$(DEPDIR)/galois.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/galois.cpp' object='galois.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o galois.obj `if test -f 'lib/par2/galois.cpp'; then $(CYGPATH_W) 'lib/par2/galois.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/galois.cpp'; fi` + +mainpacket.o: lib/par2/mainpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT mainpacket.o -MD -MP -MF "$(DEPDIR)/mainpacket.Tpo" -c -o mainpacket.o `test -f 'lib/par2/mainpacket.cpp' || echo '$(srcdir)/'`lib/par2/mainpacket.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/mainpacket.Tpo" "$(DEPDIR)/mainpacket.Po"; else rm -f "$(DEPDIR)/mainpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/mainpacket.cpp' object='mainpacket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o mainpacket.o `test -f 'lib/par2/mainpacket.cpp' || echo '$(srcdir)/'`lib/par2/mainpacket.cpp + +mainpacket.obj: lib/par2/mainpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT mainpacket.obj -MD -MP -MF "$(DEPDIR)/mainpacket.Tpo" -c -o mainpacket.obj `if test -f 'lib/par2/mainpacket.cpp'; then $(CYGPATH_W) 'lib/par2/mainpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/mainpacket.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/mainpacket.Tpo" "$(DEPDIR)/mainpacket.Po"; else rm -f "$(DEPDIR)/mainpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/mainpacket.cpp' object='mainpacket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o mainpacket.obj `if test -f 'lib/par2/mainpacket.cpp'; then $(CYGPATH_W) 'lib/par2/mainpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/mainpacket.cpp'; fi` + +md5.o: lib/par2/md5.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT md5.o -MD -MP -MF "$(DEPDIR)/md5.Tpo" -c -o md5.o `test -f 'lib/par2/md5.cpp' || echo '$(srcdir)/'`lib/par2/md5.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/md5.Tpo" "$(DEPDIR)/md5.Po"; else rm -f "$(DEPDIR)/md5.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/md5.cpp' object='md5.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o md5.o `test -f 'lib/par2/md5.cpp' || echo '$(srcdir)/'`lib/par2/md5.cpp + +md5.obj: lib/par2/md5.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT md5.obj -MD -MP -MF "$(DEPDIR)/md5.Tpo" -c -o md5.obj `if test -f 'lib/par2/md5.cpp'; then $(CYGPATH_W) 'lib/par2/md5.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/md5.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/md5.Tpo" "$(DEPDIR)/md5.Po"; else rm -f "$(DEPDIR)/md5.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/md5.cpp' object='md5.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o md5.obj `if test -f 'lib/par2/md5.cpp'; then $(CYGPATH_W) 'lib/par2/md5.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/md5.cpp'; fi` + +par2creatorsourcefile.o: lib/par2/par2creatorsourcefile.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2creatorsourcefile.o -MD -MP -MF "$(DEPDIR)/par2creatorsourcefile.Tpo" -c -o par2creatorsourcefile.o `test -f 'lib/par2/par2creatorsourcefile.cpp' || echo '$(srcdir)/'`lib/par2/par2creatorsourcefile.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2creatorsourcefile.Tpo" "$(DEPDIR)/par2creatorsourcefile.Po"; else rm -f "$(DEPDIR)/par2creatorsourcefile.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2creatorsourcefile.cpp' object='par2creatorsourcefile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2creatorsourcefile.o `test -f 'lib/par2/par2creatorsourcefile.cpp' || echo '$(srcdir)/'`lib/par2/par2creatorsourcefile.cpp + +par2creatorsourcefile.obj: lib/par2/par2creatorsourcefile.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2creatorsourcefile.obj -MD -MP -MF "$(DEPDIR)/par2creatorsourcefile.Tpo" -c -o par2creatorsourcefile.obj `if test -f 'lib/par2/par2creatorsourcefile.cpp'; then $(CYGPATH_W) 'lib/par2/par2creatorsourcefile.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2creatorsourcefile.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2creatorsourcefile.Tpo" "$(DEPDIR)/par2creatorsourcefile.Po"; else rm -f "$(DEPDIR)/par2creatorsourcefile.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2creatorsourcefile.cpp' object='par2creatorsourcefile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2creatorsourcefile.obj `if test -f 'lib/par2/par2creatorsourcefile.cpp'; then $(CYGPATH_W) 'lib/par2/par2creatorsourcefile.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2creatorsourcefile.cpp'; fi` + +par2fileformat.o: lib/par2/par2fileformat.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2fileformat.o -MD -MP -MF "$(DEPDIR)/par2fileformat.Tpo" -c -o par2fileformat.o `test -f 'lib/par2/par2fileformat.cpp' || echo '$(srcdir)/'`lib/par2/par2fileformat.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2fileformat.Tpo" "$(DEPDIR)/par2fileformat.Po"; else rm -f "$(DEPDIR)/par2fileformat.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2fileformat.cpp' object='par2fileformat.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2fileformat.o `test -f 'lib/par2/par2fileformat.cpp' || echo '$(srcdir)/'`lib/par2/par2fileformat.cpp + +par2fileformat.obj: lib/par2/par2fileformat.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2fileformat.obj -MD -MP -MF "$(DEPDIR)/par2fileformat.Tpo" -c -o par2fileformat.obj `if test -f 'lib/par2/par2fileformat.cpp'; then $(CYGPATH_W) 'lib/par2/par2fileformat.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2fileformat.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2fileformat.Tpo" "$(DEPDIR)/par2fileformat.Po"; else rm -f "$(DEPDIR)/par2fileformat.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2fileformat.cpp' object='par2fileformat.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2fileformat.obj `if test -f 'lib/par2/par2fileformat.cpp'; then $(CYGPATH_W) 'lib/par2/par2fileformat.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2fileformat.cpp'; fi` + +par2repairer.o: lib/par2/par2repairer.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2repairer.o -MD -MP -MF "$(DEPDIR)/par2repairer.Tpo" -c -o par2repairer.o `test -f 'lib/par2/par2repairer.cpp' || echo '$(srcdir)/'`lib/par2/par2repairer.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2repairer.Tpo" "$(DEPDIR)/par2repairer.Po"; else rm -f "$(DEPDIR)/par2repairer.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2repairer.cpp' object='par2repairer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2repairer.o `test -f 'lib/par2/par2repairer.cpp' || echo '$(srcdir)/'`lib/par2/par2repairer.cpp + +par2repairer.obj: lib/par2/par2repairer.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2repairer.obj -MD -MP -MF "$(DEPDIR)/par2repairer.Tpo" -c -o par2repairer.obj `if test -f 'lib/par2/par2repairer.cpp'; then $(CYGPATH_W) 'lib/par2/par2repairer.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2repairer.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2repairer.Tpo" "$(DEPDIR)/par2repairer.Po"; else rm -f "$(DEPDIR)/par2repairer.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2repairer.cpp' object='par2repairer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2repairer.obj `if test -f 'lib/par2/par2repairer.cpp'; then $(CYGPATH_W) 'lib/par2/par2repairer.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2repairer.cpp'; fi` + +par2repairersourcefile.o: lib/par2/par2repairersourcefile.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2repairersourcefile.o -MD -MP -MF "$(DEPDIR)/par2repairersourcefile.Tpo" -c -o par2repairersourcefile.o `test -f 'lib/par2/par2repairersourcefile.cpp' || echo '$(srcdir)/'`lib/par2/par2repairersourcefile.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2repairersourcefile.Tpo" "$(DEPDIR)/par2repairersourcefile.Po"; else rm -f "$(DEPDIR)/par2repairersourcefile.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2repairersourcefile.cpp' object='par2repairersourcefile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2repairersourcefile.o `test -f 'lib/par2/par2repairersourcefile.cpp' || echo '$(srcdir)/'`lib/par2/par2repairersourcefile.cpp + +par2repairersourcefile.obj: lib/par2/par2repairersourcefile.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2repairersourcefile.obj -MD -MP -MF "$(DEPDIR)/par2repairersourcefile.Tpo" -c -o par2repairersourcefile.obj `if test -f 'lib/par2/par2repairersourcefile.cpp'; then $(CYGPATH_W) 'lib/par2/par2repairersourcefile.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2repairersourcefile.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2repairersourcefile.Tpo" "$(DEPDIR)/par2repairersourcefile.Po"; else rm -f "$(DEPDIR)/par2repairersourcefile.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2repairersourcefile.cpp' object='par2repairersourcefile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2repairersourcefile.obj `if test -f 'lib/par2/par2repairersourcefile.cpp'; then $(CYGPATH_W) 'lib/par2/par2repairersourcefile.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2repairersourcefile.cpp'; fi` + +parheaders.o: lib/par2/parheaders.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT parheaders.o -MD -MP -MF "$(DEPDIR)/parheaders.Tpo" -c -o parheaders.o `test -f 'lib/par2/parheaders.cpp' || echo '$(srcdir)/'`lib/par2/parheaders.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/parheaders.Tpo" "$(DEPDIR)/parheaders.Po"; else rm -f "$(DEPDIR)/parheaders.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/parheaders.cpp' object='parheaders.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o parheaders.o `test -f 'lib/par2/parheaders.cpp' || echo '$(srcdir)/'`lib/par2/parheaders.cpp + +parheaders.obj: lib/par2/parheaders.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT parheaders.obj -MD -MP -MF "$(DEPDIR)/parheaders.Tpo" -c -o parheaders.obj `if test -f 'lib/par2/parheaders.cpp'; then $(CYGPATH_W) 'lib/par2/parheaders.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/parheaders.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/parheaders.Tpo" "$(DEPDIR)/parheaders.Po"; else rm -f "$(DEPDIR)/parheaders.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/parheaders.cpp' object='parheaders.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o parheaders.obj `if test -f 'lib/par2/parheaders.cpp'; then $(CYGPATH_W) 'lib/par2/parheaders.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/parheaders.cpp'; fi` + +recoverypacket.o: lib/par2/recoverypacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT recoverypacket.o -MD -MP -MF "$(DEPDIR)/recoverypacket.Tpo" -c -o recoverypacket.o `test -f 'lib/par2/recoverypacket.cpp' || echo '$(srcdir)/'`lib/par2/recoverypacket.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/recoverypacket.Tpo" "$(DEPDIR)/recoverypacket.Po"; else rm -f "$(DEPDIR)/recoverypacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/recoverypacket.cpp' object='recoverypacket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o recoverypacket.o `test -f 'lib/par2/recoverypacket.cpp' || echo '$(srcdir)/'`lib/par2/recoverypacket.cpp + +recoverypacket.obj: lib/par2/recoverypacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT recoverypacket.obj -MD -MP -MF "$(DEPDIR)/recoverypacket.Tpo" -c -o recoverypacket.obj `if test -f 'lib/par2/recoverypacket.cpp'; then $(CYGPATH_W) 'lib/par2/recoverypacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/recoverypacket.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/recoverypacket.Tpo" "$(DEPDIR)/recoverypacket.Po"; else rm -f "$(DEPDIR)/recoverypacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/recoverypacket.cpp' object='recoverypacket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o recoverypacket.obj `if test -f 'lib/par2/recoverypacket.cpp'; then $(CYGPATH_W) 'lib/par2/recoverypacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/recoverypacket.cpp'; fi` + +reedsolomon.o: lib/par2/reedsolomon.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT reedsolomon.o -MD -MP -MF "$(DEPDIR)/reedsolomon.Tpo" -c -o reedsolomon.o `test -f 'lib/par2/reedsolomon.cpp' || echo '$(srcdir)/'`lib/par2/reedsolomon.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/reedsolomon.Tpo" "$(DEPDIR)/reedsolomon.Po"; else rm -f "$(DEPDIR)/reedsolomon.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/reedsolomon.cpp' object='reedsolomon.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o reedsolomon.o `test -f 'lib/par2/reedsolomon.cpp' || echo '$(srcdir)/'`lib/par2/reedsolomon.cpp + +reedsolomon.obj: lib/par2/reedsolomon.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT reedsolomon.obj -MD -MP -MF "$(DEPDIR)/reedsolomon.Tpo" -c -o reedsolomon.obj `if test -f 'lib/par2/reedsolomon.cpp'; then $(CYGPATH_W) 'lib/par2/reedsolomon.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/reedsolomon.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/reedsolomon.Tpo" "$(DEPDIR)/reedsolomon.Po"; else rm -f "$(DEPDIR)/reedsolomon.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/reedsolomon.cpp' object='reedsolomon.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o reedsolomon.obj `if test -f 'lib/par2/reedsolomon.cpp'; then $(CYGPATH_W) 'lib/par2/reedsolomon.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/reedsolomon.cpp'; fi` + +verificationhashtable.o: lib/par2/verificationhashtable.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT verificationhashtable.o -MD -MP -MF "$(DEPDIR)/verificationhashtable.Tpo" -c -o verificationhashtable.o `test -f 'lib/par2/verificationhashtable.cpp' || echo '$(srcdir)/'`lib/par2/verificationhashtable.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/verificationhashtable.Tpo" "$(DEPDIR)/verificationhashtable.Po"; else rm -f "$(DEPDIR)/verificationhashtable.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/verificationhashtable.cpp' object='verificationhashtable.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o verificationhashtable.o `test -f 'lib/par2/verificationhashtable.cpp' || echo '$(srcdir)/'`lib/par2/verificationhashtable.cpp + +verificationhashtable.obj: lib/par2/verificationhashtable.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT verificationhashtable.obj -MD -MP -MF "$(DEPDIR)/verificationhashtable.Tpo" -c -o verificationhashtable.obj `if test -f 'lib/par2/verificationhashtable.cpp'; then $(CYGPATH_W) 'lib/par2/verificationhashtable.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/verificationhashtable.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/verificationhashtable.Tpo" "$(DEPDIR)/verificationhashtable.Po"; else rm -f "$(DEPDIR)/verificationhashtable.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/verificationhashtable.cpp' object='verificationhashtable.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o verificationhashtable.obj `if test -f 'lib/par2/verificationhashtable.cpp'; then $(CYGPATH_W) 'lib/par2/verificationhashtable.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/verificationhashtable.cpp'; fi` + +verificationpacket.o: lib/par2/verificationpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT verificationpacket.o -MD -MP -MF "$(DEPDIR)/verificationpacket.Tpo" -c -o verificationpacket.o `test -f 'lib/par2/verificationpacket.cpp' || echo '$(srcdir)/'`lib/par2/verificationpacket.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/verificationpacket.Tpo" "$(DEPDIR)/verificationpacket.Po"; else rm -f "$(DEPDIR)/verificationpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/verificationpacket.cpp' object='verificationpacket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o verificationpacket.o `test -f 'lib/par2/verificationpacket.cpp' || echo '$(srcdir)/'`lib/par2/verificationpacket.cpp + +verificationpacket.obj: lib/par2/verificationpacket.cpp +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT verificationpacket.obj -MD -MP -MF "$(DEPDIR)/verificationpacket.Tpo" -c -o verificationpacket.obj `if test -f 'lib/par2/verificationpacket.cpp'; then $(CYGPATH_W) 'lib/par2/verificationpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/verificationpacket.cpp'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/verificationpacket.Tpo" "$(DEPDIR)/verificationpacket.Po"; else rm -f "$(DEPDIR)/verificationpacket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/verificationpacket.cpp' object='verificationpacket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o verificationpacket.obj `if test -f 'lib/par2/verificationpacket.cpp'; then $(CYGPATH_W) 'lib/par2/verificationpacket.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/verificationpacket.cpp'; fi` uninstall-info-am: install-dist_docDATA: $(dist_doc_DATA) @$(NORMAL_INSTALL) @@ -1435,7 +1830,7 @@ distclean-tags: distdir: $(DISTFILES) $(am__remove_distdir) mkdir $(distdir) - $(mkdir_p) $(distdir)/daemon/windows $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/scripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib + $(mkdir_p) $(distdir)/daemon/windows $(distdir)/lib/par2 $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/scripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ @@ -1756,6 +2151,7 @@ clean-bak: rm *~ dist-hook: find $(distdir)/daemon -type f -print -exec chmod -x {} \; find $(distdir)/webui -type f -print -exec chmod -x {} \; + find $(distdir)/lib -type f -print -exec chmod -x {} \; # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/README b/README index d6c620c33..bcbe2efbe 100644 --- a/README +++ b/README @@ -85,12 +85,7 @@ And the following libraries are optional: - libcurses (usually part of commercial systems) or (better) - libncurses (http://invisible-island.net/ncurses) - - - for par-check and -repair (enabled by default): - - libpar2 (http://launchpad.net/libpar2, - http://parchive.sourceforge.net) - - libsigc++ (http://libsigc.sourceforge.net) - + - for encrypted connections (TLS/SSL): - OpenSSL (http://www.openssl.org) or @@ -152,7 +147,7 @@ You may run configure with additional arguments: if you can not use curses/ncurses. --disable-parcheck - to make without parcheck-support. Use this option - if you can not use libpar2 or libsigc++. + if you have troubles when compiling par2-module. --with-tlslib=(OpenSSL, GnuTLS) - to select which TLS/SSL library should be used for encrypted server connections. @@ -169,34 +164,13 @@ You may run configure with additional arguments: Optional package: par-check --------------------------- NZBGet can check and repair downloaded files for you. For this purpose -it uses library par2 (libpar2), which needs sigc++ on its part. - -The libpar2 and libsigc++ (version 2 or later) must be installed on your -system. On most linux distributions these libraries are available as packages. -If you do not have these packages you can compile them yourself. -Following configure-parameters may be usefull: - - --with-libpar2-includes - --with-libpar2-libraries - --with-libsigc-includes - --with-libsigc-libraries - -The library libsigc++ must be installed first, since libpar2 requires it. - -Official project libpar2 (http://parchive.sourceforge.net) has not been -updated for many years. The last official version of libpar 0.2 contains -known bugs which may crash the program during par-check. These bugs -have been fixed in the semi-official fork maintained by Debian/Ubuntu -team (http://launchpad.net/libpar2). It is highly recommended to use this -version of libpar2 (version 0.4 or newer) instead of the last official -version 0.2. Recent releases of Debian/Ubuntu include this libpar2 version. +it uses library par2. -NZBGets configure script checks the installed version of libpar2 and prints -an error if it is older than 0.4. The check can be suppressed if the update -of libpar2 is not possible. +For your convenience the source code of libpar2 is integrated into +NZBGetā€™s source tree and is compiled automatically when you make NZBGet. -If you are not able to use libpar2 or libsigc++ or do not want them you can -make nzbget without support for par-check using option "--disable-parcheck": +In a case errors occur during this process the inclusion of par2-module +can be disabled using configure option "--disable-parcheck": ./configure --disable-parcheck @@ -204,7 +178,7 @@ Optional package: curses ------------------------- For curses-outputmode you need ncurses or curses on your system. If you do not have one of them you can download and compile ncurses yourself. -Following configure-parameters may be usefull: +Following configure-parameters may be useful: --with-libcurses-includes --with-libcurses-libraries @@ -245,24 +219,15 @@ NZBGet is developed using MS Visual C++ 2005. The project file and solution are provided. If you use MS Visual C++ 2005 Express you need to download and install Platform SDK. -To compile the program with par-check-support you also need the following -libraries: - - - libsigc++ (http://libsigc.sourceforge.net) - - libpar2 (http://launchpad.net/libpar2, - http://parchive.sourceforge.net) - -Download these libaries, then use patch-files provided with NZBGet to create -preconfigured project files and solutions for each library. -Look at http://gnuwin32.sourceforge.net/packages/patch.htm for info on how -to use patch-files, if you do not familiar with this technique. - -To compile the program with TLS/SSL support you also need the library: - +To compile the program with TLS/SSL support you need either OpenSSL or GnuTLS: - OpenSSL (http://www.openssl.org) or - GnuTLS (http://www.gnu.org/software/gnutls) +Also required are: + - Regex (http://gnuwin32.sourceforge.net/packages/regex.htm) + - Zlib (http://gnuwin32.sourceforge.net/packages/zlib.htm) + ===================================== 6. Configuration ===================================== @@ -497,6 +462,32 @@ Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005. In 2007 the abandoned project was overtaken by Andrey Prygunkov. Since then the program has been completely rewritten. +NZBGet distribution archive includes additional components +written by other authors: + +PAR2: + Peter Brian Clements + +PAR2 library API: + Francois Lesueur + +jQuery: + John Resig + The Dojo Foundation + +Bootstrap: + Twitter, Inc + +RaphaĆ«l: + Dmitry Baranovskiy + Sencha Labs + +Elycharts: + Void Labs s.n.c. + +iconSweets: + Yummygum + ===================================== 9. Copyright ===================================== diff --git a/config.h.in b/config.h.in index 9ff23d68c..f1e1196d1 100644 --- a/config.h.in +++ b/config.h.in @@ -13,7 +13,7 @@ /* Define to 1 to disable gzip-support */ #undef DISABLE_GZIP -/* Define to 1 to disable smart par-verification and restoration */ +/* Define to 1 to disable par-verification and repair */ #undef DISABLE_PARCHECK /* Define to 1 to not use TLS/SSL */ @@ -35,6 +35,16 @@ /* Define to 1 if you have the header file. */ #undef HAVE_CURSES_H +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ENDIAN_H + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#undef HAVE_FSEEKO + /* Define to 1 if getaddrinfo is supported */ #undef HAVE_GETADDRINFO @@ -50,6 +60,12 @@ /* Define to 1 if gethostbyname_r takes 6 arguments */ #undef HAVE_GETHOSTBYNAME_R_6 +/* Define to 1 if you have the `getopt' function. */ +#undef HAVE_GETOPT + +/* Define to 1 if you have the header file. */ +#undef HAVE_GETOPT_H + /* Define to 1 if getopt_long is supported */ #undef HAVE_GETOPT_LONG @@ -59,6 +75,9 @@ /* Define to 1 to use GnuTLS library for TLS/SSL-support. */ #undef HAVE_LIBGNUTLS +/* Define to 1 if you have the `memcpy' function. */ +#undef HAVE_MEMCPY + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H @@ -68,33 +87,53 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NCURSES_NCURSES_H +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + /* Define to 1 to use OpenSSL library for TLS/SSL-support. */ #undef HAVE_OPENSSL -/* Define to 1 if libpar2 supports cancelling (needs a special patch) */ -#undef HAVE_PAR2_CANCEL - -/* Define to 1 if libpar2 supports external verification */ -#undef HAVE_PAR2_CRCPATCH - /* Define to 1 if you have the header file. */ #undef HAVE_REGEX_H /* Define to 1 if spinlocks are supported */ #undef HAVE_SPINLOCK +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `stricmp' function. */ +#undef HAVE_STRICMP + /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PRCTL_H @@ -110,6 +149,9 @@ /* Define to 1 if variadic macros are supported */ #undef HAVE_VARIADIC_MACROS +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + /* Name of package */ #undef PACKAGE @@ -140,8 +182,27 @@ /* Version number of package */ #undef VERSION +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +#undef _LARGEFILE_SOURCE + /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/configure b/configure index 4a03a56c6..8d4f9da29 100755 --- a/configure +++ b/configure @@ -709,11 +709,11 @@ EGREP PKG_CONFIG libxml2_CFLAGS libxml2_LIBS -libsigc_CFLAGS -libsigc_LIBS +LIBOBJS +WITH_PAR2_TRUE +WITH_PAR2_FALSE openssl_CFLAGS openssl_LIBS -LIBOBJS LTLIBOBJS' ac_subst_files='' ac_precious_vars='build_alias @@ -729,8 +729,6 @@ CXXCPP PKG_CONFIG libxml2_CFLAGS libxml2_LIBS -libsigc_CFLAGS -libsigc_LIBS openssl_CFLAGS openssl_LIBS' @@ -1318,10 +1316,7 @@ Optional Features: --disable-largefile omit support for large files --disable-curses do not use curses (removes dependency from curses-library) - --disable-parcheck do not include par-check/-repair-support (removes - dependency from libpar2- and libsigc-libraries) - --disable-libpar2-version-check - do not check libpar2 version + --disable-parcheck do not include par-check/-repair-support --disable-tls do not use TLS/SSL (removes dependency from TLS/SSL-libraries) --disable-gzip disable gzip-compression/decompression (removes @@ -1342,14 +1337,6 @@ Optional Packages: libcurses include directory --with-libcurses-libraries=DIR libcurses library directory - --with-libsigc-includes=DIR - libsigc++-2.0 include directory - --with-libsigc-libraries=DIR - libsigc++-2.0 library directory - --with-libpar2-includes=DIR - libpar2 include directory - --with-libpar2-libraries=DIR - libpar2 library directory --with-tlslib=(OpenSSL, GnuTLS) TLS/SSL library to use --with-openssl-includes=DIR @@ -1379,10 +1366,6 @@ Some influential environment variables: C compiler flags for libxml2, overriding pkg-config libxml2_LIBS linker flags for libxml2, overriding pkg-config - libsigc_CFLAGS - C compiler flags for libsigc, overriding pkg-config - libsigc_LIBS - linker flags for libsigc, overriding pkg-config openssl_CFLAGS C compiler flags for openssl, overriding pkg-config openssl_LIBS @@ -7013,159 +6996,33 @@ if test "$ENABLEPARCHECK" = "yes"; then -# Check whether --with-libsigc_includes was given. -if test "${with_libsigc_includes+set}" = set; then - withval=$with_libsigc_includes; CPPFLAGS="${CPPFLAGS} -I${withval}" - INCVAL="yes" -else - INCVAL="no" -fi - - -# Check whether --with-libsigc_libraries was given. -if test "${with_libsigc_libraries+set}" = set; then - withval=$with_libsigc_libraries; LDFLAGS="${LDFLAGS} -L${withval}" - CPPFLAGS="${CPPFLAGS} -I${withval}/sigc++-2.0/include" - LIBVAL="yes" -else - LIBVAL="no" -fi - - if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then - -pkg_failed=no -{ echo "$as_me:$LINENO: checking for libsigc" >&5 -echo $ECHO_N "checking for libsigc... $ECHO_C" >&6; } - -if test -n "$PKG_CONFIG"; then - if test -n "$libsigc_CFLAGS"; then - pkg_cv_libsigc_CFLAGS="$libsigc_CFLAGS" - else - if test -n "$PKG_CONFIG" && \ - { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"sigc++-2.0\"") >&5 - ($PKG_CONFIG --exists --print-errors "sigc++-2.0") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - pkg_cv_libsigc_CFLAGS=`$PKG_CONFIG --cflags "sigc++-2.0" 2>/dev/null` -else - pkg_failed=yes -fi - fi -else - pkg_failed=untried -fi -if test -n "$PKG_CONFIG"; then - if test -n "$libsigc_LIBS"; then - pkg_cv_libsigc_LIBS="$libsigc_LIBS" - else - if test -n "$PKG_CONFIG" && \ - { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"sigc++-2.0\"") >&5 - ($PKG_CONFIG --exists --print-errors "sigc++-2.0") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - pkg_cv_libsigc_LIBS=`$PKG_CONFIG --libs "sigc++-2.0" 2>/dev/null` -else - pkg_failed=yes -fi - fi -else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - libsigc_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "sigc++-2.0"` - else - libsigc_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "sigc++-2.0"` - fi - # Put the nasty error message in config.log where it belongs - echo "$libsigc_PKG_ERRORS" >&5 - - { { echo "$as_me:$LINENO: error: Package requirements (sigc++-2.0) were not met: - -$libsigc_PKG_ERRORS - -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -Alternatively, you may set the environment variables libsigc_CFLAGS -and libsigc_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. -" >&5 -echo "$as_me: error: Package requirements (sigc++-2.0) were not met: - -$libsigc_PKG_ERRORS - -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -Alternatively, you may set the environment variables libsigc_CFLAGS -and libsigc_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. -" >&2;} - { (exit 1); exit 1; }; } -elif test $pkg_failed = untried; then - { { echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -Alternatively, you may set the environment variables libsigc_CFLAGS -and libsigc_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. - -To get pkg-config, see . -See \`config.log' for more details." >&5 -echo "$as_me: error: The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -Alternatively, you may set the environment variables libsigc_CFLAGS -and libsigc_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. -To get pkg-config, see . -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -else - libsigc_CFLAGS=$pkg_cv_libsigc_CFLAGS - libsigc_LIBS=$pkg_cv_libsigc_LIBS - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } - LIBS="${LIBS} $libsigc_LIBS" - CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS" -fi - fi - if test "${ac_cv_header_sigcpp_type_traits_h+set}" = set; then - { echo "$as_me:$LINENO: checking for sigc++/type_traits.h" >&5 -echo $ECHO_N "checking for sigc++/type_traits.h... $ECHO_C" >&6; } -if test "${ac_cv_header_sigcpp_type_traits_h+set}" = set; then +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5 +echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -{ echo "$as_me:$LINENO: result: $ac_cv_header_sigcpp_type_traits_h" >&5 -echo "${ECHO_T}$ac_cv_header_sigcpp_type_traits_h" >&6; } else - # Is the header compilable? -{ echo "$as_me:$LINENO: checking sigc++/type_traits.h usability" >&5 -echo $ECHO_N "checking sigc++/type_traits.h usability... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$ac_includes_default -#include +#include +#include <$ac_hdr> + +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" @@ -7184,148 +7041,157 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then - ac_header_compiler=yes + eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_header_compiler=no + eval "$as_ac_Header=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6; } +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +_ACEOF -# Is the header present? -{ echo "$as_me:$LINENO: checking sigc++/type_traits.h presence" >&5 -echo $ECHO_N "checking sigc++/type_traits.h presence... $ECHO_C" >&6; } +ac_header_dirent=$ac_hdr; break +fi + +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then + { echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6; } +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -#include + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (); +int +main () +{ +return opendir (); + ; + return 0; +} _ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" +for ac_lib in '' dir; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || test ! -s conftest.err - }; then - ac_header_preproc=yes + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_search_opendir=$ac_res else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_header_preproc=no -fi - -rm -f conftest.err conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6; } -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: sigc++/type_traits.h: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: sigc++/type_traits.h: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: sigc++/type_traits.h: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: sigc++/type_traits.h: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: sigc++/type_traits.h: present but cannot be compiled" >&5 -echo "$as_me: WARNING: sigc++/type_traits.h: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: sigc++/type_traits.h: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: sigc++/type_traits.h: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: sigc++/type_traits.h: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: sigc++/type_traits.h: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: sigc++/type_traits.h: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: sigc++/type_traits.h: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: sigc++/type_traits.h: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: sigc++/type_traits.h: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: sigc++/type_traits.h: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: sigc++/type_traits.h: in the future, the compiler will take precedence" >&2;} - ( cat <<\_ASBOX -## ------------------------------------------- ## -## Report this to hugbug@users.sourceforge.net ## -## ------------------------------------------- ## -_ASBOX - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -{ echo "$as_me:$LINENO: checking for sigc++/type_traits.h" >&5 -echo $ECHO_N "checking for sigc++/type_traits.h... $ECHO_C" >&6; } -if test "${ac_cv_header_sigcpp_type_traits_h+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_header_sigcpp_type_traits_h=$ac_header_preproc fi -{ echo "$as_me:$LINENO: result: $ac_cv_header_sigcpp_type_traits_h" >&5 -echo "${ECHO_T}$ac_cv_header_sigcpp_type_traits_h" >&6; } +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_opendir+set}" = set; then + break fi -if test $ac_cv_header_sigcpp_type_traits_h = yes; then +done +if test "${ac_cv_search_opendir+set}" = set; then : else - { { echo "$as_me:$LINENO: error: \"libsigc++-2.0 header files not found\"" >&5 -echo "$as_me: error: \"libsigc++-2.0 header files not found\"" >&2;} - { (exit 1); exit 1; }; } + ac_cv_search_opendir=no fi - - - - INCVAL="${LIBPREF}/include" - LIBVAL="${LIBPREF}/lib" - -# Check whether --with-libpar2_includes was given. -if test "${with_libpar2_includes+set}" = set; then - withval=$with_libpar2_includes; INCVAL="$withval" +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS fi +{ echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6; } +ac_res=$ac_cv_search_opendir +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" +fi - CPPFLAGS="${CPPFLAGS} -I${INCVAL}" - - if test "${ac_cv_header_libpar2_libpar2_h+set}" = set; then - { echo "$as_me:$LINENO: checking for libpar2/libpar2.h" >&5 -echo $ECHO_N "checking for libpar2/libpar2.h... $ECHO_C" >&6; } -if test "${ac_cv_header_libpar2_libpar2_h+set}" = set; then +else + { echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6; } +if test "${ac_cv_search_opendir+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -{ echo "$as_me:$LINENO: result: $ac_cv_header_libpar2_libpar2_h" >&5 -echo "${ECHO_T}$ac_cv_header_libpar2_libpar2_h" >&6; } else - # Is the header compilable? -{ echo "$as_me:$LINENO: checking libpar2/libpar2.h usability" >&5 -echo $ECHO_N "checking libpar2/libpar2.h usability... $ECHO_C" >&6; } + ac_func_search_save_LIBS=$LIBS cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$ac_includes_default -#include + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (); +int +main () +{ +return opendir (); + ; + return 0; +} _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" +for ac_lib in '' x; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 + (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 @@ -7334,130 +7200,1267 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (exit $ac_status); } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_header_compiler=yes + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_search_opendir=$ac_res else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_header_compiler=no + fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6; } +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_opendir+set}" = set; then + break +fi +done +if test "${ac_cv_search_opendir+set}" = set; then + : +else + ac_cv_search_opendir=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6; } +ac_res=$ac_cv_search_opendir +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" -# Is the header present? -{ echo "$as_me:$LINENO: checking libpar2/libpar2.h presence" >&5 -echo $ECHO_N "checking libpar2/libpar2.h presence... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include +fi + +fi + + { echo "$as_me:$LINENO: checking for stdbool.h that conforms to C99" >&5 +echo $ECHO_N "checking for stdbool.h that conforms to C99... $ECHO_C" >&6; } +if test "${ac_cv_header_stdbool_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#ifndef bool + "error: bool is not defined" +#endif +#ifndef false + "error: false is not defined" +#endif +#if false + "error: false is not 0" +#endif +#ifndef true + "error: true is not defined" +#endif +#if true != 1 + "error: true is not 1" +#endif +#ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" +#endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) 0.5 == true ? 1 : -1]; + bool e = &s; + char f[(_Bool) 0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + enum { j = false, k = true, l = false * true, m = true * 256 }; + _Bool n[m]; + char o[sizeof n == m * sizeof n[0] ? 1 : -1]; + char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; +# if defined __xlc__ || defined __GNUC__ + /* Catch a bug in IBM AIX xlc compiler version 6.0.0.0 + reported by James Lemley on 2005-10-05; see + http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html + This test is not quite right, since xlc is allowed to + reject this program, as the initializer for xlcbug is + not one of the forms that C requires support for. + However, doing the test right would require a runtime + test, and that would make cross-compilation harder. + Let us hope that IBM fixes the xlc bug, and also adds + support for this kind of constant expression. In the + meantime, this test will reject xlc, which is OK, since + our stdbool.h substitute should suffice. We also test + this with GCC, where it should work, to detect more + quickly whether someone messes up the test in the + future. */ + char digs[] = "0123456789"; + int xlcbug = 1 / (&(digs + 5)[-2 + (bool) 1] == &digs[4] ? 1 : -1); +# endif + /* Catch a bug in an HP-UX C compiler. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + _Bool q = true; + _Bool *pq = &q; + +int +main () +{ + + *pq |= q; + *pq |= ! q; + /* Refer to every declared value, to avoid compiler optimizations. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + + !m + !n + !o + !p + !q + !pq); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdbool_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdbool_h=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_stdbool_h" >&5 +echo "${ECHO_T}$ac_cv_header_stdbool_h" >&6; } +{ echo "$as_me:$LINENO: checking for _Bool" >&5 +echo $ECHO_N "checking for _Bool... $ECHO_C" >&6; } +if test "${ac_cv_type__Bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +typedef _Bool ac__type_new_; +int +main () +{ +if ((ac__type_new_ *) 0) + return 0; +if (sizeof (ac__type_new_)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type__Bool=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type__Bool=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_type__Bool" >&5 +echo "${ECHO_T}$ac_cv_type__Bool" >&6; } +if test $ac_cv_type__Bool = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + + +fi + +if test $ac_cv_header_stdbool_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STDBOOL_H 1 +_ACEOF + +fi + + { echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + + + + +for ac_header in stdio.h endian.h getopt.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------------- ## +## Report this to hugbug@users.sourceforge.net ## +## ------------------------------------------- ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + { echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6; } +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +typedef size_t ac__type_new_; +int +main () +{ +if ((ac__type_new_ *) 0) + return 0; +if (sizeof (ac__type_new_)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_size_t=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6; } +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + + { echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6; } +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN && defined LITTLE_ENDIAN \ + && BYTE_ORDER && BIG_ENDIAN && LITTLE_ENDIAN) + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_bigendian=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # It does not; compile a test program. +if test "$cross_compiling" = yes; then + # try to guess the endianness by grepping values into an object file + ac_cv_c_bigendian=unknown + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; +short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; +void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } +short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; +short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; +void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } +int +main () +{ + _ascii (); _ebcdic (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then + ac_cv_c_bigendian=yes +fi +if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi +fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6; } +case $ac_cv_c_bigendian in + yes) + +cat >>confdefs.h <<\_ACEOF +#define WORDS_BIGENDIAN 1 _ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" + ;; + no) + ;; + *) + { { echo "$as_me:$LINENO: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&5 +echo "$as_me: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} + { (exit 1); exit 1; }; } ;; +esac + + { echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6; } +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset cs; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || test ! -s conftest.err - }; then - ac_header_preproc=yes + } && test -s conftest.$ac_objext; then + ac_cv_c_const=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_header_preproc=no + ac_cv_c_const=no fi -rm -f conftest.err conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6; } +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: libpar2/libpar2.h: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: libpar2/libpar2.h: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: libpar2/libpar2.h: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: libpar2/libpar2.h: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: libpar2/libpar2.h: present but cannot be compiled" >&5 -echo "$as_me: WARNING: libpar2/libpar2.h: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: libpar2/libpar2.h: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: libpar2/libpar2.h: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: libpar2/libpar2.h: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: libpar2/libpar2.h: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: libpar2/libpar2.h: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: libpar2/libpar2.h: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: libpar2/libpar2.h: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: libpar2/libpar2.h: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: libpar2/libpar2.h: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: libpar2/libpar2.h: in the future, the compiler will take precedence" >&2;} - ( cat <<\_ASBOX -## ------------------------------------------- ## -## Report this to hugbug@users.sourceforge.net ## -## ------------------------------------------- ## -_ASBOX - ) | sed "s/^/$as_me: WARNING: /" >&2 +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + + { echo "$as_me:$LINENO: checking for inline" >&5 +echo $ECHO_N "checking for inline... $ECHO_C" >&6; } +if test "${ac_cv_c_inline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_inline=$ac_kw +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 +echo "${ECHO_T}$ac_cv_c_inline" >&6; } + + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF ;; esac -{ echo "$as_me:$LINENO: checking for libpar2/libpar2.h" >&5 -echo $ECHO_N "checking for libpar2/libpar2.h... $ECHO_C" >&6; } -if test "${ac_cv_header_libpar2_libpar2_h+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + + { echo "$as_me:$LINENO: checking for _LARGEFILE_SOURCE value needed for large files" >&5 +echo $ECHO_N "checking for _LARGEFILE_SOURCE value needed for large files... $ECHO_C" >&6; } +if test "${ac_cv_sys_largefile_source+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include /* for off_t */ + #include +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_sys_largefile_source=no; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _LARGEFILE_SOURCE 1 +#include /* for off_t */ + #include +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_sys_largefile_source=1; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + ac_cv_sys_largefile_source=unknown + break +done +fi +{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_source" >&5 +echo "${ECHO_T}$ac_cv_sys_largefile_source" >&6; } +case $ac_cv_sys_largefile_source in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source +_ACEOF +;; +esac +rm -f conftest* + +# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug +# in glibc 2.1.3, but that breaks too many other things. +# If you want fseeko and ftello with glibc, upgrade to a fixed glibc. +if test $ac_cv_sys_largefile_source != unknown; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_FSEEKO 1 +_ACEOF + +fi + + { echo "$as_me:$LINENO: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6; } +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = '\100', c1 = '\200', c2 = '\201'; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + return 1; + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + return 1; + } + return 0; + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes else - ac_cv_header_libpar2_libpar2_h=$ac_header_preproc -fi -{ echo "$as_me:$LINENO: result: $ac_cv_header_libpar2_libpar2_h" >&5 -echo "${ECHO_T}$ac_cv_header_libpar2_libpar2_h" >&6; } + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 +( exit $ac_status ) +ac_cv_func_memcmp_working=no fi -if test $ac_cv_header_libpar2_libpar2_h = yes; then - : -else - { { echo "$as_me:$LINENO: error: \"libpar2 header files not found\"" >&5 -echo "$as_me: error: \"libpar2 header files not found\"" >&2;} - { (exit 1); exit 1; }; } +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi - - -# Check whether --with-libpar2_libraries was given. -if test "${with_libpar2_libraries+set}" = set; then - withval=$with_libpar2_libraries; LIBVAL="$withval" fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6; } +test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" + ;; +esac + - LDFLAGS="${LDFLAGS} -L${LIBVAL}" - { echo "$as_me:$LINENO: checking for library containing _ZN12Par2RepairerC1Ev" >&5 -echo $ECHO_N "checking for library containing _ZN12Par2RepairerC1Ev... $ECHO_C" >&6; } -if test "${ac_cv_search__ZN12Par2RepairerC1Ev+set}" = set; then +for ac_func in stricmp strcasecmp +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then echo $ECHO_N "(cached) $ECHO_C" >&6 else - ac_func_search_save_LIBS=$LIBS -cat >conftest.$ac_ext <<_ACEOF + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC @@ -7465,23 +8468,23 @@ cat >>conftest.$ac_ext <<_ACEOF #ifdef __cplusplus extern "C" #endif -char _ZN12Par2RepairerC1Ev (); +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + int main () { -return _ZN12Par2RepairerC1Ev (); +return $ac_func (); ; return 0; } _ACEOF -for ac_lib in '' par2; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - rm -f conftest.$ac_objext conftest$ac_exeext +rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; @@ -7499,56 +8502,79 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then - ac_cv_search__ZN12Par2RepairerC1Ev=$ac_res + eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - + eval "$as_ac_var=no" fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext - if test "${ac_cv_search__ZN12Par2RepairerC1Ev+set}" = set; then - break -fi -done -if test "${ac_cv_search__ZN12Par2RepairerC1Ev+set}" = set; then - : -else - ac_cv_search__ZN12Par2RepairerC1Ev=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS + conftest$ac_exeext conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_search__ZN12Par2RepairerC1Ev" >&5 -echo "${ECHO_T}$ac_cv_search__ZN12Par2RepairerC1Ev" >&6; } -ac_res=$ac_cv_search__ZN12Par2RepairerC1Ev -if test "$ac_res" != no; then - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF -else - { { echo "$as_me:$LINENO: error: \"libpar2 library not found\"" >&5 -echo "$as_me: error: \"libpar2 library not found\"" >&2;} - { (exit 1); exit 1; }; } fi +done - { echo "$as_me:$LINENO: checking for libpar2 linking" >&5 -echo $ECHO_N "checking for libpar2 linking... $ECHO_C" >&6; } - cat >conftest.$ac_ext <<_ACEOF + +for ac_func in strchr memcpy +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -#include - #include - class Repairer : public Par2Repairer { }; +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + int main () { - Repairer* p = new Repairer(); +return $ac_func (); ; return 0; } @@ -7571,104 +8597,90 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && $as_test_x conftest$ac_exeext; then - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } + eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } - { { echo "$as_me:$LINENO: error: \"libpar2 library not found\"" >&5 -echo "$as_me: error: \"libpar2 library not found\"" >&2;} - { (exit 1); exit 1; }; } + eval "$as_ac_var=no" fi rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF - { echo "$as_me:$LINENO: checking whether libpar2 supports cancelling" >&5 -echo $ECHO_N "checking whether libpar2 supports cancelling... $ECHO_C" >&6; } - cat >conftest.$ac_ext <<_ACEOF +fi +done + + +for ac_func in getopt +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -#include - #include - class Repairer : public Par2Repairer { void test() { cancelled = true; } }; -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func -cat >>confdefs.h <<\_ACEOF -#define HAVE_PAR2_CANCEL 1 -_ACEOF +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +#ifdef __STDC__ +# include +#else +# include +#endif - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi +#undef $ac_func -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif - { echo "$as_me:$LINENO: checking whether libpar2 supports external verification" >&5 -echo $ECHO_N "checking whether libpar2 supports external verification... $ECHO_C" >&6; } - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - #include - class Repairer : public Par2Repairer { protected: - virtual int ScanDataFile(DiskFile *diskfile, Par2RepairerSourceFile* &sourcefile, - MatchType &matchtype, MD5Hash &hashfull, MD5Hash &hash16k, u32 &count) { return true; } }; int main () { - +return $ac_func (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 + (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 @@ -7677,40 +8689,39 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (exit $ac_status); } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err - } && test -s conftest.$ac_objext; then - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } - PAR2CRCPATCH=no - -cat >>confdefs.h <<\_ACEOF -#define HAVE_PAR2_CRCPATCH 1 -_ACEOF - + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } - PAR2CRCPATCH=yes + eval "$as_ac_var=no" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF - if test "$PAR2CRCPATCH" = "no" ; then - # Check whether --enable-libpar2-version-check was given. -if test "${enable_libpar2_version_check+set}" = set; then - enableval=$enable_libpar2_version_check; PAR2PATCHCHECK=$enableval -else - PAR2PATCHCHECK=yes fi +done + - if test "$PAR2PATCHCHECK" = "yes"; then - { { echo "$as_me:$LINENO: error: Your version of libpar2 doesn't support external verification, which drastically improves verification time in nzbget. Please update your libpar2 as explained on http://nzbget.net/libpar2. If you cannot update libpar2, you can use configure parameter --disable-libpar2-version-check to suppress the check." >&5 -echo "$as_me: error: Your version of libpar2 doesn't support external verification, which drastically improves verification time in nzbget. Please update your libpar2 as explained on http://nzbget.net/libpar2. If you cannot update libpar2, you can use configure parameter --disable-libpar2-version-check to suppress the check." >&2;} - { (exit 1); exit 1; }; } - fi - fi + +if true; then + WITH_PAR2_TRUE= + WITH_PAR2_FALSE='#' +else + WITH_PAR2_TRUE='#' + WITH_PAR2_FALSE= +fi else @@ -7718,6 +8729,16 @@ cat >>confdefs.h <<\_ACEOF #define DISABLE_PARCHECK 1 _ACEOF + + +if false; then + WITH_PAR2_TRUE= + WITH_PAR2_FALSE='#' +else + WITH_PAR2_TRUE='#' + WITH_PAR2_FALSE= +fi + fi @@ -9216,6 +10237,20 @@ echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." >&2;} { (exit 1); exit 1; }; } fi +if test -z "${WITH_PAR2_TRUE}" && test -z "${WITH_PAR2_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"WITH_PAR2\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"WITH_PAR2\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${WITH_PAR2_TRUE}" && test -z "${WITH_PAR2_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"WITH_PAR2\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"WITH_PAR2\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files @@ -9841,9 +10876,9 @@ EGREP!$EGREP$ac_delim PKG_CONFIG!$PKG_CONFIG$ac_delim libxml2_CFLAGS!$libxml2_CFLAGS$ac_delim libxml2_LIBS!$libxml2_LIBS$ac_delim -libsigc_CFLAGS!$libsigc_CFLAGS$ac_delim -libsigc_LIBS!$libsigc_LIBS$ac_delim -openssl_CFLAGS!$openssl_CFLAGS$ac_delim +LIBOBJS!$LIBOBJS$ac_delim +WITH_PAR2_TRUE!$WITH_PAR2_TRUE$ac_delim +WITH_PAR2_FALSE!$WITH_PAR2_FALSE$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then @@ -9885,8 +10920,8 @@ _ACEOF ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF +openssl_CFLAGS!$openssl_CFLAGS$ac_delim openssl_LIBS!$openssl_LIBS$ac_delim -LIBOBJS!$LIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF diff --git a/configure.ac b/configure.ac index e7c7d048f..3a5f02e73 100644 --- a/configure.ac +++ b/configure.ac @@ -1,3 +1,24 @@ +# +# This file is part of nzbget +# +# Copyright (C) 2008-2014 Andrey Prygunkov +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. @@ -264,118 +285,37 @@ fi dnl -dnl Use libpar2 for par-checking. Deafult: no +dnl Use par-checking. Deafult: yes. dnl AC_MSG_CHECKING(whether to include code for par-checking) AC_ARG_ENABLE(parcheck, - [AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support (removes dependency from libpar2- and libsigc-libraries)])], + [AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support])], [ ENABLEPARCHECK=$enableval ], [ ENABLEPARCHECK=yes] ) AC_MSG_RESULT($ENABLEPARCHECK) if test "$ENABLEPARCHECK" = "yes"; then - + dnl PAR2 checks. dnl - dnl checks for libsigc++ includes and libraries (required for libpar2). - dnl - - AC_ARG_WITH(libsigc_includes, - [AS_HELP_STRING([--with-libsigc-includes=DIR], [libsigc++-2.0 include directory])], - [CPPFLAGS="${CPPFLAGS} -I${withval}"] - [INCVAL="yes"], - [INCVAL="no"]) - AC_ARG_WITH(libsigc_libraries, - [AS_HELP_STRING([--with-libsigc-libraries=DIR], [libsigc++-2.0 library directory])], - [LDFLAGS="${LDFLAGS} -L${withval}"] - [CPPFLAGS="${CPPFLAGS} -I${withval}/sigc++-2.0/include"] - [LIBVAL="yes"], - [LIBVAL="no"]) - if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then - PKG_CHECK_MODULES(libsigc, sigc++-2.0, - [LIBS="${LIBS} $libsigc_LIBS"] - [CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"]) - fi - - AC_CHECK_HEADER(sigc++/type_traits.h,, - AC_MSG_ERROR("libsigc++-2.0 header files not found")) - - dnl - dnl checks for libpar2 includes and libraries. - dnl - INCVAL="${LIBPREF}/include" - LIBVAL="${LIBPREF}/lib" - AC_ARG_WITH(libpar2_includes, - [AS_HELP_STRING([--with-libpar2-includes=DIR], [libpar2 include directory])], - [INCVAL="$withval"]) - - CPPFLAGS="${CPPFLAGS} -I${INCVAL}" - - AC_CHECK_HEADER(libpar2/libpar2.h,, - AC_MSG_ERROR("libpar2 header files not found")) - - AC_ARG_WITH(libpar2_libraries, - [AS_HELP_STRING([--with-libpar2-libraries=DIR], [libpar2 library directory])], - [LIBVAL="$withval"]) - - LDFLAGS="${LDFLAGS} -L${LIBVAL}" - - AC_SEARCH_LIBS([_ZN12Par2RepairerC1Ev], [par2], , - AC_MSG_ERROR("libpar2 library not found")) - - dnl - dnl check if libpar2 library is linkable - dnl - AC_MSG_CHECKING(for libpar2 linking) - AC_TRY_LINK( - [#include ] - [#include ] - [ class Repairer : public Par2Repairer { }; ], - [ Repairer* p = new Repairer(); ], - AC_MSG_RESULT([[yes]]), - AC_MSG_RESULT([[no]]) - AC_MSG_ERROR("libpar2 library not found")) - - dnl - dnl check if libpar2 has support for cancelling - dnl - AC_MSG_CHECKING(whether libpar2 supports cancelling) - AC_TRY_COMPILE( - [#include ] - [#include ] - [ class Repairer : public Par2Repairer { void test() { cancelled = true; } }; ], - [], - AC_MSG_RESULT([[yes]]) - AC_DEFINE([HAVE_PAR2_CANCEL], 1, [Define to 1 if libpar2 supports cancelling (needs a special patch)]), - AC_MSG_RESULT([[no]])) - - dnl - dnl check if libpar2 has external-verify-patch - dnl - AC_MSG_CHECKING(whether libpar2 supports external verification) - AC_TRY_COMPILE( - [#include ] - [#include ] - [ class Repairer : public Par2Repairer { protected: - virtual int ScanDataFile(DiskFile *diskfile, Par2RepairerSourceFile* &sourcefile, - MatchType &matchtype, MD5Hash &hashfull, MD5Hash &hash16k, u32 &count) { return true; } }; ], - [], - AC_MSG_RESULT([[no]]) - PAR2CRCPATCH=no - AC_DEFINE([HAVE_PAR2_CRCPATCH], 1, [Define to 1 if libpar2 supports external verification]), - AC_MSG_RESULT([[yes]]) - PAR2CRCPATCH=yes) - - if test "$PAR2CRCPATCH" = "no" ; then - AC_ARG_ENABLE(libpar2-version-check, - [AS_HELP_STRING([--disable-libpar2-version-check], [do not check libpar2 version])], - [ PAR2PATCHCHECK=$enableval ], - [ PAR2PATCHCHECK=yes] ) - if test "$PAR2PATCHCHECK" = "yes"; then - AC_ERROR([Your version of libpar2 doesn't support external verification, which drastically improves verification time in nzbget. Please update your libpar2 as explained on http://nzbget.net/libpar2. If you cannot update libpar2, you can use configure parameter --disable-libpar2-version-check to suppress the check.]) - fi - fi - + dnl Checks for header files. + AC_HEADER_DIRENT + AC_HEADER_STDBOOL + AC_HEADER_STDC + AC_CHECK_HEADERS([stdio.h] [endian.h] [getopt.h]) + dnl Checks for typedefs, structures, and compiler characteristics. + AC_TYPE_SIZE_T + AC_C_BIGENDIAN + AC_C_CONST + AC_C_INLINE + AC_FUNC_FSEEKO + dnl Checks for library functions. + AC_FUNC_MEMCMP + AC_CHECK_FUNCS([stricmp] [strcasecmp]) + AC_CHECK_FUNCS([strchr] [memcpy]) + AC_CHECK_FUNCS([getopt]) + AM_CONDITIONAL(WITH_PAR2, true) else - AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration]) + AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable par-verification and repair]) + AM_CONDITIONAL(WITH_PAR2, false) fi diff --git a/daemon/postprocess/ParChecker.cpp b/daemon/postprocess/ParChecker.cpp index ee3902b19..e12262874 100644 --- a/daemon/postprocess/ParChecker.cpp +++ b/daemon/postprocess/ParChecker.cpp @@ -37,16 +37,14 @@ #include #include #include -#ifdef WIN32 -#include -#include -#else +#ifndef WIN32 #include -#include -#include #endif #include +#include "par2cmdline.h" +#include "par2repairer.h" + #include "nzbget.h" #include "ParChecker.h" #include "ParCoordinator.h" @@ -74,6 +72,10 @@ class Repairer : public Par2Repairer ParChecker* m_pOwner; protected: + virtual void sig_filename(std::string filename) { m_pOwner->signal_filename(filename); } + virtual void sig_progress(double progress) { m_pOwner->signal_progress(progress); } + virtual void sig_done(std::string filename, int available, int total) { m_pOwner->signal_done(filename, available, total); } + virtual bool ScanDataFile(DiskFile *diskfile, Par2RepairerSourceFile* &sourcefile, MatchType &matchtype, MD5Hash &hashfull, MD5Hash &hash16k, u32 &count); @@ -132,14 +134,14 @@ bool Repairer::ScanDataFile(DiskFile *diskfile, Par2RepairerSourceFile* &sourcef string name; DiskFile::SplitFilename(diskfile->FileName(), path, name); - sig_filename.emit(name); + sig_filename(name); int iAvailableBlocks = sourcefile->BlockCount(); ParChecker::EFileStatus eFileStatus = m_pOwner->VerifyDataFile(diskfile, sourcefile, &iAvailableBlocks); if (eFileStatus != ParChecker::fsUnknown) { - sig_done.emit(name, iAvailableBlocks, sourcefile->BlockCount()); - sig_progress.emit(1000.0); + sig_done(name, iAvailableBlocks, sourcefile->BlockCount()); + sig_progress(1000.0); matchtype = eFileStatus == ParChecker::fsSuccess ? eFullMatch : ParChecker::fsPartial ? ePartialMatch : eNoMatch; return true; } @@ -470,9 +472,6 @@ int ParChecker::PreProcessPar() Repairer* pRepairer = new Repairer(this); m_pRepairer = pRepairer; - pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename)); - pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress)); - pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done)); res = pRepairer->PreProcess(m_szParFilename); debug("ParChecker: PreProcess-result=%i", res); @@ -974,13 +973,9 @@ void ParChecker::signal_done(std::string str, int available, int total) void ParChecker::Cancel() { -#ifdef HAVE_PAR2_CANCEL ((Repairer*)m_pRepairer)->cancelled = true; m_bCancelled = true; QueueChanged(); -#else - PrintMessage(Message::mkError, "Could not cancel par-repair. The program was compiled using version of libpar2 which doesn't support cancelling of par-repair. Please apply libpar2-patches supplied with NZBGet and recompile libpar2 and NZBGet (see README for details)."); -#endif } void ParChecker::WriteBrokenLog(EStatus eStatus) diff --git a/daemon/postprocess/ParCoordinator.cpp b/daemon/postprocess/ParCoordinator.cpp index d6372a500..1f3259f07 100644 --- a/daemon/postprocess/ParCoordinator.cpp +++ b/daemon/postprocess/ParCoordinator.cpp @@ -387,16 +387,12 @@ bool ParCoordinator::Cancel() { if (m_eCurrentJob == jkParCheck) { -#ifdef HAVE_PAR2_CANCEL if (!m_ParChecker.GetCancelled()) { debug("Cancelling par-repair for %s", m_ParChecker.GetInfoName()); m_ParChecker.Cancel(); return true; } -#else - warn("Cannot cancel par-repair for %s, used version of libpar2 does not support cancelling", m_ParChecker.GetInfoName()); -#endif } else if (m_eCurrentJob == jkParRename) { @@ -681,7 +677,6 @@ void ParCoordinator::UpdateParCheckProgress() } bool bParCancel = false; -#ifdef HAVE_PAR2_CANCEL if (!m_ParChecker.GetCancelled()) { if ((g_pOptions->GetParTimeLimit() > 0) && @@ -700,7 +695,6 @@ void ParCoordinator::UpdateParCheckProgress() } } } -#endif if (bParCancel) { diff --git a/daemon/postprocess/ParRenamer.cpp b/daemon/postprocess/ParRenamer.cpp index b59457236..eb2d6b10f 100644 --- a/daemon/postprocess/ParRenamer.cpp +++ b/daemon/postprocess/ParRenamer.cpp @@ -37,17 +37,14 @@ #include #include #include -#ifdef WIN32 -#include -#include -#include -#else +#ifndef WIN32 #include -#include -#include -#include #endif +#include "par2cmdline.h" +#include "par2repairer.h" +#include "md5.h" + #include "nzbget.h" #include "ParRenamer.h" #include "ParCoordinator.h" diff --git a/lib/par2/AUTHORS b/lib/par2/AUTHORS new file mode 100644 index 000000000..39eec7b1d --- /dev/null +++ b/lib/par2/AUTHORS @@ -0,0 +1,3 @@ +par2cmdline 0.4: Peter Brian Clements +library API: Francois Lesueur +changes for NZBGet: Andrey Prygunkov \ No newline at end of file diff --git a/lib/par2/README b/lib/par2/README new file mode 100644 index 000000000..3d7709814 --- /dev/null +++ b/lib/par2/README @@ -0,0 +1,326 @@ +Based on libpar2 v 0.4 (http://launchpad.net/libpar2), which +is a Debian/Ubuntu fork of original libpar2 (http://parchive.sourceforge.net), +which is a library for par2 based on par2cmdline 0.4. + +Changes made to libpar2-0.4: + - removed files not required for NZBGet; + - eliminated dependency from libsigc++ by changing of library + API to use virtual functions instead of sigc++ signals. + +par2cmdline README follows : + +------------------------------------------------------------- + +par2cmdline is a PAR 2.0 compatible file verification and repair tool. + +See http://parchive.sourceforge.net for details of PAR 2.0 specification +and discussion of all things PAR. + +WHAT EXACTLY IS PAR2CMDLINE? + +par2cmdline is a program for creating and using PAR2 files to detect +damage in data files and repair them if necessary. It can be used with +any kind of file. + +WHY IS PAR 2.0 better than PAR 1.0? + + * It is not necessary to split a single large file into many equally + size small files (although you can still do so if you wish). + + * There is no loss of efficiency when operating on multiple files + of different sizes. + + * It is possible to repair damaged files (using exactly the amount of + recovery data that corresponds to the amount of damage), rather than + requiring the complete reconstruction of the damaged file. + + * Recovery files may be of different sizes making it possible to + obtain exactly the amount of recovery data required to carry out + a repair. + + * Because damaged data files are still useable during the recovery + process, less recovery data is required to achieve a successfull + repair. It is not therefore necessary to create as much recovery + data in the first place to achieve the same level of protection. + + * You can protect up to 32768 files rather than the 256 that PAR 1.0 + is limited to. + + * Damaged or incomplete recovery files can also be used during the + recovery process in the same way that damaged data files can. + + * You require less recovery data to provide the same level of protection + from damage compared with PAR 1.0. + +DOES PAR 2.0 HAVE ANY DISSADVANTAGES? + +Yes, there is one dissadvantage: + + * All PAR 2.0 program will take somewhat longer to create recovery + files than a PAR 1.0 program does. + +This dissadvantage is considerably mitigated by the fact that you don't +need to create as much recovery data in the first place to provide the +same level of protection against loss and damage. + +COMPILING PAR2CMDLINE + +You should have received par2cmdline in the form of source code which +you can compile on your computer. You may optionally have received a +pre-compiled version of the program for your operating system. + +If you have only downloaded a precompiled executable, then the source +code should be available from the same location that you downloaded the +executable from. + +If you have MS Visual Studio .NET, then just open the par2cmdline.sln +file and compile. You should then copy par2cmdline.exe to an appropriate +location that is on your path. + +To compile on Linux and other unix variants use the following commands: + + ./configure + make + make check + make install + +See INSTALL for full details of how to use the "configure" script. + +USING PAR2CMDLINE + +The command line parameters for par2cmdline are as follow: + + par2 c(reate) [options] [files] + par2 v(erify) [options] [files] + par2 r(epair) [options] [files] + + Also: + + par2create [options] [files] + par2verify [options] [files] + par2repair [options] [files] + + Options: + + -b : Set the Block-Count + -s : Set the Block-Size (Don't use both -b and -s) + -r : Level of Redundancy (%) + -c : Recovery block count (don't use both -r and -c) + -f : First Recovery-Block-Number + -u : Uniform recovery file sizes + -l : Limit size of recovery files (Don't use both -u and -l) + -n : Number of recovery files (Don't use both -n and -l) + -m : Memory (in MB) to use + -v [-v]: Be more verbose + -q [-q]: Be more quiet (-qq gives silence) + -- : Treat all remaining CommandLine as filenames + +If you wish to create par2 files for a single source file, you may leave +out the name of the par2 file from the command line. par2cmdline will then +assume that you wish to base the filenames for the par2 files on the name +of the source file. + +You may also leave off the .par2 file extension when verifying and repairing. + +CREATING PAR2 FILES + +With PAR 2.0 you can create PAR2 recovery files for as few as 1 or as many as +32768 files. If you wanted to create PAR1 recovery files for a single file +you are forced to split the file into muliple parts and RAR is frequently +used for this purpose. You do NOT need to split files with PAR 2.0. + +To create PAR 2 recovery files for a single data file (e.g. one called +test.mpg), you can use the following command: + + par2 create test.mpg + +If test.mpg is an 800 MB file, then this will create a total of 8 PAR2 files +with the following filenames (taking roughly 6 minutes on a PC with a +1500MHz CPU): + + test.mpg.par2 - This is an index file for verification only + test.mpg.vol00+01.par2 - Recovery file with 1 recovery block + test.mpg.vol01+02.par2 - Recovery file with 2 recovery blocks + test.mpg.vol03+04.par2 - Recovery file with 4 recovery blocks + test.mpg.vol07+08.par2 - Recovery file with 8 recovery blocks + test.mpg.vol15+16.par2 - Recovery file with 16 recovery blocks + test.mpg.vol31+32.par2 - Recovery file with 32 recovery blocks + test.mpg.vol63+37.par2 - Recovery file with 37 recovery blocks + +The test.mpg.par2 file is 39 KB in size and the other files vary in size from +443 KB to 15 MB. + +These par2 files will enable the recovery of up to 100 errors totalling 40 MB +of lost or damaged data from the original test.mpg file when it and the par2 +files are posted on UseNet. + +When posting on UseNet it is recommended that you use the "-s" option to set +a blocksize that is equal to the Article size that you will use to post the +data file. If you wanted to post the test.mpg file using an article size +of 300 KB then the command you would type is: + + par2 create -s307200 test.mpg + +This will create 9 PAR2 files instead of 8, and they will be capable of +correcting up to 134 errors totalling 40 MB. It will take roughly 8 minutes +to create the recovery files this time. + +In both of these two examples, the total quantity of recovery data created +was 40 MB (which is 5% of 800 MB). If you wish to create a greater or lesser +quantity of recovery data, you can use the "-r" option. + +To create 10% recovery data instead of the default of 5% and also to use a +block size of 300 KB, you would use the following command: + + par2 create -s307200 -r10 test.mpg + +This would also create 9 PAR2 files, but they would be able to correct up to +269 errors totalling 80 MB. Since twice as much recovery data is created, it +will take about 16 minutes to do so with a 1500MHz CPU. + +The "-u" and "-n" options can be used to control exactly how many recovery +files are created and how the recovery blocks are distributed amoungst them. +They do not affect the total quantity of recovery data created. + +The "-f" option is used when you create additional recovery data. + +e.g. If you have already created 10% and want another 5% then you migh use +the following command: + + par2 create -s307200 -r5 -f300 test.mpg + +This specifies the same block size (which is a requirement for additional +recovery files), 5% recovery data, and a first block number of 300. + +The "-m" option controls how much memory par2cmdline uses. It defaults to +16 MB unless you override it. + +CREATING PAR2 FILES FOR MULTIPLE DATA FILES + +When creating PAR2 recovery files form multiple data files, you must specify +the base filename to use for the par2 files and the names of all of the data +files. + +If test.mpg had been split into multiple RAR files, then you could use: + + par2 create test.mpg.rar.par2 test.mpg.part*.rar + +The files filename "test.mpg.rar.par2" says what you want the par2 files to +be called and "test.mpg.part*.rar" should select all of the RAR files. + +VERIFYING AND REPAIRING + +When using par2 recovery files to verify or repair the data files from +which they were created, you only need to specify the filename of one +of the par2 files to par2cmdline. + +e.g.: + + par2 verify test.mpg.par2 + +This tells par2cmdline to use the information in test.mpg.par2 to verify the +data files. + +par2cmdline will automatically search for the other par2 files that were +created and use the information they contain to determine the filenames +of the original data files and then to verify them. + +If all of the data files are ok, then par2cmdline will report that repair +will not be required. + +If any of the data files are missing or damaged, par2cmdline will report +the details of what it has found. If the recovery files contain enough +recovery blocks to repair the damage, you will be told that repair is +possible. Otherwise you will be told exactly how many recovery blocks +will be required in order to repair. + +To carry out a repair use the following command: + + par2 repair test.mpg.par2 + +This tells par2cmdline to verify and if possible repair any damaged or +missing files. If a repair is carried out, then each file which is +repaired will be re-verified to confirm that the repair was successful. + +MISSNAMED AND INCOMPLETE DATA FILES + +If any of the recovery files or data files have the wrong filename, then +par2cmdline will not automatically find and scan them. + +To have par2cmdline scan such files, you must include them on the command +line when attempting to verify or repair. + +e.g.: + + par2 r test.mpg.par2 other.mpg + +This tells par2cmdline to scan the file called other.mpg to see if it +contains any data belonging to the original data files. + +If one of the extra files specified in this way is an exact match +for a data file, then the repair process will rename the file so that +it has the correct filename. + +Because par2cmdline is designed to be able to find good data within a +damaged file, it can do the same with incomplete files downloaded from +UseNet. If some of the articles for a file are missing, you should still +download the file and save it to disk for par2cmdline to scan. If you +do this then you may find that you can carry out a repair in a situation +where you would not otherwise have sufficient recovery data. + +You can have par2cmdline scan all files that are in the current directory +using a command such as: + + par2 r test.mpg.par2 * + +WHAT TO DO WHEN YOU ARE TOLD YOU NEED MORE RECOVERY BLOCKS + +If par2cmdline determines that any of the data files are damaged or +missing and finds that there is insufficient recovery data to effect +a repair, you will be told that you need a certain number of recovery +blocks. You can obtain these by downloading additional recovery files. + +In order to make things easy, par2 files have filenames that tell you +exactly how many recovery blocks each one contains. + +Assuming that the following command was used to create recovery data: + + par2 c -b1000 -r5 test.mpg + +Then the recovery files that are created would be called: + + test.mpg.par2 + test.mpg.vol00+01.par2 + test.mpg.vol01+02.par2 + test.mpg.vol03+04.par2 + test.mpg.vol07+08.par2 + test.mpg.vol15+16.par2 + test.mpg.vol31+19.par2 + +The first file in this list does not contain any recovery data, it only +contains information sufficient to verify the data files. + +Each of the other files contains a different number of recovery blocks. +The number after the '+' sign is the number of recovery blocks and the +number preceding the '+' sign is the block number of the first recovery +block in that file. + +If par2cmdline told you that you needed 10 recovery blocks, then you would +need "test.mpg.vol01+02.par2" and "test.mpg.vol07+08.par". You might of course +choose to fetch "test.mpg.vol15+16.par2" instead (in which case you would have +an extra 6 recovery blocks which would not be used for the repair). + +NOTES + +This version of par2cmdline does not support recording path information for +files. Whilst you can create recovery files for files from multiple locations, +it will expect all files to be in the current directory when verifying and +repairing. This limitation will be corrected in an update. + +REED SOLOMON CODING + +PAR2 uses Reed Solomon Coding to perform its calculations. For details of this +coding technique try the following link: + +``A Tutorial on Reed-Solomon Coding for Fault-Tolerance in RAID-like Systems'' + diff --git a/lib/par2/commandline.cpp b/lib/par2/commandline.cpp new file mode 100644 index 000000000..aafe4894d --- /dev/null +++ b/lib/par2/commandline.cpp @@ -0,0 +1,827 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +CommandLine::ExtraFile::ExtraFile(void) +: filename() +, filesize(0) +{ +} + +CommandLine::ExtraFile::ExtraFile(const CommandLine::ExtraFile &other) +: filename(other.filename) +, filesize(other.filesize) +{ +} + +CommandLine::ExtraFile& CommandLine::ExtraFile::operator=(const CommandLine::ExtraFile &other) +{ + filename = other.filename; + filesize = other.filesize; + + return *this; +} + +CommandLine::ExtraFile::ExtraFile(const string &name, u64 size) +: filename(name) +, filesize(size) +{ +} + + +CommandLine::CommandLine(void) +: operation(opNone) +, version(verUnknown) +, noiselevel(nlUnknown) +, blockcount(0) +, blocksize(0) +, firstblock(0) +, recoveryfilescheme(scUnknown) +, recoveryfilecount(0) +, recoveryblockcount(0) +, recoveryblockcountset(false) +, redundancy(0) +, redundancyset(false) +, parfilename() +, extrafiles() +, totalsourcesize(0) +, largestsourcesize(0) +, memorylimit(0) +{ +} + +void CommandLine::usage(void) +{ + cout << + "\n" + "Usage:\n" + "\n" + " par2 c(reate) [options] [files] : Create PAR2 files\n" + " par2 v(erify) [options] [files] : Verify files using PAR2 file\n" + " par2 r(epair) [options] [files] : Repair files using PAR2 files\n" + "\n" + "You may also leave out the \"c\", \"v\", and \"r\" commands by using \"parcreate\",\n" + "\"par2verify\", or \"par2repair\" instead.\n" + "\n" + "Options:\n" + "\n" + " -b : Set the Block-Count\n" + " -s : Set the Block-Size (Don't use both -b and -s)\n" + " -r : Level of Redundancy (%%)\n" + " -c : Recovery block count (Don't use both -r and -c)\n" + " -f : First Recovery-Block-Number\n" + " -u : Uniform recovery file sizes\n" + " -l : Limit size of recovery files (Don't use both -u and -l)\n" + " -n : Number of recovery files (Don't use both -n and -l)\n" + " -m : Memory (in MB) to use\n" + " -v [-v]: Be more verbose\n" + " -q [-q]: Be more quiet (-q -q gives silence)\n" + " -- : Treat all remaining CommandLine as filenames\n" + "\n" + "If you wish to create par2 files for a single source file, you may leave\n" + "out the name of the par2 file from the command line.\n"; +} + +bool CommandLine::Parse(int argc, char *argv[]) +{ + if (argc<1) + { + return false; + } + + // Split the program name into path and filename + string path, name; + DiskFile::SplitFilename(argv[0], path, name); + argc--; + argv++; + + // Strip ".exe" from the end + if (name.size() > 4 && 0 == stricmp(".exe", name.substr(name.length()-4).c_str())) + { + name = name.substr(0, name.length()-4); + } + + // Check the resulting program name + if (0 == stricmp("par2create", name.c_str())) + { + operation = opCreate; + } + else if (0 == stricmp("par2verify", name.c_str())) + { + operation = opVerify; + } + else if (0 == stricmp("par2repair", name.c_str())) + { + operation = opRepair; + } + + // Have we determined what operation we want? + if (operation == opNone) + { + if (argc<2) + { + cerr << "Not enough command line arguments." << endl; + return false; + } + + switch (tolower(argv[0][0])) + { + case 'c': + if (argv[0][1] == 0 || 0 == stricmp(argv[0], "create")) + operation = opCreate; + break; + case 'v': + if (argv[0][1] == 0 || 0 == stricmp(argv[0], "verify")) + operation = opVerify; + break; + case 'r': + if (argv[0][1] == 0 || 0 == stricmp(argv[0], "repair")) + operation = opRepair; + break; + } + if (operation == opNone) + { + cerr << "Invalid operation specified: " << argv[0] << endl; + return false; + } + argc--; + argv++; + } + + bool options = true; + + while (argc>0) + { + if (argv[0][0]) + { + if (options && argv[0][0] != '-') + options = false; + + if (options) + { + switch (tolower(argv[0][1])) + { + case 'b': // Set the block count + { + if (operation != opCreate) + { + cerr << "Cannot specify block count unless creating." << endl; + return false; + } + if (blockcount > 0) + { + cerr << "Cannot specify block count twice." << endl; + return false; + } + else if (blocksize > 0) + { + cerr << "Cannot specify both block count and block size." << endl; + return false; + } + + char *p = &argv[0][2]; + while (blockcount <= 3276 && *p && isdigit(*p)) + { + blockcount = blockcount * 10 + (*p - '0'); + p++; + } + if (0 == blockcount || blockcount > 32768 || *p) + { + cerr << "Invalid block count option: " << argv[0] << endl; + return false; + } + } + break; + + case 's': // Set the block size + { + if (operation != opCreate) + { + cerr << "Cannot specify block size unless creating." << endl; + return false; + } + if (blocksize > 0) + { + cerr << "Cannot specify block size twice." << endl; + return false; + } + else if (blockcount > 0) + { + cerr << "Cannot specify both block count and block size." << endl; + return false; + } + + char *p = &argv[0][2]; + while (blocksize <= 429496729 && *p && isdigit(*p)) + { + blocksize = blocksize * 10 + (*p - '0'); + p++; + } + if (*p || blocksize == 0) + { + cerr << "Invalid block size option: " << argv[0] << endl; + return false; + } + if (blocksize & 3) + { + cerr << "Block size must be a multiple of 4." << endl; + return false; + } + } + break; + + case 'r': // Set the amount of redundancy required + { + if (operation != opCreate) + { + cerr << "Cannot specify redundancy unless creating." << endl; + return false; + } + if (redundancyset) + { + cerr << "Cannot specify redundancy twice." << endl; + return false; + } + else if (recoveryblockcountset) + { + cerr << "Cannot specify both redundancy and recovery block count." << endl; + return false; + } + + char *p = &argv[0][2]; + while (redundancy <= 10 && *p && isdigit(*p)) + { + redundancy = redundancy * 10 + (*p - '0'); + p++; + } + if (redundancy > 100 || *p) + { + cerr << "Invalid redundancy option: " << argv[0] << endl; + return false; + } + if (redundancy == 0 && recoveryfilecount > 0) + { + cerr << "Cannot set redundancy to 0 and file count > 0" << endl; + return false; + } + redundancyset = true; + } + break; + + case 'c': // Set the number of recovery blocks to create + { + if (operation != opCreate) + { + cerr << "Cannot specify recovery block count unless creating." << endl; + return false; + } + if (recoveryblockcountset) + { + cerr << "Cannot specify recovery block count twice." << endl; + return false; + } + else if (redundancyset) + { + cerr << "Cannot specify both recovery block count and redundancy." << endl; + return false; + } + + char *p = &argv[0][2]; + while (recoveryblockcount <= 32768 && *p && isdigit(*p)) + { + recoveryblockcount = recoveryblockcount * 10 + (*p - '0'); + p++; + } + if (recoveryblockcount > 32768 || *p) + { + cerr << "Invalid recoveryblockcount option: " << argv[0] << endl; + return false; + } + if (recoveryblockcount == 0 && recoveryfilecount > 0) + { + cerr << "Cannot set recoveryblockcount to 0 and file count > 0" << endl; + return false; + } + recoveryblockcountset = true; + } + break; + + case 'f': // Specify the First block recovery number + { + if (operation != opCreate) + { + cerr << "Cannot specify first block number unless creating." << endl; + return false; + } + if (firstblock > 0) + { + cerr << "Cannot specify first block twice." << endl; + return false; + } + + char *p = &argv[0][2]; + while (firstblock <= 3276 && *p && isdigit(*p)) + { + firstblock = firstblock * 10 + (*p - '0'); + p++; + } + if (firstblock > 32768 || *p) + { + cerr << "Invalid first block option: " << argv[0] << endl; + return false; + } + } + break; + + case 'u': // Specify uniformly sized recovery files + { + if (operation != opCreate) + { + cerr << "Cannot specify uniform files unless creating." << endl; + return false; + } + if (argv[0][2]) + { + cerr << "Invalid option: " << argv[0] << endl; + return false; + } + if (recoveryfilescheme != scUnknown) + { + cerr << "Cannot specify two recovery file size schemes." << endl; + return false; + } + + recoveryfilescheme = scUniform; + } + break; + + case 'l': // Limit the size of the recovery files + { + if (operation != opCreate) + { + cerr << "Cannot specify limit files unless creating." << endl; + return false; + } + if (argv[0][2]) + { + cerr << "Invalid option: " << argv[0] << endl; + return false; + } + if (recoveryfilescheme != scUnknown) + { + cerr << "Cannot specify two recovery file size schemes." << endl; + return false; + } + if (recoveryfilecount > 0) + { + cerr << "Cannot specify limited size and number of files at the same time." << endl; + return false; + } + + recoveryfilescheme = scLimited; + } + break; + + case 'n': // Specify the number of recovery files + { + if (operation != opCreate) + { + cerr << "Cannot specify recovery file count unless creating." << endl; + return false; + } + if (recoveryfilecount > 0) + { + cerr << "Cannot specify recovery file count twice." << endl; + return false; + } + if (redundancyset && redundancy == 0) + { + cerr << "Cannot set file count when redundancy is set to 0." << endl; + return false; + } + if (recoveryblockcountset && recoveryblockcount == 0) + { + cerr << "Cannot set file count when recovery block count is set to 0." << endl; + return false; + } + if (recoveryfilescheme == scLimited) + { + cerr << "Cannot specify limited size and number of files at the same time." << endl; + return false; + } + + char *p = &argv[0][2]; + while (*p && isdigit(*p)) + { + recoveryfilecount = recoveryfilecount * 10 + (*p - '0'); + p++; + } + if (recoveryfilecount == 0 || *p) + { + cerr << "Invalid recovery file count option: " << argv[0] << endl; + return false; + } + } + break; + + case 'm': // Specify how much memory to use for output buffers + { + if (memorylimit > 0) + { + cerr << "Cannot specify memory limit twice." << endl; + return false; + } + + char *p = &argv[0][2]; + while (*p && isdigit(*p)) + { + memorylimit = memorylimit * 10 + (*p - '0'); + p++; + } + if (memorylimit == 0 || *p) + { + cerr << "Invalid memory limit option: " << argv[0] << endl; + return false; + } + } + break; + + case 'v': + { + switch (noiselevel) + { + case nlUnknown: + { + if (argv[0][2] == 'v') + noiselevel = nlDebug; + else + noiselevel = nlNoisy; + } + break; + case nlNoisy: + case nlDebug: + noiselevel = nlDebug; + break; + default: + cerr << "Cannot use both -v and -q." << endl; + return false; + break; + } + } + break; + + case 'q': + { + switch (noiselevel) + { + case nlUnknown: + { + if (argv[0][2] == 'q') + noiselevel = nlSilent; + else + noiselevel = nlQuiet; + } + break; + case nlQuiet: + case nlSilent: + noiselevel = nlSilent; + break; + default: + cerr << "Cannot use both -v and -q." << endl; + return false; + break; + } + } + break; + + case '-': + { + argc--; + argv++; + options = false; + continue; + } + break; + default: + { + cerr << "Invalid option specified: " << argv[0] << endl; + return false; + } + } + } + else + { + list *filenames; + + // If the argument includes wildcard characters, + // search the disk for matching files + if (strchr(argv[0], '*') || strchr(argv[0], '?')) + { + string path; + string name; + DiskFile::SplitFilename(argv[0], path, name); + + filenames = DiskFile::FindFiles(path, name); + } + else + { + //start of shell expanded * patch. -- Michael Evans + //The shell might expaned * so, if we have our name and we're creating, then filter for files... + if ((parfilename.length() != 0) && (operation == opCreate)) + { +#ifdef WIN32 + if (GetFileAttributes(argv[0]) & FILE_ATTRIBUTE_DIRECTORY) // != 0, but no need... +#else //Not WIN32, probably *nix + struct stat st; + if (!(stat(argv[0], &st) == 0 && S_ISREG(st.st_mode))) +#endif + { + cerr << "Skipping non-regular file: " << argv[0] << endl; + argc--; + argv++; + options = false; + continue; + } + }//end of shell expanded * patch. -- Michael Evans + filenames = new list; + filenames->push_back(argv[0]); + } + + list::iterator fn = filenames->begin(); + while (fn != filenames->end()) + { + // Convert filename from command line into a full path + filename + string filename = DiskFile::GetCanonicalPathname(*fn); + + // If this is the first file on the command line, then it + // is the main PAR2 file. + if (parfilename.length() == 0) + { + // If we are verifying or repairing, the PAR2 file must + // already exist + if (operation != opCreate) + { + // Find the last '.' in the filename + string::size_type where = filename.find_last_of('.'); + if (where != string::npos) + { + // Get what follows the last '.' + string tail = filename.substr(where+1); + + if (0 == stricmp(tail.c_str(), "par2")) + { + parfilename = filename; + version = verPar2; + } + else if (0 == stricmp(tail.c_str(), "par") || + (tail.size() == 3 && + tolower(tail[0]) == 'p' && + isdigit(tail[1]) && + isdigit(tail[2]))) + { + parfilename = filename; + version = verPar1; + } + } + + // If we haven't figured out which version of PAR file we + // are using from the file extension, then presumable the + // files filename was actually the name of a data file. + if (version == verUnknown) + { + // Check for the existence of a PAR2 of PAR file. + if (DiskFile::FileExists(filename + ".par2")) + { + version = verPar2; + parfilename = filename + ".par2"; + } + else if (DiskFile::FileExists(filename + ".PAR2")) + { + version = verPar2; + parfilename = filename + ".PAR2"; + } + else if (DiskFile::FileExists(filename + ".par")) + { + version = verPar1; + parfilename = filename + ".par"; + } + else if (DiskFile::FileExists(filename + ".PAR")) + { + version = verPar1; + parfilename = filename + ".PAR"; + } + } + else + { + // Does the specified PAR or PAR2 file exist + if (!DiskFile::FileExists(filename)) + { + version = verUnknown; + } + } + + if (version == verUnknown) + { + cerr << "The recovery file does not exist: " << filename << endl; + return false; + } + } + else + { + // We are creating a new file + version = verPar2; + parfilename = filename; + } + } + else + { + // All other files must exist + if (!DiskFile::FileExists(filename)) + { + cerr << "The source file does not exist: " << filename << endl; + return false; + } + + u64 filesize = DiskFile::GetFileSize(filename); + + // Ignore all 0 byte files + if (filesize > 0) + { + extrafiles.push_back(ExtraFile(filename, filesize)); + + // track the total size of the source files and how + // big the largest one is. + totalsourcesize += filesize; + if (largestsourcesize < filesize) + largestsourcesize = filesize; + } + else + { + cout << "Skipping 0 byte file: " << filename << endl; + } + } + + ++fn; + } + delete filenames; + } + } + + argc--; + argv++; + } + + if (parfilename.length() == 0) + { + cerr << "You must specify a Recovery file." << endl; + return false; + } + + // Default noise level + if (noiselevel == nlUnknown) + { + noiselevel = nlNormal; + } + + // If we a creating, check the other parameters + if (operation == opCreate) + { + // If no recovery file size scheme is specified then use Variable + if (recoveryfilescheme == scUnknown) + { + recoveryfilescheme = scVariable; + } + + // If neither block count not block size is specified + if (blockcount == 0 && blocksize == 0) + { + // Use a block count of 2000 + blockcount = 2000; + } + + // If we are creating, the source files must be given. + if (extrafiles.size() == 0) + { + // Does the par filename include the ".par2" on the end? + if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) + { + // Yes it does. + cerr << "You must specify a list of files when creating." << endl; + return false; + } + else + { + // No it does not. + + // In that case check to see if the file exists, and if it does + // assume that you wish to create par2 files for it. + + u64 filesize = 0; + if (DiskFile::FileExists(parfilename) && + (filesize = DiskFile::GetFileSize(parfilename)) > 0) + { + extrafiles.push_back(ExtraFile(parfilename, filesize)); + + // track the total size of the source files and how + // big the largest one is. + totalsourcesize += filesize; + if (largestsourcesize < filesize) + largestsourcesize = filesize; + } + else + { + // The file does not exist or it is empty. + + cerr << "You must specify a list of files when creating." << endl; + return false; + } + } + } + + // Strip the ".par2" from the end of the filename of the main PAR2 file. + if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) + { + parfilename = parfilename.substr(0, parfilename.length()-5); + } + + // Assume a redundancy of 5% if neither redundancy or recoveryblockcount were set. + if (!redundancyset && !recoveryblockcountset) + { + redundancy = 5; + } + } + + // Assume a memory limit of 16MB if not specified. + if (memorylimit == 0) + { +#ifdef WIN32 + u64 TotalPhysicalMemory = 0; + + HMODULE hLib = ::LoadLibraryA("kernel32.dll"); + if (NULL != hLib) + { + BOOL (WINAPI *pfn)(LPMEMORYSTATUSEX) = (BOOL (WINAPI*)(LPMEMORYSTATUSEX))::GetProcAddress(hLib, "GlobalMemoryStatusEx"); + + if (NULL != pfn) + { + MEMORYSTATUSEX mse; + mse.dwLength = sizeof(mse); + if (pfn(&mse)) + { + TotalPhysicalMemory = mse.ullTotalPhys; + } + } + + ::FreeLibrary(hLib); + } + + if (TotalPhysicalMemory == 0) + { + MEMORYSTATUS ms; + ::ZeroMemory(&ms, sizeof(ms)); + ::GlobalMemoryStatus(&ms); + + TotalPhysicalMemory = ms.dwTotalPhys; + } + + if (TotalPhysicalMemory == 0) + { + // Assume 128MB + TotalPhysicalMemory = 128 * 1048576; + } + + // Half of total physical memory + memorylimit = (size_t)(TotalPhysicalMemory / 1048576 / 2); +#else + memorylimit = 16; +#endif + } + memorylimit *= 1048576; + + return true; +} diff --git a/lib/par2/commandline.h b/lib/par2/commandline.h new file mode 100644 index 000000000..65058cd06 --- /dev/null +++ b/lib/par2/commandline.h @@ -0,0 +1,157 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __COMMANDLINE_H__ +#define __COMMANDLINE_H__ + +// The CommandLine object is responsible for understanding the format +// of the command line parameters are parsing the command line to +// extract details as to what the user wants to do. + +class CommandLine +{ +public: + CommandLine(void); + + // Parse the supplied command line arguments. + bool Parse(int argc, char *argv[]); + + // Display details of the correct format for command line parameters. + static void usage(void); + + // What operation will we be carrying out + typedef enum + { + opNone = 0, + opCreate, // Create new PAR2 recovery volumes + opVerify, // Verify but don't repair damaged data files + opRepair // Verify and if possible repair damaged data files + } Operation; + + typedef enum + { + verUnknown = 0, + verPar1, // Processing PAR 1.0 files + verPar2 // Processing PAR 2.0 files + } Version; + + typedef enum + { + scUnknown = 0, + scVariable, // Each PAR2 file will have 2x as many blocks as previous + scLimited, // Limit PAR2 file size + scUniform // All PAR2 files the same size + } Scheme; + + typedef enum + { + nlUnknown = 0, + nlSilent, // Absolutely no output (other than errors) + nlQuiet, // Bare minimum of output + nlNormal, // Normal level of output + nlNoisy, // Lots of output + nlDebug // Extra debugging information + } NoiseLevel; + + // Any extra files listed on the command line + class ExtraFile + { + public: + ExtraFile(void); + ExtraFile(const ExtraFile&); + ExtraFile& operator=(const ExtraFile&); + + ExtraFile(const string &name, u64 size); + + string FileName(void) const {return filename;} + u64 FileSize(void) const {return filesize;} + + protected: + string filename; + u64 filesize; + }; + +public: + // Accessor functions for the command line parameters + + CommandLine::Operation GetOperation(void) const {return operation;} + CommandLine::Version GetVersion(void) const {return version;} + u64 GetBlockSize(void) const {return blocksize;} + u32 GetBlockCount(void) const {return blockcount;} + u32 GetRedundancy(void) const {return redundancy;} + u32 GetFirstRecoveryBlock(void) const {return firstblock;} + u32 GetRecoveryFileCount(void) const {return recoveryfilecount;} + u32 GetRecoveryBlockCount(void) const {return recoveryblockcount;} + CommandLine::Scheme GetRecoveryFileScheme(void) const {return recoveryfilescheme;} + size_t GetMemoryLimit(void) const {return memorylimit;} + u64 GetLargestSourceSize(void) const {return largestsourcesize;} + u64 GetTotalSourceSize(void) const {return totalsourcesize;} + CommandLine::NoiseLevel GetNoiseLevel(void) const {return noiselevel;} + + string GetParFilename(void) const {return parfilename;} + const list& GetExtraFiles(void) const {return extrafiles;} + +protected: + Operation operation; // The operation to be carried out. + Version version; // What version files will be processed. + + NoiseLevel noiselevel; // How much display output should there be. + + u32 blockcount; // How many blocks the source files should + // be virtually split into. + + u64 blocksize; // What virtual block size to use. + + u32 firstblock; // What the exponent value for the first + // recovery block will be. + + Scheme recoveryfilescheme; // How the the size of the recovery files should + // be calculated. + + u32 recoveryfilecount; // How many recovery files should be created. + + u32 recoveryblockcount; // How many recovery blocks should be created. + bool recoveryblockcountset; // Set if the recoveryblockcount as been specified + + u32 redundancy; // What percentage of recovery data should + // be created. + bool redundancyset; // Set if the redundancy has been specified + + string parfilename; // The name of the PAR2 file to create, or + // the name of the first PAR2 file to read + // when verifying or repairing. + + list extrafiles; // The list of other files specified on the + // command line. When creating, this will be + // the source files, and when verifying or + // repairing, this will be additional PAR2 + // files or data files to be examined. + + u64 totalsourcesize; // Total size of the source files. + + u64 largestsourcesize; // Size of the largest source file. + + size_t memorylimit; // How much memory is permitted to be used + // for the output buffer when creating + // or repairing. +}; + +typedef list::const_iterator ExtraFileIterator; + +#endif // __COMMANDLINE_H__ diff --git a/lib/par2/crc.cpp b/lib/par2/crc.cpp new file mode 100644 index 000000000..49be234c9 --- /dev/null +++ b/lib/par2/crc.cpp @@ -0,0 +1,78 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +// The one and only CCITT CRC32 lookup table +crc32table ccitttable(0xEDB88320L); + +// Construct the CRC32 lookup table from the specified polynomial +void GenerateCRC32Table(u32 polynomial, u32 (&table)[256]) +{ + for (u32 i = 0; i <= 255 ; i++) + { + u32 crc = i; + + for (u32 j = 0; j < 8; j++) + { + crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0); + } + + table[i] = crc; + } +} + +// Construct a CRC32 lookup table for windowing +void GenerateWindowTable(u64 window, u32 (&target)[256]) +{ + for (u32 i=0; i<=255; i++) + { + u32 crc = ccitttable.table[i]; + + for (u64 j=0; j> 8) & 0x00ffffffL) ^ ccitttable.table[(u8)crc]; + } + + target[i] = crc; + } +} + +// Construct the mask value to apply to the CRC when windowing +u32 ComputeWindowMask(u64 window) +{ + u32 result = ~0; + while (window > 0) + { + result = CRCUpdateChar(result, (char)0); + + window--; + } + result ^= ~0; + + return result; +} diff --git a/lib/par2/crc.h b/lib/par2/crc.h new file mode 100644 index 000000000..b7004a120 --- /dev/null +++ b/lib/par2/crc.h @@ -0,0 +1,113 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __CRC_H__ +#define __CRC_H__ + +// These global functions are used to compute the CCITT CRC32 checksum of +// blocks of data. + +// The CRC for a block of data may be computed piecemeal be repeatedly +// calling CRCUpdateChar, and CRCUpdateBlock. + +// Given the CRC for a block of data in a buffer, CRCSlideChar may be used +// to quickly compute the CRC for the block of data in the buffer that is the +// same size but offset one character later in the buffer. + + +// Construct the CRC32 lookup table from the specified polynomial +void GenerateCRC32Table(u32 polynomial, u32 (&table)[256]); + +// A CRC32 lookup table +struct crc32table +{ + crc32table(u32 polynomial) + { + GenerateCRC32Table(polynomial, table); + } + + u32 table[256]; +}; + +// The one and only CCITT CRC32 lookup table +extern crc32table ccitttable; + +// Update the CRC using one character +inline u32 CRCUpdateChar(u32 crc, u8 ch) +{ + return ((crc >> 8) & 0x00ffffffL) ^ ccitttable.table[(u8)crc ^ ch]; +} + +// Update the CRC using a block of characters in a buffer +inline u32 CRCUpdateBlock(u32 crc, size_t length, const void *buffer) +{ + const unsigned char *current = (const unsigned char *)buffer; + + while (length-- > 0) + { + crc = ((crc >> 8) & 0x00ffffffL) ^ ccitttable.table[(u8)crc ^ (*current++)]; + } + + return crc; +} + +// Update the CRC using a block of 0s. +inline u32 CRCUpdateBlock(u32 crc, size_t length) +{ + while (length-- > 0) + { + crc = ((crc >> 8) & 0x00ffffffL) ^ ccitttable.table[(u8)crc]; + } + + return crc; +} + +// Construct a CRC32 lookup table for windowing +void GenerateWindowTable(u64 window, u32 (&windowtable)[256]); +// Construct the mask value to apply to the CRC when windowing +u32 ComputeWindowMask(u64 window); + +// Slide the CRC along a buffer by one character (removing the old and adding the new). +// The new character is added using the main CCITT CRC32 table, and the old character +// is removed using the windowtable. +inline u32 CRCSlideChar(u32 crc, u8 chNew, u8 chOld, const u32 (&windowtable)[256]) +{ + return ((crc >> 8) & 0x00ffffffL) ^ ccitttable.table[(u8)crc ^ chNew] ^ windowtable[chOld]; +} + +/* + + char *buffer; + u64 window; + + //... + + u32 windowtable[256]; + GenerateWindowTable(window, windowtable); + u32 windowmask = ComputeWindowMask(window); + + u32 crc = ~0 ^ CRCUpdateBlock(~0, window, buffer); + crc = windowmask ^ CRCSlideChar(windowmask ^ crc, buffer[window], buffer[0], windowtable); + + assert(crc == ~0 ^ CRCUpdateBlock(~0, window, buffer+1)); + +*/ + + +#endif // __CRC_H__ diff --git a/lib/par2/creatorpacket.cpp b/lib/par2/creatorpacket.cpp new file mode 100644 index 000000000..fc064b99b --- /dev/null +++ b/lib/par2/creatorpacket.cpp @@ -0,0 +1,85 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +// Construct the creator packet. + +// The only external information required to complete construction is +// the set_id_hash (which is normally computed from information in the +// main packet). + +bool CreatorPacket::Create(const MD5Hash &setid) +{ + string creator = "Created by " PACKAGE " version " VERSION "."; + + // Allocate a packet just large enough for creator name + CREATORPACKET *packet = (CREATORPACKET *)AllocatePacket(sizeof(*packet) + (~3 & (3+(u32)creator.size()))); + + // Fill in the details the we know + packet->header.magic = packet_magic; + packet->header.length = packetlength; + //packet->header.hash; // Compute shortly + packet->header.setid = setid; + packet->header.type = creatorpacket_type; + + // Copy the creator description into the packet + memcpy(packet->client, creator.c_str(), creator.size()); + + // Compute the packet hash + MD5Context packetcontext; + packetcontext.Update(&packet->header.setid, packetlength - offsetof(PACKET_HEADER, setid)); + packetcontext.Final(packet->header.hash); + + return true; +} + +// Load the packet from disk. + +bool CreatorPacket::Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + // Is the packet long enough + if (header.length <= sizeof(CREATORPACKET)) + { + return false; + } + + // Is the packet too large (what is the longest reasonable creator description) + if (header.length - sizeof(CREATORPACKET) > 100000) + { + return false; + } + + // Allocate the packet (with a little extra so we will have NULLs after the description) + CREATORPACKET *packet = (CREATORPACKET *)AllocatePacket((size_t)header.length, 4); + packet->header = header; + + // Load the rest of the packet from disk + return diskfile->Read(offset + sizeof(PACKET_HEADER), + packet->client, + (size_t)packet->header.length - sizeof(PACKET_HEADER)); +} diff --git a/lib/par2/creatorpacket.h b/lib/par2/creatorpacket.h new file mode 100644 index 000000000..74199c71a --- /dev/null +++ b/lib/par2/creatorpacket.h @@ -0,0 +1,44 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __CREATORPACKET_H__ +#define __CREATORPACKET_H__ + +// The creator packet records details as to which PAR2 client +// created a particular recovery file. + +// The PAR 2.0 specification requires the presence of a +// creator packet, but it is not actually needed for the +// verification or recovery of damaged files. + +class CreatorPacket : public CriticalPacket +{ +public: + // Construct the packet + CreatorPacket(void) {}; + ~CreatorPacket(void) {}; + + // Create a creator packet for a specified set id hash value + bool Create(const MD5Hash &set_id_hash); + + // Load a creator packet from a specified file + bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); +}; + +#endif // __CREATORPACKET_H__ diff --git a/lib/par2/criticalpacket.cpp b/lib/par2/criticalpacket.cpp new file mode 100644 index 000000000..9b9090c55 --- /dev/null +++ b/lib/par2/criticalpacket.cpp @@ -0,0 +1,48 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +bool CriticalPacket::WritePacket(DiskFile &diskfile, u64 fileoffset) const +{ + assert(&diskfile != 0 && packetdata != 0 && packetlength != 0); + + return diskfile.Write(fileoffset, packetdata, packetlength); +} + +void CriticalPacket::FinishPacket(const MD5Hash &setid) +{ + assert(packetdata != 0 && packetlength >= sizeof(PACKET_HEADER)); + + PACKET_HEADER *header = (PACKET_HEADER*)packetdata; + header->setid = setid; + + MD5Context packetcontext; + packetcontext.Update(&header->setid, packetlength - offsetof(PACKET_HEADER, setid)); + packetcontext.Final(header->hash); +} + diff --git a/lib/par2/criticalpacket.h b/lib/par2/criticalpacket.h new file mode 100644 index 000000000..6d4749aa0 --- /dev/null +++ b/lib/par2/criticalpacket.h @@ -0,0 +1,145 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __CRITICALPACKET_H__ +#define __CRITICALPACKET_H__ + +// Base class for main packet, file verification packet, file description packet +// and creator packet. + +// These packets are all small and are held in memory in their entirity + +class CriticalPacket +{ +public: + CriticalPacket(void); + ~CriticalPacket(void); + +public: + // Write a copy of the packet to the specified file at the specified offset + bool WritePacket(DiskFile &diskfile, u64 fileoffset) const; + + // Obtain the lenght of the packet. + size_t PacketLength(void) const; + + // Allocate some memory for the packet (plus some extra padding). + void* AllocatePacket(size_t length, size_t extra = 0); + + // Finish a packet (by storing the set_id_hash and then computing the packet_hash). + void FinishPacket(const MD5Hash &set_id_hash); + +protected: + u8 *packetdata; + size_t packetlength; +}; + +inline CriticalPacket::CriticalPacket(void) +{ + // There is no data initially + packetdata = 0; + packetlength = 0; +} + +inline CriticalPacket::~CriticalPacket(void) +{ + // Delete the data for the packet + delete [] packetdata; +} + +inline size_t CriticalPacket::PacketLength(void) const +{ + return packetlength; +} + +inline void* CriticalPacket::AllocatePacket(size_t length, size_t extra) +{ + // Hey! We can't allocate the packet twice + assert(packetlength == 0 && packetdata == 0); + + // Remember the requested packet length + packetlength = length; + + // Allocate and clear the requested packet length plus the extra. + packetdata = new u8[length+extra]; + memset(packetdata, 0, length+extra); + + return packetdata; +} + +// Class used to record the fact that a copy of a particular critical packet +// will be written to a particular file at a specific offset. + +class CriticalPacketEntry +{ +public: + CriticalPacketEntry(DiskFile *_diskfile, + u64 _offset, + const CriticalPacket *_packet) + : diskfile(_diskfile) + , offset(_offset) + , packet(_packet) + {} + CriticalPacketEntry(void) + : diskfile(0) + , offset(0) + , packet(0) + {} + CriticalPacketEntry(const CriticalPacketEntry &other) + : diskfile(other.diskfile) + , offset(other.offset) + , packet(other.packet) + {} + CriticalPacketEntry& operator=(const CriticalPacketEntry &other) + { + diskfile = other.diskfile; + offset = other.offset; + packet = other.packet; + return *this; + } + +public: + // Write the packet to disk. + bool WritePacket(void) const; + + // Obtain the length of the packet. + u64 PacketLength(void) const; + +protected: + DiskFile *diskfile; + u64 offset; + const CriticalPacket *packet; +}; + +inline bool CriticalPacketEntry::WritePacket(void) const +{ + assert(packet != 0 && diskfile != 0); + + // Tell the packet to write itself to disk + return packet->WritePacket(*diskfile, offset); +} + +inline u64 CriticalPacketEntry::PacketLength(void) const +{ + assert(packet != 0); + + // Ask the packet how big it is. + return packet->PacketLength(); +} + +#endif // __CRITICALPACKET_H__ diff --git a/lib/par2/datablock.cpp b/lib/par2/datablock.cpp new file mode 100644 index 000000000..8349a91c6 --- /dev/null +++ b/lib/par2/datablock.cpp @@ -0,0 +1,107 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +// Open the file associated with the data block if is not already open +bool DataBlock::Open(void) +{ + if (diskfile == 0) + return false; + + if (diskfile->IsOpen()) + return true; + + return diskfile->Open(); +} + +// Read some data at a specified position within a data block +// into a buffer in memory + +bool DataBlock::ReadData(u64 position, // Position within the block + size_t size, // Size of the memory buffer + void *buffer) // Pointer to memory buffer +{ + assert(diskfile != 0); + + // Check to see if the position from which data is to be read + // is within the bounds of the data block + if (length > position) + { + // Compute the file offset and how much data to physically read from disk + u64 fileoffset = offset + position; + size_t want = (size_t)min((u64)size, length - position); + + // Read the data from the file into the buffer + if (!diskfile->Read(fileoffset, buffer, want)) + return false; + + // If the read extends beyond the end of the data block, + // then the rest of the buffer is zeroed. + if (want < size) + { + memset(&((u8*)buffer)[want], 0, size-want); + } + } + else + { + // Zero the whole buffer + memset(buffer, 0, size); + } + + return true; +} + +// Write some data at a specified position within a datablock +// from memory to disk + +bool DataBlock::WriteData(u64 position, // Position within the block + size_t size, // Size of the memory buffer + const void *buffer, // Pointer to memory buffer + size_t &wrote) // Amount actually written +{ + assert(diskfile != 0); + + wrote = 0; + + // Check to see if the position from which data is to be written + // is within the bounds of the data block + if (length > position) + { + // Compute the file offset and how much data to physically write to disk + u64 fileoffset = offset + position; + size_t have = (size_t)min((u64)size, length - position); + + // Write the data from the buffer to disk + if (!diskfile->Write(fileoffset, buffer, have)) + return false; + + wrote = have; + } + + return true; +} diff --git a/lib/par2/datablock.h b/lib/par2/datablock.h new file mode 100644 index 000000000..ba8734bcc --- /dev/null +++ b/lib/par2/datablock.h @@ -0,0 +1,133 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __DATABLOCK_H__ +#define __DATABLOCK_H__ + +class DiskFile; + +// A Data Block is a block of data of a specific length at a specific +// offset in a specific file. + +// It may be either a block of data in a source file from which recovery +// data is being computed, a block of recovery data in a recovery file, or +// a block in a target file that is being reconstructed. + +class DataBlock +{ +public: + DataBlock(void); + ~DataBlock(void); + +public: + // Set the length of the block + void SetLength(u64 length); + + // Set the location of the block + void SetLocation(DiskFile *diskfile, u64 offset); + void ClearLocation(void); + +public: + // Check to see if the location of the block has been set + bool IsSet(void) const; + + // Which disk file is this data block in + DiskFile* GetDiskFile(void) const; + + // What offset is the block located at + u64 GetOffset(void) const; + + // What is the length of this block + u64 GetLength(void) const; + +public: + // Open the disk file if it is not already open (so that it can be read) + bool Open(void); + + // Read some of the data from disk into memory. + bool ReadData(u64 position, size_t size, void *buffer); + + // Write some of the data from memory to disk + bool WriteData(u64 position, size_t size, const void *buffer, size_t &wrote); + +protected: + DiskFile *diskfile; // Which disk file is the block associated with + u64 offset; // What is the file offset + u64 length; // How large is the block +}; + + +// Construct the data block +inline DataBlock::DataBlock(void) +{ + diskfile = 0; + offset = 0; + length = 0; +} + +// Destroy the data block +inline DataBlock::~DataBlock(void) +{ +} + +// Set the length of the block +inline void DataBlock::SetLength(u64 _length) +{ + length = _length; +} + +// Set the location of the block +inline void DataBlock::SetLocation(DiskFile *_diskfile, u64 _offset) +{ + diskfile = _diskfile; + offset = _offset; +} + +// Clear the location of the block +inline void DataBlock::ClearLocation(void) +{ + diskfile = 0; + offset = 0; +} + +// Check to see of the location is known +inline bool DataBlock::IsSet(void) const +{ + return (diskfile != 0); +} + +// Which disk file is this data block in +inline DiskFile* DataBlock::GetDiskFile(void) const +{ + return diskfile; +} + +// What offset is the block located at +inline u64 DataBlock::GetOffset(void) const +{ + return offset; +} + +// What is the length of this block +inline u64 DataBlock::GetLength(void) const +{ + return length; +} + +#endif // __DATABLOCK_H__ diff --git a/lib/par2/descriptionpacket.cpp b/lib/par2/descriptionpacket.cpp new file mode 100644 index 000000000..44208dbb2 --- /dev/null +++ b/lib/par2/descriptionpacket.cpp @@ -0,0 +1,113 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +// Construct the packet and store the filename and size. + +bool DescriptionPacket::Create(string filename, u64 filesize) +{ + // Allocate some extra bytes for the packet in memory so that strlen() can + // be used on the filename. The extra bytes do not get written to disk. + FILEDESCRIPTIONPACKET *packet = (FILEDESCRIPTIONPACKET *)AllocatePacket(sizeof(*packet) + (~3 & (3 + (u32)filename.size())), 4); + + // Store everything that is currently known in the packet. + + packet->header.magic = packet_magic; + packet->header.length = packetlength; + //packet->header.hash; // Not known yet + //packet->header.setid; // Not known yet + packet->header.type = filedescriptionpacket_type; + + //packet->fileid; // Not known yet + //packet->hashfull; // Not known yet + //packet->hash16k; // Not known yet + packet->length = filesize; + + memcpy(packet->name, filename.c_str(), filename.size()); + + return true; +} + + +void DescriptionPacket::Hash16k(const MD5Hash &hash) +{ + ((FILEDESCRIPTIONPACKET *)packetdata)->hash16k = hash; +} + +void DescriptionPacket::HashFull(const MD5Hash &hash) +{ + ((FILEDESCRIPTIONPACKET *)packetdata)->hashfull = hash; +} + +void DescriptionPacket::ComputeFileId(void) +{ + FILEDESCRIPTIONPACKET *packet = ((FILEDESCRIPTIONPACKET *)packetdata); + + // Compute the fileid from the hash, length, and name fields in the packet. + + MD5Context context; + context.Update(&packet->hash16k, + sizeof(FILEDESCRIPTIONPACKET)-offsetof(FILEDESCRIPTIONPACKET,hash16k) + +strlen((const char*)packet->name)); + context.Final(packet->fileid); +} + +// Load a description packet from a specified file +bool DescriptionPacket::Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + // Is the packet big enough + if (header.length <= sizeof(FILEDESCRIPTIONPACKET)) + { + return false; + } + + // Is the packet too large (what is the longest permissible filename) + if (header.length - sizeof(FILEDESCRIPTIONPACKET) > 100000) + { + return false; + } + + // Allocate the packet (with a little extra so we will have NULLs after the filename) + FILEDESCRIPTIONPACKET *packet = (FILEDESCRIPTIONPACKET *)AllocatePacket((size_t)header.length, 4); + + packet->header = header; + + // Read the rest of the packet from disk + if (!diskfile->Read(offset + sizeof(PACKET_HEADER), + &packet->fileid, + (size_t)packet->header.length - sizeof(PACKET_HEADER))) + return false; + + // Are the file and 16k hashes consistent + if (packet->length <= 16384 && packet->hash16k != packet->hashfull) + { + return false; + } + + return true; +} diff --git a/lib/par2/descriptionpacket.h b/lib/par2/descriptionpacket.h new file mode 100644 index 000000000..6fd0c7909 --- /dev/null +++ b/lib/par2/descriptionpacket.h @@ -0,0 +1,105 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __DESCRIPTIONPACKET_H__ +#define __DESCRIPTIONPACKET_H__ + +// The description packet records details about a file (including its name, +// size, and the Hash of both the whole file and the first 16k of the file). + +class DescriptionPacket : public CriticalPacket +{ +public: + // Construct the packet + DescriptionPacket(void) {}; + ~DescriptionPacket(void) {}; + +public: + // Construct the packet and store the filename and size. + bool Create(string _filename, u64 _filesize); + + // Store the computed Hash values in the packet. + void Hash16k(const MD5Hash &hash); + void HashFull(const MD5Hash &hash); + + // Compute and return the file id hash from information in the packet + void ComputeFileId(void); + const MD5Hash& FileId(void) const; + + // Return the size of the file + u64 FileSize(void) const; + +public: + // Load a description packet from a specified file + bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + + // Return the name of the file + string FileName(void) const; + + // Get the Hash values from the packet + const MD5Hash& HashFull(void) const; + const MD5Hash& Hash16k(void) const; +}; + +// Get the file id from the packet +inline const MD5Hash& DescriptionPacket::FileId(void) const +{ + assert(packetdata != 0); + + return ((const FILEDESCRIPTIONPACKET*)packetdata)->fileid; +} + +// Get the size of the file from the packet +inline u64 DescriptionPacket::FileSize(void) const +{ + assert(packetdata != 0); + + return ((const FILEDESCRIPTIONPACKET*)packetdata)->length; +} + +// Get the name of the file from the packet +// NB whilst the file format does not guarantee that the name will have a NULL +// termination character, par2cmdline always allocates a little extra data +// and fills it with NULLs to allow the filename to be directly read out of +// the packet. +inline string DescriptionPacket::FileName(void) const +{ + assert(packetdata != 0); + +// return (char*)((const FILEDESCRIPTIONPACKET*)packetdata)->name(); + return (char*)((const FILEDESCRIPTIONPACKET*)packetdata)->name; +} + +// Get the full file hash value from the packet +inline const MD5Hash& DescriptionPacket::HashFull(void) const +{ + assert(packetdata != 0); + + return ((const FILEDESCRIPTIONPACKET*)packetdata)->hashfull; +} + +// The the hash of the first 16k of the file from the packet +inline const MD5Hash& DescriptionPacket::Hash16k(void) const +{ + assert(packetdata != 0); + + return ((const FILEDESCRIPTIONPACKET*)packetdata)->hash16k; +} + +#endif // __DESCRIPTIONPACKET_H__ diff --git a/lib/par2/diskfile.cpp b/lib/par2/diskfile.cpp new file mode 100644 index 000000000..57512b675 --- /dev/null +++ b/lib/par2/diskfile.cpp @@ -0,0 +1,997 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + + +#ifdef WIN32 +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define OffsetType __int64 +#define MaxOffset 0x7fffffffffffffffI64 +#define LengthType unsigned int +#define MaxLength 0xffffffffUL + +DiskFile::DiskFile(void) +{ + filename; + filesize = 0; + offset = 0; + + hFile = INVALID_HANDLE_VALUE; + + exists = false; +} + +DiskFile::~DiskFile(void) +{ + if (hFile != INVALID_HANDLE_VALUE) + ::CloseHandle(hFile); +} + +// Create new file on disk and make sure that there is enough +// space on disk for it. +bool DiskFile::Create(string _filename, u64 _filesize) +{ + assert(hFile == INVALID_HANDLE_VALUE); + + filename = _filename; + filesize = _filesize; + + // Create the file + hFile = ::CreateFileA(_filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + DWORD error = ::GetLastError(); + + cerr << "Could not create \"" << _filename << "\": " << ErrorMessage(error) << endl; + + return false; + } + + if (filesize > 0) + { + // Seek to the end of the file + LONG lowoffset = ((LONG*)&filesize)[0]; + LONG highoffset = ((LONG*)&filesize)[1]; + + if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN)) + { + DWORD error = ::GetLastError(); + + cerr << "Could not set size of \"" << _filename << "\": " << ErrorMessage(error) << endl; + + ::CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + ::DeleteFile(_filename.c_str()); + + return false; + } + + // Set the end of the file + if (!::SetEndOfFile(hFile)) + { + DWORD error = ::GetLastError(); + + cerr << "Could not set size of \"" << _filename << "\": " << ErrorMessage(error) << endl; + + ::CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + ::DeleteFile(_filename.c_str()); + + return false; + } + } + + offset = filesize; + + exists = true; + return true; +} + +// Write some data to disk + +bool DiskFile::Write(u64 _offset, const void *buffer, size_t length) +{ + assert(hFile != INVALID_HANDLE_VALUE); + + if (offset != _offset) + { + LONG lowoffset = ((LONG*)&_offset)[0]; + LONG highoffset = ((LONG*)&_offset)[1]; + + // Seek to the required offset + if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN)) + { + DWORD error = ::GetLastError(); + + cerr << "Could not write " << (u64)length << " bytes to \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl; + + return false; + } + offset = _offset; + } + + if (length > MaxLength) + { + cerr << "Could not write " << (u64)length << " bytes to \"" << filename << "\" at offset " << _offset << ": " << "Write too long" << endl; + + return false; + } + + DWORD write = (LengthType)length; + DWORD wrote; + + // Write the data + if (!::WriteFile(hFile, buffer, write, &wrote, NULL)) + { + DWORD error = ::GetLastError(); + + cerr << "Could not write " << (u64)length << " bytes to \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl; + + return false; + } + + offset += length; + + if (filesize < offset) + { + filesize = offset; + } + + return true; +} + +// Open the file + +bool DiskFile::Open(string _filename, u64 _filesize) +{ + assert(hFile == INVALID_HANDLE_VALUE); + + filename = _filename; + filesize = _filesize; + + hFile = ::CreateFileA(_filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + DWORD error = ::GetLastError(); + + switch (error) + { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + break; + default: + cerr << "Could not open \"" << _filename << "\": " << ErrorMessage(error) << endl; + } + + return false; + } + + offset = 0; + exists = true; + + return true; +} + +// Read some data from disk + +bool DiskFile::Read(u64 _offset, void *buffer, size_t length) +{ + assert(hFile != INVALID_HANDLE_VALUE); + + if (offset != _offset) + { + LONG lowoffset = ((LONG*)&_offset)[0]; + LONG highoffset = ((LONG*)&_offset)[1]; + + // Seek to the required offset + if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN)) + { + DWORD error = ::GetLastError(); + + cerr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl; + + return false; + } + offset = _offset; + } + + if (length > MaxLength) + { + cerr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << "Read too long" << endl; + + return false; + } + + DWORD want = (LengthType)length; + DWORD got; + + // Read the data + if (!::ReadFile(hFile, buffer, want, &got, NULL)) + { + DWORD error = ::GetLastError(); + + cerr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl; + + return false; + } + + offset += length; + + return true; +} + +void DiskFile::Close(void) +{ + if (hFile != INVALID_HANDLE_VALUE) + { + ::CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + } +} + +string DiskFile::GetCanonicalPathname(string filename) +{ + char fullname[MAX_PATH]; + char *filepart; + + // Resolve a relative path to a full path + int length = ::GetFullPathName(filename.c_str(), sizeof(fullname), fullname, &filepart); + if (length <= 0 || sizeof(fullname) < length) + return filename; + + // Make sure the drive letter is upper case. + fullname[0] = toupper(fullname[0]); + + // Translate all /'s to \'s + char *current = strchr(fullname, '/'); + while (current) + { + *current++ = '\\'; + current = strchr(current, '/'); + } + + // Copy the root directory to the output string + string longname(fullname, 3); + + // Start processing at the first path component + current = &fullname[3]; + char *limit = &fullname[length]; + + // Process until we reach the end of the full name + while (current < limit) + { + char *tail; + + // Find the next \, or the end of the string + (tail = strchr(current, '\\')) || (tail = limit); + *tail = 0; + + // Create a wildcard to search for the path + string wild = longname + current; + WIN32_FIND_DATA finddata; + HANDLE hFind = ::FindFirstFile(wild.c_str(), &finddata); + if (hFind == INVALID_HANDLE_VALUE) + { + // If the component was not found then just copy the rest of the path to the + // output buffer verbatim. + longname += current; + break; + } + ::FindClose(hFind); + + // Copy the component found to the output + longname += finddata.cFileName; + + current = tail + 1; + + // If we have not reached the end of the name, add a "\" + if (current < limit) + longname += '\\'; + } + + return longname; +} + +list* DiskFile::FindFiles(string path, string wildcard) +{ + list *matches = new list; + + wildcard = path + wildcard; + WIN32_FIND_DATA fd; + HANDLE h = ::FindFirstFile(wildcard.c_str(), &fd); + if (h != INVALID_HANDLE_VALUE) + { + do + { + if (0 == (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + matches->push_back(path + fd.cFileName); + } + } while (::FindNextFile(h, &fd)); + ::FindClose(h); + } + + return matches; +} + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#else // !WIN32 +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef HAVE_FSEEKO +# define OffsetType off_t +# define MaxOffset ((off_t)0x7fffffffffffffffULL) +# define fseek fseeko +#else +# if _FILE_OFFSET_BITS == 64 +# define OffsetType unsigned long long +# define MaxOffset 0x7fffffffffffffffULL +# else +# define OffsetType long +# define MaxOffset 0x7fffffffUL +# endif +#endif + +#define LengthType unsigned int +#define MaxLength 0xffffffffUL + +DiskFile::DiskFile(void) +{ + //filename; + filesize = 0; + offset = 0; + + file = 0; + + exists = false; +} + +DiskFile::~DiskFile(void) +{ + if (file != 0) + fclose(file); +} + +// Create new file on disk and make sure that there is enough +// space on disk for it. +bool DiskFile::Create(string _filename, u64 _filesize) +{ + assert(file == 0); + + filename = _filename; + filesize = _filesize; + + file = fopen(_filename.c_str(), "wb"); + if (file == 0) + { + cerr << "Could not create: " << _filename << endl; + + return false; + } + + if (_filesize > (u64)MaxOffset) + { + cerr << "Requested file size for " << _filename << " is too large." << endl; + return false; + } + + if (_filesize > 0) + { + if (fseek(file, (OffsetType)_filesize-1, SEEK_SET)) + { + fclose(file); + file = 0; + ::remove(filename.c_str()); + + cerr << "Could not set end of file: " << _filename << endl; + return false; + } + + if (1 != fwrite(&_filesize, 1, 1, file)) + { + fclose(file); + file = 0; + ::remove(filename.c_str()); + + cerr << "Could not set end of file: " << _filename << endl; + return false; + } + } + + offset = filesize; + + exists = true; + return true; +} + +// Write some data to disk + +bool DiskFile::Write(u64 _offset, const void *buffer, size_t length) +{ + assert(file != 0); + + if (offset != _offset) + { + if (_offset > (u64)MaxOffset) + { + cerr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << endl; + return false; + } + + + if (fseek(file, (OffsetType)_offset, SEEK_SET)) + { + cerr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << endl; + return false; + } + offset = _offset; + } + + if (length > MaxLength) + { + cerr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << endl; + return false; + } + + if (1 != fwrite(buffer, (LengthType)length, 1, file)) + { + cerr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << endl; + return false; + } + + offset += length; + + if (filesize < offset) + { + filesize = offset; + } + + return true; +} + +// Open the file + +bool DiskFile::Open(string _filename, u64 _filesize) +{ + assert(file == 0); + + filename = _filename; + filesize = _filesize; + + if (_filesize > (u64)MaxOffset) + { + cerr << "File size for " << _filename << " is too large." << endl; + return false; + } + + file = fopen(filename.c_str(), "rb"); + if (file == 0) + { + return false; + } + + offset = 0; + exists = true; + + return true; +} + +// Read some data from disk + +bool DiskFile::Read(u64 _offset, void *buffer, size_t length) +{ + assert(file != 0); + + if (offset != _offset) + { + if (_offset > (u64)MaxOffset) + { + cerr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl; + return false; + } + + + if (fseek(file, (OffsetType)_offset, SEEK_SET)) + { + cerr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl; + return false; + } + offset = _offset; + } + + if (length > MaxLength) + { + cerr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl; + return false; + } + + if (1 != fread(buffer, (LengthType)length, 1, file)) + { + cerr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl; + return false; + } + + offset += length; + + return true; +} + +void DiskFile::Close(void) +{ + if (file != 0) + { + fclose(file); + file = 0; + } +} + +// Attempt to get the full pathname of the file +string DiskFile::GetCanonicalPathname(string filename) +{ + // Is the supplied path already an absolute one + if (filename.size() == 0 || filename[0] == '/') + return filename; + + // Get the current directory + char curdir[1000]; + if (0 == getcwd(curdir, sizeof(curdir))) + { + return filename; + } + + + // Allocate a work buffer and copy the resulting full path into it. + char *work = new char[strlen(curdir) + filename.size() + 2]; + strcpy(work, curdir); + if (work[strlen(work)-1] != '/') + strcat(work, "/"); + strcat(work, filename.c_str()); + + char *in = work; + char *out = work; + + while (*in) + { + if (*in == '/') + { + if (in[1] == '.' && in[2] == '/') + { + // skip the input past /./ + in += 2; + } + else if (in[1] == '.' && in[2] == '.' && in[3] == '/') + { + // backtrack the output if /../ was found on the input + in += 3; + if (out > work) + { + do + { + out--; + } while (out > work && *out != '/'); + } + } + else + { + *out++ = *in++; + } + } + else + { + *out++ = *in++; + } + } + *out = 0; + + string result = work; + delete [] work; + + return result; +} + +list* DiskFile::FindFiles(string path, string wildcard) +{ + list *matches = new list; + + string::size_type where; + + if ((where = wildcard.find_first_of('*')) != string::npos || + (where = wildcard.find_first_of('?')) != string::npos) + { + string front = wildcard.substr(0, where); + bool multiple = wildcard[where] == '*'; + string back = wildcard.substr(where+1); + + DIR *dirp = opendir(path.c_str()); + if (dirp != 0) + { + struct dirent *d; + while ((d = readdir(dirp)) != 0) + { + string name = d->d_name; + + if (name == "." || name == "..") + continue; + + if (multiple) + { + if (name.size() >= wildcard.size() && + name.substr(0, where) == front && + name.substr(name.size()-back.size()) == back) + { + matches->push_back(path + name); + } + } + else + { + if (name.size() == wildcard.size()) + { + string::const_iterator pw = wildcard.begin(); + string::const_iterator pn = name.begin(); + while (pw != wildcard.end()) + { + if (*pw != '?' && *pw != *pn) + break; + ++pw; + ++pn; + } + + if (pw == wildcard.end()) + { + matches->push_back(path + name); + } + } + } + + } + closedir(dirp); + } + } + else + { + struct stat st; + string fn = path + wildcard; + if (stat(fn.c_str(), &st) == 0) + { + matches->push_back(path + wildcard); + } + } + + return matches; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#endif + + + + + + + + + + + + + + + + + + + + + + + +bool DiskFile::Open(void) +{ + string _filename = filename; + + return Open(_filename); +} + +bool DiskFile::Open(string _filename) +{ + return Open(_filename, GetFileSize(_filename)); +} + + + + + + + + + +// Delete the file + +bool DiskFile::Delete(void) +{ +#ifdef WIN32 + assert(hFile == INVALID_HANDLE_VALUE); +#else + assert(file == 0); +#endif + + if (filename.size() > 0 && 0 == unlink(filename.c_str())) + { + return true; + } + else + { + cerr << "Cannot delete " << filename << endl; + + return false; + } +} + + + + + + + + + +//string DiskFile::GetPathFromFilename(string filename) +//{ +// string::size_type where; +// +// if (string::npos != (where = filename.find_last_of('/')) || +// string::npos != (where = filename.find_last_of('\\'))) +// { +// return filename.substr(0, where+1); +// } +// else +// { +// return "." PATHSEP; +// } +//} + +void DiskFile::SplitFilename(string filename, string &path, string &name) +{ + string::size_type where; + + if (string::npos != (where = filename.find_last_of('/')) || + string::npos != (where = filename.find_last_of('\\'))) + { + path = filename.substr(0, where+1); + name = filename.substr(where+1); + } + else + { + path = "." PATHSEP; + name = filename; + } +} + +bool DiskFile::FileExists(string filename) +{ + struct stat st; + return ((0 == stat(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG))); +} + +u64 DiskFile::GetFileSize(string filename) +{ + struct stat st; + if ((0 == stat(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG))) + { + return st.st_size; + } + else + { + return 0; + } +} + + + +// Take a filename from a PAR2 file and replace any characters +// which would be illegal for a file on disk +string DiskFile::TranslateFilename(string filename) +{ + string result; + + string::iterator p = filename.begin(); + while (p != filename.end()) + { + unsigned char ch = *p; + + bool ok = true; +#ifdef WIN32 + if (ch < 32) + { + ok = false; + } + else + { + switch (ch) + { + case '"': + case '*': + case '/': + case ':': + case '<': + case '>': + case '?': + case '\\': + case '|': + ok = false; + } + } +#else + if (ch < 32) + { + ok = false; + } + else + { + switch (ch) + { + case '/': + ok = false; + } + } +#endif + + + if (ok) + { + result += ch; + } + else + { + // convert problem characters to hex + result += ((ch >> 4) < 10) ? (ch >> 4) + '0' : (ch >> 4) + 'A'-10; + result += ((ch & 0xf) < 10) ? (ch & 0xf) + '0' : (ch & 0xf) + 'A'-10; + } + + ++p; + } + + return result; +} + +bool DiskFile::Rename(void) +{ + char newname[_MAX_PATH+1]; + u32 index = 0; + + struct stat st; + + do + { + int length = snprintf(newname, _MAX_PATH, "%s.%d", filename.c_str(), ++index); + if (length < 0 || length >= _MAX_PATH) + { + cerr << filename << " cannot be renamed." << endl; + return false; + } + newname[length] = 0; + } while (stat(newname, &st) == 0); + + return Rename(newname); +} + +bool DiskFile::Rename(string _filename) +{ +#ifdef WIN32 + assert(hFile == INVALID_HANDLE_VALUE); +#else + assert(file == 0); +#endif + + if (::rename(filename.c_str(), _filename.c_str()) == 0) + { + filename = _filename; + + return true; + } + else + { + cerr << filename << " cannot be renamed to " << _filename << endl; + + return false; + } +} + +#ifdef WIN32 +string DiskFile::ErrorMessage(DWORD error) +{ + string result; + + LPVOID lpMsgBuf; + if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&lpMsgBuf, + 0, + NULL)) + { + result = (char*)lpMsgBuf; + LocalFree(lpMsgBuf); + } + else + { + char message[40]; + _snprintf(message, sizeof(message), "Unknown error code (%d)", error); + result = message; + } + + return result; +} +#endif + +DiskFileMap::DiskFileMap(void) +{ +} + +DiskFileMap::~DiskFileMap(void) +{ + map::iterator fi = diskfilemap.begin(); + while (fi != diskfilemap.end()) + { + delete (*fi).second; + + ++fi; + } +} + +bool DiskFileMap::Insert(DiskFile *diskfile) +{ + string filename = diskfile->FileName(); + assert(filename.length() != 0); + + pair::const_iterator,bool> location = diskfilemap.insert(pair(filename, diskfile)); + + return location.second; +} + +void DiskFileMap::Remove(DiskFile *diskfile) +{ + string filename = diskfile->FileName(); + assert(filename.length() != 0); + + diskfilemap.erase(filename); +} + +DiskFile* DiskFileMap::Find(string filename) const +{ + assert(filename.length() != 0); + + map::const_iterator f = diskfilemap.find(filename); + + return (f != diskfilemap.end()) ? f->second : 0; +} diff --git a/lib/par2/diskfile.h b/lib/par2/diskfile.h new file mode 100644 index 000000000..5e4d026d0 --- /dev/null +++ b/lib/par2/diskfile.h @@ -0,0 +1,125 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __DISKFILE_H__ +#define __DISKFILE_H__ + +// A disk file can be any type of file that par2cmdline needs +// to read or write data from or to. + +class DiskFile +{ +public: + DiskFile(void); + ~DiskFile(void); + + // Create a file and set its length + bool Create(string filename, u64 filesize); + + // Write some data to the file + bool Write(u64 offset, const void *buffer, size_t length); + + // Open the file + bool Open(void); + bool Open(string filename); + bool Open(string filename, u64 filesize); + + // Check to see if the file is open +#ifdef WIN32 + bool IsOpen(void) const {return hFile != INVALID_HANDLE_VALUE;} +#else + bool IsOpen(void) const {return file != 0;} +#endif + + // Read some data from the file + bool Read(u64 offset, void *buffer, size_t length); + + // Close the file + void Close(void); + + // Get the size of the file + u64 FileSize(void) const {return filesize;} + + // Get the name of the file + string FileName(void) const {return filename;} + + // Does the file exist + bool Exists(void) const {return exists;} + + // Rename the file + bool Rename(void); // Pick a filename automatically + bool Rename(string filename); + + // Delete the file + bool Delete(void); + +public: + static string GetCanonicalPathname(string filename); + + static void SplitFilename(string filename, string &path, string &name); + static string TranslateFilename(string filename); + + static bool FileExists(string filename); + static u64 GetFileSize(string filename); + + // Search the specified path for files which match the specified wildcard + // and return their names in a list. + static list* FindFiles(string path, string wildcard); + +protected: + string filename; + u64 filesize; + + // OS file handle +#ifdef WIN32 + HANDLE hFile; +#else + FILE *file; +#endif + + // Current offset within the file + u64 offset; + + // Does the file exist + bool exists; + +protected: +#ifdef WIN32 + static string ErrorMessage(DWORD error); +#endif +}; + +// This class keeps track of which DiskFile objects exist +// and which file on disk they are associated with. +// It is used to avoid a file being processed twice. +class DiskFileMap +{ +public: + DiskFileMap(void); + ~DiskFileMap(void); + + bool Insert(DiskFile *diskfile); + void Remove(DiskFile *diskfile); + DiskFile* Find(string filename) const; + +protected: + map diskfilemap; // Map from filename to DiskFile +}; + +#endif // __DISKFILE_H__ diff --git a/lib/par2/filechecksummer.cpp b/lib/par2/filechecksummer.cpp new file mode 100644 index 000000000..be4b90fd4 --- /dev/null +++ b/lib/par2/filechecksummer.cpp @@ -0,0 +1,255 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +// Construct the checksummer and allocate buffers + +FileCheckSummer::FileCheckSummer(DiskFile *_diskfile, + u64 _blocksize, + const u32 (&_windowtable)[256], + u32 _windowmask) +: diskfile(_diskfile) +, blocksize(_blocksize) +, windowtable(_windowtable) +, windowmask(_windowmask) +{ + buffer = new char[(size_t)blocksize*2]; + + filesize = diskfile->FileSize(); + + currentoffset = 0; +} + +FileCheckSummer::~FileCheckSummer(void) +{ + delete [] buffer; +} + +// Start reading the file at the beginning +bool FileCheckSummer::Start(void) +{ + currentoffset = readoffset = 0; + + tailpointer = outpointer = buffer; + inpointer = &buffer[blocksize]; + + // Fill the buffer with new data + if (!Fill()) + return false; + + // Compute the checksum for the block + checksum = ~0 ^ CRCUpdateBlock(~0, (size_t)blocksize, buffer); + + return true; +} + +// Jump ahead +bool FileCheckSummer::Jump(u64 distance) +{ + // Are we already at the end of the file + if (currentoffset >= filesize) + return false; + + // Special distances + if (distance == 0) + return false; + if (distance == 1) + return Step(); + + // Not allowed to jump more than one block + assert(distance <= blocksize); + if (distance > blocksize) + distance = blocksize; + + // Advance the current offset and check if we have reached the end of the file + currentoffset += distance; + if (currentoffset >= filesize) + { + currentoffset = filesize; + tailpointer = outpointer = buffer; + memset(buffer, 0, (size_t)blocksize); + checksum = 0; + + return true; + } + + // Move past the data being discarded + outpointer += distance; + assert(outpointer <= tailpointer); + + // Is there any data left in the buffer that we are keeping + size_t keep = tailpointer - outpointer; + if (keep > 0) + { + // Move it back to the start of the buffer + memmove(buffer, outpointer, keep); + tailpointer = &buffer[keep]; + } + else + { + tailpointer = buffer; + } + + outpointer = buffer; + inpointer = &buffer[blocksize]; + + if (!Fill()) + return false; + + // Compute the checksum for the block + checksum = ~0 ^ CRCUpdateBlock(~0, (size_t)blocksize, buffer); + + return true; +} + +// Fill the buffer from disk + +bool FileCheckSummer::Fill(void) +{ + // Have we already reached the end of the file + if (readoffset >= filesize) + return true; + + // How much data can we read into the buffer + size_t want = (size_t)min(filesize-readoffset, (u64)(&buffer[2*blocksize]-tailpointer)); + + if (want > 0) + { + // Read data + if (!diskfile->Read(readoffset, tailpointer, want)) + return false; + + UpdateHashes(readoffset, tailpointer, want); + readoffset += want; + tailpointer += want; + } + + // Did we fill the buffer + want = &buffer[2*blocksize] - tailpointer; + if (want > 0) + { + // Blank the rest of the buffer + memset(tailpointer, 0, want); + } + + return true; +} + +// Update the full file hash and the 16k hash using the new data +void FileCheckSummer::UpdateHashes(u64 offset, const void *buffer, size_t length) +{ + // Are we already beyond the first 16k + if (offset >= 16384) + { + contextfull.Update(buffer, length); + } + // Would we reach the 16k mark + else if (offset+length >= 16384) + { + // Finish the 16k hash + size_t first = (size_t)(16384-offset); + context16k.Update(buffer, first); + + // Continue with the full hash + contextfull = context16k; + + // Do we go beyond the 16k mark + if (offset+length > 16384) + { + contextfull.Update(&((const char*)buffer)[first], length-first); + } + } + else + { + context16k.Update(buffer, length); + } +} + +// Return the full file hash and the 16k file hash +void FileCheckSummer::GetFileHashes(MD5Hash &hashfull, MD5Hash &hash16k) const +{ + // Compute the hash of the first 16k + MD5Context context = context16k; + context.Final(hash16k); + + // Is the file smaller than 16k + if (filesize < 16384) + { + // The hashes are the same + hashfull = hash16k; + } + else + { + // Compute the hash of the full file + context = contextfull; + context.Final(hashfull); + } +} + +// Compute and return the current hash +MD5Hash FileCheckSummer::Hash(void) +{ + MD5Context context; + context.Update(outpointer, (size_t)blocksize); + + MD5Hash hash; + context.Final(hash); + + return hash; +} + +u32 FileCheckSummer::ShortChecksum(u64 blocklength) +{ + u32 crc = CRCUpdateBlock(~0, (size_t)blocklength, outpointer); + + if (blocksize > blocklength) + { + crc = CRCUpdateBlock(crc, (size_t)(blocksize-blocklength)); + } + + crc ^= ~0; + + return crc; +} + +MD5Hash FileCheckSummer::ShortHash(u64 blocklength) +{ + MD5Context context; + context.Update(outpointer, (size_t)blocklength); + + if (blocksize > blocklength) + { + context.Update((size_t)(blocksize-blocklength)); + } + + // Get the hash value + MD5Hash hash; + context.Final(hash); + + return hash; +} diff --git a/lib/par2/filechecksummer.h b/lib/par2/filechecksummer.h new file mode 100644 index 000000000..9919426b3 --- /dev/null +++ b/lib/par2/filechecksummer.h @@ -0,0 +1,178 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __FILECHECKSUMMER_H__ +#define __FILECHECKSUMMER_H__ + +// This source file defines the FileCheckSummer object which is used +// when scanning a data file to find blocks of undamaged data. +// +// The object uses a "window" into the data file and slides that window +// along the file computing the CRC of the data in that window as it +// goes. If the computed CRC matches the value for a block of data +// from a target data file, then the MD5 Hash value is also computed +// and compared with the value for that block of data. When a match +// has been confirmed, the object jumps forward to where the next +// block of data is expected to start. Whilst the file is being scanned +// the object also computes the MD5 Hash of the whole file and of +// the first 16k of the file for later tests. + +class FileCheckSummer +{ +public: + FileCheckSummer(DiskFile *diskfile, + u64 blocksize, + const u32 (&windowtable)[256], + u32 windowmask); + ~FileCheckSummer(void); + + // Start reading the file at the beginning + bool Start(void); + + // Jump ahead the specified distance + bool Jump(u64 distance); + + // Step forward one byte + bool Step(void); + + // Return the current checksum + u32 Checksum(void) const; + + // Compute and return the current hash + MD5Hash Hash(void); + + // Compute short values of checksum and hash + u32 ShortChecksum(u64 blocklength); + MD5Hash ShortHash(u64 blocklength); + + // Do we have less than a full block of data + bool ShortBlock(void) const; + u64 BlockLength(void) const; + + // Return the current file offset + u64 Offset(void) const; + + // Return the full file hash and the 16k file hash + void GetFileHashes(MD5Hash &hashfull, MD5Hash &hash16k) const; + + // Which disk file is this + const DiskFile* GetDiskFile(void) const {return diskfile;} + +protected: + DiskFile *diskfile; + u64 blocksize; + const u32 (&windowtable)[256]; + u32 windowmask; + + u64 filesize; + + u64 currentoffset; // file offset for current window position + char *buffer; // buffer for reading from the file + char *outpointer; // position in buffer of scan window + char *inpointer; // &outpointer[blocksize]; + char *tailpointer; // after last valid data in buffer + + // File offset for next read + u64 readoffset; + + // The current checksum + u32 checksum; + + // MD5 hash of whole file and of first 16k + MD5Context contextfull; + MD5Context context16k; + +protected: + //void ComputeCurrentCRC(void); + void UpdateHashes(u64 offset, const void *buffer, size_t length); + + //// Fill the buffers with more data from disk + bool Fill(void); +}; + +// Return the current checksum + +inline u32 FileCheckSummer::Checksum(void) const +{ + return checksum; +} + +// Return the current block length + +inline u64 FileCheckSummer::BlockLength(void) const +{ + return min(blocksize, filesize-currentoffset); +} + +// Return whether or not the current block is a short one. +inline bool FileCheckSummer::ShortBlock(void) const +{ + return BlockLength() < blocksize; +} + +// Return the current file offset +inline u64 FileCheckSummer::Offset(void) const +{ + return currentoffset; +} + +// Step forward one byte +inline bool FileCheckSummer::Step(void) +{ + // Are we already at the end of the file + if (currentoffset >= filesize) + return false; + + // Advance the file offset and check to see if + // we have reached the end of the file + if (++currentoffset >= filesize) + { + currentoffset = filesize; + tailpointer = outpointer = buffer; + memset(buffer, 0, (size_t)blocksize); + checksum = 0; + + return true; + } + + // Get the incoming and outgoing characters + char inch = *inpointer++; + char outch = *outpointer++; + + // Update the checksum + checksum = windowmask ^ CRCSlideChar(windowmask ^ checksum, inch, outch, windowtable); + + // Can the window slide further + if (outpointer < &buffer[blocksize]) + return true; + + assert(outpointer == &buffer[blocksize]); + + // Copy the data back to the beginning of the buffer + memmove(buffer, outpointer, (size_t)blocksize); + inpointer = outpointer; + outpointer = buffer; + tailpointer -= blocksize; + + // Fill the rest of the buffer + return Fill(); +} + + +#endif // __FILECHECKSUMMER_H__ diff --git a/lib/par2/galois.cpp b/lib/par2/galois.cpp new file mode 100644 index 000000000..a11d0913f --- /dev/null +++ b/lib/par2/galois.cpp @@ -0,0 +1,29 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + diff --git a/lib/par2/galois.h b/lib/par2/galois.h new file mode 100644 index 000000000..e826c04b7 --- /dev/null +++ b/lib/par2/galois.h @@ -0,0 +1,338 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __GALOIS_H__ +#define __GALOIS_H__ + +template class GaloisTable; +template class Galois; + +template class GaloisLongMultiplyTable; + +// This source file defines the Galois object for carrying out +// arithmetic in GF(2^16) using the generator 0x1100B. + +// Also defined are the GaloisTable object (which contains log and +// anti log tables for use in multiplication and division), and +// the GaloisLongMultiplyTable object (which contains tables for +// carrying out multiplation of 16-bit galois numbers 8 bits at a time). + +template +class GaloisTable +{ +public: + typedef valuetype ValueType; + + GaloisTable(void); + + enum + { + Bits = bits, + Count = 1< +class Galois +{ +public: + typedef valuetype ValueType; + + // Basic constructors + Galois(void) {}; + Galois(ValueType v); + + // Copy and assignment + Galois(const Galois &right) {value = right.value;} + Galois& operator = (const Galois &right) { value = right.value; return *this;} + + // Addition + Galois operator + (const Galois &right) const { return (value ^ right.value); } + Galois& operator += (const Galois &right) { value ^= right.value; return *this;} + + // Subtraction + Galois operator - (const Galois &right) const { return (value ^ right.value); } + Galois& operator -= (const Galois &right) { value ^= right.value; return *this;} + + // Multiplication + Galois operator * (const Galois &right) const; + Galois& operator *= (const Galois &right); + + // Division + Galois operator / (const Galois &right) const; + Galois& operator /= (const Galois &right); + + // Power + Galois pow(unsigned int right) const; + Galois operator ^ (unsigned int right) const; + Galois& operator ^= (unsigned int right); + + // Cast to value and value access + operator ValueType(void) const {return value;} + ValueType Value(void) const {return value;} + + // Direct log and antilog + ValueType Log(void) const; + ValueType ALog(void) const; + + enum + { + Bits = GaloisTable::Bits, + Count = GaloisTable::Count, + Limit = GaloisTable::Limit, + }; + +protected: + ValueType value; + + static GaloisTable table; +}; + +#ifdef LONGMULTIPLY +template +class GaloisLongMultiplyTable +{ +public: + GaloisLongMultiplyTable(void); + + typedef g G; + + enum + { + Bytes = ((G::Bits + 7) >> 3), + Count = ((Bytes * (Bytes+1)) / 2), + }; + + G tables[Count * 256 * 256]; +}; +#endif + +// Construct the log and antilog tables from the generator + +template +inline GaloisTable::GaloisTable(void) +{ + u32 b = 1; + + for (u32 l=0; l +GaloisTable Galois::table; + + +template +inline Galois::Galois(typename Galois::ValueType v) +{ + value = v; +} + +template +inline Galois Galois::operator * (const Galois &right) const +{ + if (value == 0 || right.value == 0) return 0; + unsigned int sum = table.log[value] + table.log[right.value]; + if (sum >= Limit) + { + return table.antilog[sum-Limit]; + } + else + { + return table.antilog[sum]; + } +} + +template +inline Galois& Galois::operator *= (const Galois &right) +{ + if (value == 0 || right.value == 0) + { + value = 0; + } + else + { + unsigned int sum = table.log[value] + table.log[right.value]; + if (sum >= Limit) + { + value = table.antilog[sum-Limit]; + } + else + { + value = table.antilog[sum]; + } + } + + return *this; +} + +template +inline Galois Galois::operator / (const Galois &right) const +{ + if (value == 0) return 0; + + assert(right.value != 0); + if (right.value == 0) {return 0;} // Division by 0! + + int sum = table.log[value] - table.log[right.value]; + if (sum < 0) + { + return table.antilog[sum+Limit]; + } + else + { + return table.antilog[sum]; + } +} + +template +inline Galois& Galois::operator /= (const Galois &right) +{ + if (value == 0) return *this; + + assert(right.value != 0); + if (right.value == 0) {return *this;} // Division by 0! + + int sum = table.log[value] - table.log[right.value]; + if (sum < 0) + { + value = table.antilog[sum+Limit]; + } + else + { + value = table.antilog[sum]; + } + + return *this; +} + +template +inline Galois Galois::pow(unsigned int right) const +{ + if (right == 0) return 1; + if (value == 0) return 0; + + unsigned int sum = table.log[value] * right; + + sum = (sum >> Bits) + (sum & Limit); + if (sum >= Limit) + { + return table.antilog[sum-Limit]; + } + else + { + return table.antilog[sum]; + } +} + +template +inline Galois Galois::operator ^ (unsigned int right) const +{ + if (right == 0) return 1; + if (value == 0) return 0; + + unsigned int sum = table.log[value] * right; + + sum = (sum >> Bits) + (sum & Limit); + if (sum >= Limit) + { + return table.antilog[sum-Limit]; + } + else + { + return table.antilog[sum]; + } +} + +template +inline Galois& Galois::operator ^= (unsigned int right) +{ + if (right == 1) {value = 1; return *this;} + if (value == 0) return *this; + + unsigned int sum = table.log[value] * right; + + sum = (sum >> Bits) + (sum & Limit); + if (sum >= Limit) + { + value = table.antilog[sum-Limit]; + } + else + { + value = table.antilog[sum]; + } + + return *this; +} + +template +inline valuetype Galois::Log(void) const +{ + return table.log[value]; +} + +template +inline valuetype Galois::ALog(void) const +{ + return table.antilog[value]; +} + +#ifdef LONGMULTIPLY +template +inline GaloisLongMultiplyTable::GaloisLongMultiplyTable(void) +{ + G *table = tables; + + for (unsigned int i=0; i Galois8; +typedef Galois<16,0x1100B,u16> Galois16; + +#endif // __GALOIS_H__ diff --git a/lib/par2/letype.h b/lib/par2/letype.h new file mode 100644 index 000000000..9ab5cd54d --- /dev/null +++ b/lib/par2/letype.h @@ -0,0 +1,120 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __LETYPE_H__ +#define __LETYPE_H__ + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +typedef u16 leu16; +typedef u32 leu32; +typedef u64 leu64; + +#else + +struct leu16 +{ + leu16& operator=(const u16 &other); + + operator u16(void) const; + + u16 value; +}; + +inline leu16& leu16::operator=(const u16 &other) +{ + ((unsigned char*)&value)[0] = (unsigned char)((other >> 0) & 0xff); + ((unsigned char*)&value)[1] = (unsigned char)((other >> 8) & 0xff); + + return *this; +} + +inline leu16::operator u16(void) const +{ + return ((unsigned char*)&value)[0] << 0 | + ((unsigned char*)&value)[1] << 8; +} + + +struct leu32 +{ + leu32& operator=(const u32 &other); + + operator u32(void) const; + + u32 value; +}; + +inline leu32& leu32::operator=(const u32 &other) +{ + ((unsigned char*)&value)[0] = (unsigned char)((other >> 0) & 0xff); + ((unsigned char*)&value)[1] = (unsigned char)((other >> 8) & 0xff); + ((unsigned char*)&value)[2] = (unsigned char)((other >> 16) & 0xff); + ((unsigned char*)&value)[3] = (unsigned char)((other >> 24) & 0xff); + + return *this; +} + +inline leu32::operator u32(void) const +{ + return ((unsigned char*)&value)[0] << 0 | + ((unsigned char*)&value)[1] << 8 | + ((unsigned char*)&value)[2] << 16 | + ((unsigned char*)&value)[3] << 24; +} + + +struct leu64 +{ + leu64& operator=(const u64 &other); + + operator u64(void) const; + + u64 value; +}; + +inline leu64& leu64::operator=(const u64 &other) +{ + ((unsigned char*)&value)[0] = (unsigned char)((other >> 0) & 0xff); + ((unsigned char*)&value)[1] = (unsigned char)((other >> 8) & 0xff); + ((unsigned char*)&value)[2] = (unsigned char)((other >> 16) & 0xff); + ((unsigned char*)&value)[3] = (unsigned char)((other >> 24) & 0xff); + ((unsigned char*)&value)[4] = (unsigned char)((other >> 32) & 0xff); + ((unsigned char*)&value)[5] = (unsigned char)((other >> 40) & 0xff); + ((unsigned char*)&value)[6] = (unsigned char)((other >> 48) & 0xff); + ((unsigned char*)&value)[7] = (unsigned char)((other >> 56) & 0xff); + + return *this; +} + +inline leu64::operator u64(void) const +{ + return (u64)(((unsigned char*)&value)[0]) << 0 | + (u64)(((unsigned char*)&value)[1]) << 8 | + (u64)(((unsigned char*)&value)[2]) << 16 | + (u64)(((unsigned char*)&value)[3]) << 24 | + (u64)(((unsigned char*)&value)[4]) << 32 | + (u64)(((unsigned char*)&value)[5]) << 40 | + (u64)(((unsigned char*)&value)[6]) << 48 | + (u64)(((unsigned char*)&value)[7]) << 56; +} + +#endif + +#endif // __LETYPE_H__ diff --git a/lib/par2/mainpacket.cpp b/lib/par2/mainpacket.cpp new file mode 100644 index 000000000..3c379d9b3 --- /dev/null +++ b/lib/par2/mainpacket.cpp @@ -0,0 +1,130 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +// Construct the main packet from the source files and the block size + +bool MainPacket::Create(vector &sourcefiles, u64 _blocksize) +{ + recoverablefilecount = totalfilecount =(u32)sourcefiles.size(); + blocksize = _blocksize; + + // Allocate memory for the main packet with enough fileid entries + MAINPACKET *packet = (MAINPACKET *)AllocatePacket(sizeof(MAINPACKET) + totalfilecount * sizeof(MD5Hash)); + + // Record the details we already know in the packet + packet->header.magic = packet_magic; + packet->header.length = packetlength; + //packet->header.hash; // Compute shortly + //packet->header.setid; // Compute shortly + packet->header.type = mainpacket_type; + + packet->blocksize = _blocksize; + packet->recoverablefilecount = totalfilecount; + //packet->fileid; // Compute shortly + + // Sort the source files according to their fileid values + if (totalfilecount > 1) + { + sort(sourcefiles.begin(), sourcefiles.end(), Par2CreatorSourceFile::CompareLess); + } + + // Store the fileid values in the main packet + vector::const_iterator sourcefile; + MD5Hash *hash; + for ((sourcefile=sourcefiles.begin()),(hash=packet->fileid); + sourcefile!=sourcefiles.end(); + ++sourcefile, ++hash) + { + *hash = (*sourcefile)->FileId(); + } + + // Compute the set_id_hash + MD5Context setidcontext; + setidcontext.Update(&packet->blocksize, packetlength - offsetof(MAINPACKET, blocksize)); + setidcontext.Final(packet->header.setid); + + // Compute the packet_hash + MD5Context packetcontext; + packetcontext.Update(&packet->header.setid, packetlength - offsetof(MAINPACKET, header.setid)); + packetcontext.Final(packet->header.hash); + + return true; +} + +// Load a main packet from a specified file + +bool MainPacket::Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + // Is the packet large enough + if (header.length < sizeof(MAINPACKET)) + { + return false; + } + + // Is there a whole number of fileid values + if (0 < (header.length - sizeof(MAINPACKET)) % sizeof(MD5Hash)) + { + return false; + } + + // Is the packet too large + if (header.length > sizeof(MAINPACKET) + 32768 * sizeof(MD5Hash)) + { + return false; + } + + // Compute the total number of entries in the fileid array + totalfilecount = (u32)(((size_t)header.length - sizeof(MAINPACKET)) / sizeof(MD5Hash)); + + MAINPACKET *packet = (MAINPACKET *)AllocatePacket((size_t)header.length); + + packet->header = header; + + // Read the rest of the packet from disk + if (!diskfile->Read(offset + sizeof(PACKET_HEADER), + &packet->blocksize, + (size_t)packet->header.length - sizeof(PACKET_HEADER))) + return false; + + // Does the packet have enough fileid values + recoverablefilecount = packet->recoverablefilecount; + if (recoverablefilecount > totalfilecount) + { + return false; + } + + // Is the block size valid + blocksize = packet->blocksize; + if (blocksize == 0 || (blocksize & 3) != 0) + { + return false; + } + + return true; +} diff --git a/lib/par2/mainpacket.h b/lib/par2/mainpacket.h new file mode 100644 index 000000000..165a69aca --- /dev/null +++ b/lib/par2/mainpacket.h @@ -0,0 +1,105 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __MAINPACKET_H__ +#define __MAINPACKET_H__ + +// The main packet ties all other critical packets together. +// It specifies the block size to use for both verification of +// files and for the Reed Solomon computation. +// It also specifies how many of the source files are repairable +// and in what order they should be processed. + +class MainPacket : public CriticalPacket +{ +public: + // Construct the packet + MainPacket(void) {}; + ~MainPacket(void) {}; + +public: + // Construct the main packet from the source file list and block size. + // "sourcefiles" will be sorted base on their FileId value. + bool Create(vector &sourcefiles, + u64 _blocksize); + + // Load a main packet from a specified file + bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + +public: + // Get the set id. + const MD5Hash& SetId(void) const; + + // Get the block size. + u64 BlockSize(void) const; + + // Get the file counts. + u32 RecoverableFileCount(void) const; + u32 TotalFileCount(void) const; + + // Get the fileid of one file + const MD5Hash& FileId(u32 filenumber) const; + +protected: + u64 blocksize; + u32 totalfilecount; + u32 recoverablefilecount; +}; + +// Get the data block size +inline u64 MainPacket::BlockSize(void) const +{ + assert(packetdata != 0); + + return blocksize; +} + +// Get the number of recoverable files +inline u32 MainPacket::RecoverableFileCount(void) const +{ + assert(packetdata != 0); + + return recoverablefilecount; +} + +// Get the total number of files +inline u32 MainPacket::TotalFileCount(void) const +{ + assert(packetdata != 0); + + return totalfilecount; +} + +// Get the file id hash of one of the files +inline const MD5Hash& MainPacket::FileId(u32 filenumber) const +{ + assert(packetdata != 0); + assert(filenumberfileid()[filenumber]; + return ((const MAINPACKET*)packetdata)->fileid[filenumber]; +} + +inline const MD5Hash& MainPacket::SetId(void) const +{ + return ((const MAINPACKET*)packetdata)->header.setid; +} + + +#endif // __MAINPACKET_H__ diff --git a/lib/par2/md5.cpp b/lib/par2/md5.cpp new file mode 100644 index 000000000..555f19f70 --- /dev/null +++ b/lib/par2/md5.cpp @@ -0,0 +1,351 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +// Convert hash values to hex + +ostream& operator<<(ostream &result, const MD5Hash &h) +{ + char buffer[33]; + + sprintf(buffer, + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + h.hash[15], h.hash[14], h.hash[13], h.hash[12], + h.hash[11], h.hash[10], h.hash[9], h.hash[8], + h.hash[7], h.hash[6], h.hash[5], h.hash[4], + h.hash[3], h.hash[2], h.hash[1], h.hash[0]); + + return result << buffer; +} + +string MD5Hash::print(void) const +{ + char buffer[33]; + + sprintf(buffer, + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + hash[15], hash[14], hash[13], hash[12], + hash[11], hash[10], hash[9], hash[8], + hash[7], hash[6], hash[5], hash[4], + hash[3], hash[2], hash[1], hash[0]); + + return buffer; +} + +MD5State::MD5State(void) +{ + Reset(); +} + +// Initialise the 16 byte state +void MD5State::Reset(void) +{ + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; +} + +// Update the state using 64 bytes of new data +void MD5State::UpdateState(const u32 (&block)[16]) +{ + // Primitive operations +#define F1(x,y,z) ( ((x) & (y)) | ((~(x)) & (z)) ) +#define F2(x,y,z) ( ((x) & (z)) | ((~(z)) & (y)) ) +#define F3(x,y,z) ( (x) ^ (y) ^ (z) ) +#define F4(x,y,z) ( (y) ^ ( (x) | ~(z) ) ) + +// The first version of ROL does not work on an Alpha CPU! +//#define ROL(x,y) ( ((x) << (y)) | (((unsigned int)x) >> (32-y)) ) +#define ROL(x,y) ( ((x) << (y)) | (((x) >> (32-y)) & ((1< 0) + { + size_t size = min(buffersize-used, length); + Update(wordblock, size); + length -= size; + } + + // Update as many whole buffers as possible + while (length >= buffersize) + { + Update(wordblock, buffersize); + + length -= buffersize; + } + + // Update any remainder + if (length > 0) + { + Update(wordblock, length); + } +} + +// Update using data from a buffer +void MD5Context::Update(const void *buffer, size_t length) +{ + const unsigned char *current = (const unsigned char *)buffer; + + // Update the total amount of data processed. + bytes += length; + + // Process any whole blocks + while (used + length >= buffersize) + { + size_t have = buffersize - used; + + memcpy(&block[used], current, have); + + current += have; + length -= have; + + u32 wordblock[16]; + for (int i=0; i<16; i++) + { + // Convert source data from little endian format to internal format if different + wordblock[i] = ( ((u32)block[i*4+3]) << 24 ) | + ( ((u32)block[i*4+2]) << 16 ) | + ( ((u32)block[i*4+1]) << 8 ) | + ( ((u32)block[i*4+0]) << 0 ); + } + + MD5State::UpdateState(wordblock); + + used = 0; + } + + // Store any remainder + if (length > 0) + { + memcpy(&block[used], current, length); + used += length; + } +} + +// Finalise the computation and extract the Hash value +void MD5Context::Final(MD5Hash &output) +{ + // Temporary work buffer + u8 buffer[64]; + + // How many bits were processed + u64 bits = bytes << 3; + + // Pad as much as needed so that there are exactly 8 bytes needed to fill the buffer + size_t padding; + if (used >= buffersize-8) + { + padding = buffersize-8 + buffersize - used; + } + else + { + padding = buffersize-8 - used; + } + memset(buffer, 0, padding); + buffer[0] = 0x80; + Update(buffer, padding); + + // Pad with an additional 8 bytes containing the bit count in little endian format + buffer[7] = (unsigned char)((bits >> 56) & 0xFF); + buffer[6] = (unsigned char)((bits >> 48) & 0xFF); + buffer[5] = (unsigned char)((bits >> 40) & 0xFF); + buffer[4] = (unsigned char)((bits >> 32) & 0xFF); + buffer[3] = (unsigned char)((bits >> 24) & 0xFF); + buffer[2] = (unsigned char)((bits >> 16) & 0xFF); + buffer[1] = (unsigned char)((bits >> 8) & 0xFF); + buffer[0] = (unsigned char)((bits >> 0) & 0xFF); + Update(buffer, 8); + + for (int i = 0; i < 4; i++) + { + // Read out the state and convert it from internal format to little endian format + output.hash[4*i+3] = (u8)((MD5State::state[i] >> 24) & 0xFF); + output.hash[4*i+2] = (u8)((MD5State::state[i] >> 16) & 0xFF); + output.hash[4*i+1] = (u8)((MD5State::state[i] >> 8) & 0xFF); + output.hash[4*i+0] = (u8)((MD5State::state[i] >> 0) & 0xFF); + } +} + +// Return the Hash value +MD5Hash MD5Context::Hash(void) const +{ + MD5Hash output; + + for (unsigned int i = 0; i < 4; i++) + { + // Read out the state and convert it from internal format to little endian format + output.hash[4*i+3] = (unsigned char)((MD5State::state[i] >> 24) & 0xFF); + output.hash[4*i+2] = (unsigned char)((MD5State::state[i] >> 16) & 0xFF); + output.hash[4*i+1] = (unsigned char)((MD5State::state[i] >> 8) & 0xFF); + output.hash[4*i+0] = (unsigned char)((MD5State::state[i] >> 0) & 0xFF); + } + + return output; +} + +ostream& operator<<(ostream &result, const MD5Context &c) +{ + char buffer[50]; + + sprintf(buffer, + "%08X%08X%08X%08X:%08X%08X", + c.state[3],c.state[2],c.state[1],c.state[0], + (u32)((c.bytes >> 32) & 0xffffffff), + (u32)(c.bytes & 0xffffffff)); + + return result << buffer; +} + +string MD5Context::print(void) const +{ + char buffer[50]; + + sprintf(buffer, + "%08X%08X%08X%08X:%08X%08X", + state[3],state[2],state[1],state[0], + (u32)((bytes >> 32) & 0xffffffff), + (u32)(bytes & 0xffffffff)); + + return buffer; +} + diff --git a/lib/par2/md5.h b/lib/par2/md5.h new file mode 100644 index 000000000..3c374a6e6 --- /dev/null +++ b/lib/par2/md5.h @@ -0,0 +1,153 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __MD5_H__ +#define __MD5_H__ + +#ifdef WIN32 +#pragma pack(push, 1) +#define PACKED +#else +#define PACKED __attribute__ ((packed)) +#endif + +// This file defines the MD5Hash and MD5Context objects which are used +// to compute and manipulate the MD5 Hash values for a block of data. + +// Usage: +// +// MD5Context context; +// context.Update(buffer, length); +// +// MD5Hash hash; +// context.Final(hash); + + + +// MD5 Hash value + +struct MD5Hash; +ostream& operator<<(ostream &s, const MD5Hash &hash); + +struct MD5Hash +{ + // Comparison operators + bool operator==(const MD5Hash &other) const; + bool operator!=(const MD5Hash &other) const; + + bool operator<(const MD5Hash &other) const; + bool operator>=(const MD5Hash &other) const; + bool operator>(const MD5Hash &other) const; + bool operator<=(const MD5Hash &other) const; + + // Convert value to hex + friend ostream& operator<<(ostream &s, const MD5Hash &hash); + string print(void) const; + + u8 hash[16]; // 16 byte MD5 Hash value +} PACKED; + +// Intermediate computation state + +class MD5State +{ +public: + MD5State(void); + void Reset(void); + +public: + void UpdateState(const u32 (&block)[16]); + +protected: + u32 state[4]; // 16 byte MD5 computation state +}; + +// MD5 computation context with 64 byte buffer + +class MD5Context : public MD5State +{ +public: + MD5Context(void); + ~MD5Context(void) {}; + void Reset(void); + + // Process data from a buffer + void Update(const void *buffer, size_t length); + + // Process 0 bytes + void Update(size_t length); + + // Compute the final hash value + void Final(MD5Hash &output); + + // Get the Hash value and the total number of bytes processed. + MD5Hash Hash(void) const; + u64 Bytes(void) const {return bytes;} + + friend ostream& operator<<(ostream &s, const MD5Context &context); + string print(void) const; + +protected: + enum {buffersize = 64}; + unsigned char block[buffersize]; + size_t used; + + u64 bytes; +}; + +// Compare hash values + +inline bool MD5Hash::operator==(const MD5Hash &other) const +{ + return (0==memcmp(&hash, &other.hash, sizeof(hash))); +} +inline bool MD5Hash::operator!=(const MD5Hash &other) const +{ + return !operator==(other); +} + +inline bool MD5Hash::operator<(const MD5Hash &other) const +{ + size_t index = 15; + while (index > 0 && hash[index] == other.hash[index]) + { + index--; + } + + return hash[index] < other.hash[index]; +} +inline bool MD5Hash::operator>=(const MD5Hash &other) const +{ + return !operator<(other); +} +inline bool MD5Hash::operator>(const MD5Hash &other) const +{ + return other.operator<(*this); +} +inline bool MD5Hash::operator<=(const MD5Hash &other) const +{ + return !other.operator<(*this); +} + +#ifdef WIN32 +#pragma pack(pop) +#endif +#undef PACKED + +#endif // __MD5_H__ diff --git a/lib/par2/par2cmdline.h b/lib/par2/par2cmdline.h new file mode 100644 index 000000000..2a32d509c --- /dev/null +++ b/lib/par2/par2cmdline.h @@ -0,0 +1,301 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __PARCMDLINE_H__ +#define __PARCMDLINE_H__ + +#ifdef WIN32 +// Windows includes +#define WIN32_LEAN_AND_MEAN +#include + +// System includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define snprintf _snprintf +#define stat _stat + +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __PDP_ENDIAN 3412 + +#define __BYTE_ORDER __LITTLE_ENDIAN + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; +typedef unsigned __int64 u64; + +#ifndef _SIZE_T_DEFINED +# ifdef _WIN64 +typedef unsigned __int64 size_t; +# else +typedef unsigned int size_t; +# endif +# define _SIZE_T_DEFINED +#endif + + +#else // WIN32 +#ifdef HAVE_CONFIG_H + +#include "config.h" + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_STDIO_H +# include +#endif + +#if HAVE_DIRENT_H +# include +# define NAMELEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMELEN(dirent) (dirent)->d_namelen +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +#if STDC_HEADERS +# include +#else +# if !HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr(), *strrchr(); +# if !HAVE_MEMCPY +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memove(d, s, n) bcopy((s), (d), (n)) +# endif +#endif + +#if HAVE_MEMORY_H +# include +#endif + +#if !HAVE_STRICMP +# if HAVE_STRCASECMP +# define stricmp strcasecmp +# endif +#endif + +#if HAVE_INTTYPES_H +# include +#endif + +#if HAVE_STDINT_H +# include +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +#else +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#endif + +#if HAVE_SYS_STAT_H +# include +#endif + +#if HAVE_SYS_TYPES_H +# include +#endif + +#if HAVE_UNISTD_H +# include +#endif + +#define _MAX_PATH 255 + +#if HAVE_ENDIAN_H +# include +# ifndef __LITTLE_ENDIAN +# ifdef _LITTLE_ENDIAN +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# define __BIG_ENDIAN _BIG_ENDIAN +# define __PDP_ENDIAN _PDP_ENDIAN +# else +# error does not define __LITTLE_ENDIAN etc. +# endif +# endif +#else +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __PDP_ENDIAN 3412 +# if WORDS_BIGENDIAN +# define __BYTE_ORDER __BIG_ENDIAN +# else +# define __BYTE_ORDER __LITTLE_ENDIAN +# endif +#endif + +#else // HAVE_CONFIG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define _MAX_PATH 255 +#define stricmp strcasecmp +#define _stat stat + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +#endif +#endif + +#ifdef WIN32 +#define PATHSEP "\\" +#define ALTPATHSEP "/" +#else +#define PATHSEP "/" +#define ALTPATHSEP "\\" +#endif + +// Return type of par2cmdline +typedef enum Result +{ + eSuccess = 0, + + eRepairPossible = 1, // Data files are damaged and there is + // enough recovery data available to + // repair them. + + eRepairNotPossible = 2, // Data files are damaged and there is + // insufficient recovery data available + // to be able to repair them. + + eInvalidCommandLineArguments = 3, // There was something wrong with the + // command line arguments + + eInsufficientCriticalData = 4, // The PAR2 files did not contain sufficient + // information about the data files to be able + // to verify them. + + eRepairFailed = 5, // Repair completed but the data files + // still appear to be damaged. + + + eFileIOError = 6, // An error occured when accessing files + eLogicError = 7, // In internal error occurred + eMemoryError = 8, // Out of memory + +} Result; + +#define LONGMULTIPLY + +// STL includes +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace std; + +#ifdef offsetof +#undef offsetof +#endif +#define offsetof(TYPE, MEMBER) ((size_t) ((char*)(&((TYPE *)1)->MEMBER) - (char*)1)) + +#include "letype.h" +// par2cmdline includes + +#include "galois.h" +#include "crc.h" +#include "md5.h" +#include "par2fileformat.h" +#include "commandline.h" +#include "reedsolomon.h" + +#include "diskfile.h" +#include "datablock.h" + +#include "criticalpacket.h" +#include "par2creatorsourcefile.h" + +#include "mainpacket.h" +#include "creatorpacket.h" +#include "descriptionpacket.h" +#include "verificationpacket.h" +#include "recoverypacket.h" + +#include "par2repairersourcefile.h" + +#include "filechecksummer.h" +#include "verificationhashtable.h" + +//#include "par2creator.h" +#include "par2repairer.h" + +//#include "par1fileformat.h" +//#include "par1repairersourcefile.h" +//#include "par1repairer.h" + +// Heap checking +#ifdef _MSC_VER +#define _CRTDBG_MAP_ALLOC +#include +#define DEBUG_NEW new(_NORMAL_BLOCK, THIS_FILE, __LINE__) +#endif + + +#endif // __PARCMDLINE_H__ + diff --git a/lib/par2/par2creatorsourcefile.cpp b/lib/par2/par2creatorsourcefile.cpp new file mode 100644 index 000000000..15cb78ef5 --- /dev/null +++ b/lib/par2/par2creatorsourcefile.cpp @@ -0,0 +1,342 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +Par2CreatorSourceFile::Par2CreatorSourceFile(void) +{ + descriptionpacket = 0; + verificationpacket = 0; + diskfile = 0; + blockcount = 0; + //diskfilename; + //parfilename; + contextfull = 0; +} + +Par2CreatorSourceFile::~Par2CreatorSourceFile(void) +{ + delete descriptionpacket; + delete verificationpacket; + delete diskfile; + delete contextfull; +} + +// Open the source file, compute the MD5 Hash of the whole file and the first +// 16k of the file, and then compute the FileId and store the results +// in a file description packet and a file verification packet. + +bool Par2CreatorSourceFile::Open(CommandLine::NoiseLevel noiselevel, const CommandLine::ExtraFile &extrafile, u64 blocksize, bool deferhashcomputation) +{ + // Get the filename and filesize + diskfilename = extrafile.FileName(); + filesize = extrafile.FileSize(); + + // Work out how many blocks the file will be sliced into + blockcount = (u32)((filesize + blocksize-1) / blocksize); + + // Determine what filename to record in the PAR2 files + string::size_type where; + if (string::npos != (where = diskfilename.find_last_of('\\')) || + string::npos != (where = diskfilename.find_last_of('/'))) + { + parfilename = diskfilename.substr(where+1); + } + else + { + parfilename = diskfilename; + } + + // Create the Description and Verification packets + descriptionpacket = new DescriptionPacket; + descriptionpacket->Create(parfilename, filesize); + + verificationpacket = new VerificationPacket; + verificationpacket->Create(blockcount); + + // Create the diskfile object + diskfile = new DiskFile; + + // Open the source file + if (!diskfile->Open(diskfilename, filesize)) + return false; + + // Do we want to defer the computation of the full file hash, and + // the block crc and hashes. This is only permitted if there + // is sufficient memory available to create all recovery blocks + // in one pass of the source files (i.e. chunksize == blocksize) + if (deferhashcomputation) + { + // Initialise a buffer to read the first 16k of the source file + size_t buffersize = 16 * 1024; + if (buffersize > filesize) + buffersize = (size_t)filesize; + char *buffer = new char[buffersize]; + + // Read the data from the file + if (!diskfile->Read(0, buffer, buffersize)) + { + diskfile->Close(); + delete [] buffer; + return false; + } + + // Compute the hash of the data read from the file + MD5Context context; + context.Update(buffer, buffersize); + delete [] buffer; + MD5Hash hash; + context.Final(hash); + + // Store the hash in the descriptionpacket and compute the file id + descriptionpacket->Hash16k(hash); + + // Compute the fileid and store it in the verification packet. + descriptionpacket->ComputeFileId(); + verificationpacket->FileId(descriptionpacket->FileId()); + + // Allocate an MD5 context for computing the file hash + // during the recovery data generation phase + contextfull = new MD5Context; + } + else + { + // Initialise a buffer to read the source file + size_t buffersize = 1024*1024; + if (buffersize > min(blocksize,filesize)) + buffersize = (size_t)min(blocksize,filesize); + char *buffer = new char[buffersize]; + + // Get ready to start reading source file to compute the hashes and crcs + u64 offset = 0; + u32 blocknumber = 0; + u64 need = blocksize; + + MD5Context filecontext; + MD5Context blockcontext; + u32 blockcrc = 0; + + // Whilst we have not reached the end of the file + while (offset < filesize) + { + // Work out how much we can read + size_t want = (size_t)min(filesize-offset, (u64)buffersize); + + // Read some data from the file into the buffer + if (!diskfile->Read(offset, buffer, want)) + { + diskfile->Close(); + delete [] buffer; + return false; + } + + // If the new data passes the 16k boundary, compute the 16k hash for the file + if (offset < 16384 && offset + want >= 16384) + { + filecontext.Update(buffer, (size_t)(16384-offset)); + + MD5Context temp = filecontext; + MD5Hash hash; + temp.Final(hash); + + // Store the 16k hash in the file description packet + descriptionpacket->Hash16k(hash); + + if (offset + want > 16384) + { + filecontext.Update(&buffer[16384-offset], (size_t)(offset+want)-16384); + } + } + else + { + filecontext.Update(buffer, want); + } + + // Get ready to update block hashes and crcs + u32 used = 0; + + // Whilst we have not used all of the data we just read + while (used < want) + { + // How much of it can we use for the current block + u32 use = (u32)min(need, (u64)(want-used)); + + blockcrc = ~0 ^ CRCUpdateBlock(~0 ^ blockcrc, use, &buffer[used]); + blockcontext.Update(&buffer[used], use); + + used += use; + need -= use; + + // Have we finished the current block + if (need == 0) + { + MD5Hash blockhash; + blockcontext.Final(blockhash); + + // Store the block hash and block crc in the file verification packet. + verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc); + + blocknumber++; + + // More blocks + if (blocknumber < blockcount) + { + need = blocksize; + + blockcontext.Reset(); + blockcrc = 0; + } + } + } + + if (noiselevel > CommandLine::nlQuiet) + { + // Display progress + u32 oldfraction = (u32)(1000 * offset / filesize); + offset += want; + u32 newfraction = (u32)(1000 * offset / filesize); + if (oldfraction != newfraction) + { + cout << newfraction/10 << '.' << newfraction%10 << "%\r" << flush; + } + } + } + + // Did we finish the last block + if (need > 0) + { + blockcrc = ~0 ^ CRCUpdateBlock(~0 ^ blockcrc, (size_t)need); + blockcontext.Update((size_t)need); + + MD5Hash blockhash; + blockcontext.Final(blockhash); + + // Store the block hash and block crc in the file verification packet. + verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc); + + blocknumber++; + + need = 0; + } + + // Finish computing the file hash. + MD5Hash filehash; + filecontext.Final(filehash); + + // Store the file hash in the file description packet. + descriptionpacket->HashFull(filehash); + + // Did we compute the 16k hash. + if (offset < 16384) + { + // Store the 16k hash in the file description packet. + descriptionpacket->Hash16k(filehash); + } + + delete [] buffer; + + // Compute the fileid and store it in the verification packet. + descriptionpacket->ComputeFileId(); + verificationpacket->FileId(descriptionpacket->FileId()); + } + + return true; +} + +void Par2CreatorSourceFile::Close(void) +{ + diskfile->Close(); +} + + +void Par2CreatorSourceFile::RecordCriticalPackets(list &criticalpackets) +{ + // Add the file description packet and file verification packet to + // the critical packet list. + criticalpackets.push_back(descriptionpacket); + criticalpackets.push_back(verificationpacket); +} + +bool Par2CreatorSourceFile::CompareLess(const Par2CreatorSourceFile* const &left, const Par2CreatorSourceFile* const &right) +{ + // Sort source files based on fileid + return left->descriptionpacket->FileId() < right->descriptionpacket->FileId(); +} + +const MD5Hash& Par2CreatorSourceFile::FileId(void) const +{ + // Get the file id hash + return descriptionpacket->FileId(); +} + +void Par2CreatorSourceFile::InitialiseSourceBlocks(vector::iterator &sourceblock, u64 blocksize) +{ + for (u32 blocknum=0; blocknumSetLocation(diskfile, // file + blocknum * blocksize); // offset + sourceblock->SetLength(min(blocksize, filesize - (u64)blocknum * blocksize)); // length + sourceblock++; + } +} + +void Par2CreatorSourceFile::UpdateHashes(u32 blocknumber, const void *buffer, size_t length) +{ + // Compute the crc and hash of the data + u32 blockcrc = ~0 ^ CRCUpdateBlock(~0, length, buffer); + MD5Context blockcontext; + blockcontext.Update(buffer, length); + MD5Hash blockhash; + blockcontext.Final(blockhash); + + // Store the results in the verification packet + verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc); + + + // Update the full file hash, but don't go beyond the end of the file + if (length > filesize - blocknumber * length) + { + length = (size_t)(filesize - blocknumber * (u64)length); + } + + assert(contextfull != 0); + + contextfull->Update(buffer, length); +} + +void Par2CreatorSourceFile::FinishHashes(void) +{ + assert(contextfull != 0); + + // Finish computation of the full file hash + MD5Hash hash; + contextfull->Final(hash); + + // Store it in the description packet + descriptionpacket->HashFull(hash); +} diff --git a/lib/par2/par2creatorsourcefile.h b/lib/par2/par2creatorsourcefile.h new file mode 100644 index 000000000..4e810a5ad --- /dev/null +++ b/lib/par2/par2creatorsourcefile.h @@ -0,0 +1,81 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __PAR2CREATORSOURCEFILE_H__ +#define __PAR2CREATORSOURCEFILE_H__ + +class DescriptionPacket; +class VerificationPacket; +class DiskFile; + +// The Par2CreatorSourceFile contains the file verification and file description +// packet for one source file. + +class Par2CreatorSourceFile +{ +private: + // Don't permit copying or assignment + Par2CreatorSourceFile(const Par2CreatorSourceFile &other); + Par2CreatorSourceFile& operator=(const Par2CreatorSourceFile &other); + +public: + Par2CreatorSourceFile(void); + ~Par2CreatorSourceFile(void); + + // Open the source file and compute the Hashes and CRCs. + bool Open(CommandLine::NoiseLevel noiselevel, const CommandLine::ExtraFile &extrafile, u64 blocksize, bool deferhashcomputation); + void Close(void); + + // Recover the file description and file verification packets + // in the critical packet list. + void RecordCriticalPackets(list &criticalpackets); + + // Get the file id + const MD5Hash& FileId(void) const; + + // Sort source files based on the file id hash + static bool CompareLess(const Par2CreatorSourceFile* const &left, const Par2CreatorSourceFile* const &right); + + // Allocate the appropriate number of source blocks to the source file + void InitialiseSourceBlocks(vector::iterator &sourceblock, u64 blocksize); + + // Update the file hash and the block crc and hashes + void UpdateHashes(u32 blocknumber, const void *buffer, size_t length); + + // Finish computation of the file hash + void FinishHashes(void); + + // How many blocks does this source file use + u32 BlockCount(void) const {return blockcount;} + +protected: + DescriptionPacket *descriptionpacket; // The file description packet. + VerificationPacket *verificationpacket; // The file verification packet. + DiskFile *diskfile; // The source file + + u64 filesize; // The size of the source file. + string diskfilename; // The filename of the source file on disk. + string parfilename; // The filename that will be recorded in the file description packet. + + u32 blockcount; // How many blocks the file will be divided into. + + MD5Context *contextfull; // MD5 context used to calculate the hash of the whole file +}; + +#endif // __PAR2CREATORSOURCEFILE_H__ diff --git a/lib/par2/par2fileformat.cpp b/lib/par2/par2fileformat.cpp new file mode 100644 index 000000000..97a295796 --- /dev/null +++ b/lib/par2/par2fileformat.cpp @@ -0,0 +1,28 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +MAGIC packet_magic = {{'P', 'A', 'R', '2', '\0','P', 'K', 'T'}}; +PACKETTYPE fileverificationpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'I', 'F', 'S', 'C', '\0','\0','\0','\0'}}; +PACKETTYPE filedescriptionpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'F', 'i', 'l', 'e', 'D', 'e', 's', 'c' }}; +PACKETTYPE mainpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'M', 'a', 'i', 'n', '\0','\0','\0','\0'}}; +PACKETTYPE recoveryblockpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'R', 'e', 'c', 'v', 'S', 'l', 'i', 'c' }}; +PACKETTYPE creatorpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'C', 'r', 'e', 'a', 't', 'o', 'r', '\0'}}; + diff --git a/lib/par2/par2fileformat.h b/lib/par2/par2fileformat.h new file mode 100644 index 000000000..1398311ab --- /dev/null +++ b/lib/par2/par2fileformat.h @@ -0,0 +1,199 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __PAR2FILEFORMAT_H__ +#define __PAR2FILEFORMAT_H__ + +// This file defines the format of a PAR2 file. + +// PAR2 files consist of one or more "packets" that contain information +// that is required to be able to verify and repair damaged data files. + +// All packets start with a short "header" which contains information +// used to describe what sort of data is stored in the rest of the packet +// and also to allow that data to be verified. + +// This file details the format for the following packet types described +// in the PAR 2.0 specification: + +// Main Packet struct MAINPACKET +// File Description Packet struct FILEDESCRIPTIONPACKET +// Input File Slice Checksum Packet struct FILEVERIFICATIONPACKET +// Recovery Slice Packet struct RECOVERYBLOCKPACKET +// Creator Packet struct CREATORPACKET + + +#ifdef WIN32 +#pragma pack(push, 1) +#define PACKED +#else +#define PACKED __attribute__ ((packed)) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4200) +#endif + +// All numeric fields in the file format are in LITTLE ENDIAN format. + +// The types leu32 and leu64 are defined in letype.h + +// Two simple types used in the packet header. +struct MAGIC {u8 magic[8];} PACKED; +struct PACKETTYPE {u8 type[16];} PACKED; + +// Every packet starts with a packet header. +struct PACKET_HEADER +{ + // Header + MAGIC magic; // = {'P', 'A', 'R', '2', '\0', 'P', 'K', 'T'} + leu64 length; // Length of entire packet including header + MD5Hash hash; // Hash of entire packet excepting the first 3 fields + MD5Hash setid; // Normally computed as the Hash of body of "Main Packet" + PACKETTYPE type; // Used to specify the meaning of the rest of the packet +} PACKED; + +// The file verification packet is used to determine whether or not any +// parts of a damaged file are useable. +// It contains a FileId used to pair it with a corresponding file description +// packet, followed by an array of hash and crc values. The number of entries in +// the array can be determined from the packet_length. +struct FILEVERIFICATIONENTRY +{ + MD5Hash hash; + leu32 crc; +} PACKED; +struct FILEVERIFICATIONPACKET +{ + PACKET_HEADER header; + // Body + MD5Hash fileid; // MD5hash of file_hash_16k, file_length, file_name + FILEVERIFICATIONENTRY entries[]; +} PACKED; + +// The file description packet is used to record the name of the file, +// its size, and the Hash of both the whole file and the first 16k of +// the file. +// If the name of the file is an exact multiple of 4 characters in length +// then it may not have a NULL termination. If the name of the file is not +// an exact multiple of 4, then it will be padded with 0 bytes at the +// end to make it up to a multiple of 4. +struct FILEDESCRIPTIONPACKET +{ + PACKET_HEADER header; + // Body + MD5Hash fileid; // MD5hash of [hash16k, length, name] + MD5Hash hashfull; // MD5 Hash of the whole file + MD5Hash hash16k; // MD5 Hash of the first 16k of the file + leu64 length; // Length of the file + u8 name[]; // Name of the file, padded with 1 to 3 zero bytes to reach + // a multiple of 4 bytes. + // Actual length can be determined from overall packet + // length and then working backwards to find the first non + // zero character. + + //u8* name(void) {return (u8*)&this[1];} + //const u8* name(void) const {return (const u8*)&this[1];} +} PACKED; + +// The main packet is used to tie together the other packets in a recovery file. +// It specifies the block size used to virtually slice the source files, a count +// of the number of source files, and an array of Hash values used to specify +// in what order the source files are processed. +// Each entry in the fileid array corresponds with the fileid value +// in a file description packet and a file verification packet. +// The fileid array may contain more entries than the count of the number +// of recoverable files. The extra entries correspond to files that were not +// used during the creation of the recovery files and which may not therefore +// be repaired if they are found to be damaged. +struct MAINPACKET +{ + PACKET_HEADER header; + // Body + leu64 blocksize; + leu32 recoverablefilecount; + MD5Hash fileid[0]; + //MD5Hash* fileid(void) {return (MD5Hash*)&this[1];} + //const MD5Hash* fileid(void) const {return (const MD5Hash*)&this[1];} +} PACKED; + +// The creator packet is used to identify which program created a particular +// recovery file. It is not required for verification or recovery of damaged +// files. +struct CREATORPACKET +{ + PACKET_HEADER header; + // Body + u8 client[]; + //u8* client(void) {return (u8*)&this[1];} +} PACKED; + +// The recovery block packet contains a single block of recovery data along +// with the exponent value used during the computation of that block. +struct RECOVERYBLOCKPACKET +{ + PACKET_HEADER header; + // Body + leu32 exponent; +// unsigned long data[]; +// unsigned long* data(void) {return (unsigned long*)&this[1];} +} PACKED; + +#ifdef _MSC_VER +#pragma warning(default:4200) +#endif + +#ifdef WIN32 +#pragma pack(pop) +#endif +#undef PACKED + + +// Operators for comparing the MAGIC and PACKETTYPE values + +inline bool operator == (const MAGIC &left, const MAGIC &right) +{ + return (0==memcmp(&left, &right, sizeof(left))); +} + +inline bool operator != (const MAGIC &left, const MAGIC &right) +{ + return !operator==(left, right); +} + +inline bool operator == (const PACKETTYPE &left, const PACKETTYPE &right) +{ + return (0==memcmp(&left, &right, sizeof(left))); +} + +inline bool operator != (const PACKETTYPE &left, const PACKETTYPE &right) +{ + return !operator==(left, right); +} + +extern MAGIC packet_magic; + +extern PACKETTYPE fileverificationpacket_type; +extern PACKETTYPE filedescriptionpacket_type; +extern PACKETTYPE mainpacket_type; +extern PACKETTYPE recoveryblockpacket_type; +extern PACKETTYPE creatorpacket_type; + + +#endif //__PAR2FILEFORMAT_H__ diff --git a/lib/par2/par2repairer.cpp b/lib/par2/par2repairer.cpp new file mode 100644 index 000000000..424ab6e6c --- /dev/null +++ b/lib/par2/par2repairer.cpp @@ -0,0 +1,2546 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +Par2Repairer::Par2Repairer(void) +{ + firstpacket = true; + mainpacket = 0; + creatorpacket = 0; + + blocksize = 0; + sourceblockcount = 0; + + blocksallocated = false; + + availableblockcount = 0; + missingblockcount = 0; + + completefilecount = 0; + renamedfilecount = 0; + damagedfilecount = 0; + missingfilecount = 0; + + inputbuffer = 0; + outputbuffer = 0; + + noiselevel = CommandLine::nlNormal; + headers = new ParHeaders; + alreadyloaded = false; + + cancelled = false; +} + +Par2Repairer::~Par2Repairer(void) +{ + delete [] (u8*)inputbuffer; + delete [] (u8*)outputbuffer; + + map::iterator rp = recoverypacketmap.begin(); + while (rp != recoverypacketmap.end()) + { + delete (*rp).second; + + ++rp; + } + + map::iterator sf = sourcefilemap.begin(); + while (sf != sourcefilemap.end()) + { + Par2RepairerSourceFile *sourcefile = (*sf).second; + delete sourcefile; + + ++sf; + } + + delete mainpacket; + delete creatorpacket; + delete headers; +} + + +Result Par2Repairer::PreProcess(const CommandLine &commandline) +{ + //sig_filename.emit("coucou cmoa"); + // What noiselevel are we using + noiselevel = commandline.GetNoiseLevel(); + + // Get filesnames from the command line + string par2filename = commandline.GetParFilename(); + const list &extrafiles = commandline.GetExtraFiles(); + + // Determine the searchpath from the location of the main PAR2 file + string name; + DiskFile::SplitFilename(par2filename, searchpath, name); + + // Load packets from the main PAR2 file + if (!LoadPacketsFromFile(searchpath + name)) + return eLogicError; + + // Load packets from other PAR2 files with names based on the original PAR2 file + if (!LoadPacketsFromOtherFiles(par2filename)) + return eLogicError; + + // Load packets from any other PAR2 files whose names are given on the command line + if (!LoadPacketsFromExtraFiles(extrafiles)) + return eLogicError; + + if (noiselevel > CommandLine::nlQuiet) + cout << endl; + + // Check that the packets are consistent and discard any that are not + if (!CheckPacketConsistency()) + return eInsufficientCriticalData; + + // Use the information in the main packet to get the source files + // into the correct order and determine their filenames + if (!CreateSourceFileList()) + return eLogicError; + + // Determine the total number of DataBlocks for the recoverable source files + // The allocate the DataBlocks and assign them to each source file + if (!AllocateSourceBlocks()) + return eLogicError; + + headers->setid = setid.print(); + headers->block_size = blocksize; + headers->chunk_size = chunksize; + headers->data_blocks = sourceblockcount; + headers->data_size = totalsize; + headers->recoverable_files = mainpacket->RecoverableFileCount(); + headers->other_files = + mainpacket->TotalFileCount() - mainpacket->RecoverableFileCount(); + sig_headers(headers); + /* + cout << "Values:" << endl << + "setid: " << setid << endl << + "blocksize: " << blocksize << endl << + "chunksize: " << chunksize << endl << + "sourceblockcount: " << sourceblockcount << endl << + "availableblockcount: " << availableblockcount << endl << + "missingblockcount: " << missingblockcount << endl << + "completefilecount: " << completefilecount << endl << + "renamedfilecount: " << renamedfilecount << endl << + "damagedfilecount: " << damagedfilecount << endl << + "missingfilecount: " << missingfilecount << endl << + "progress: " << progress << endl << + "totaldata: " << totaldata << endl << + "totalsize: " << totalsize << endl << + "RecoverableFileCount: " << mainpacket->RecoverableFileCount() << endl << + "TotalFileCount: " << mainpacket->TotalFileCount() << endl; + */ + + return eSuccess; +} + + +Result Par2Repairer::Process(const CommandLine &commandline, bool dorepair) +{ + //sig_filename.emit("coucou cmoa"); + // What noiselevel are we using + noiselevel = commandline.GetNoiseLevel(); + + // Get filesnames from the command line + string par2filename = commandline.GetParFilename(); + const list &extrafiles = commandline.GetExtraFiles(); + + // Determine the searchpath from the location of the main PAR2 file + string name; + DiskFile::SplitFilename(par2filename, searchpath, name); + + // Load packets from the main PAR2 file + /* if (!LoadPacketsFromFile(searchpath + name)) + return eLogicError; + + // Load packets from other PAR2 files with names based on the original PAR2 file + if (!LoadPacketsFromOtherFiles(par2filename)) + return eLogicError; + + // Load packets from any other PAR2 files whose names are given on the command line + if (!LoadPacketsFromExtraFiles(extrafiles)) + return eLogicError; + + if (noiselevel > CommandLine::nlQuiet) + cout << endl; + + // Check that the packets are consistent and discard any that are not + if (!CheckPacketConsistency()) + return eInsufficientCriticalData; + + // Use the information in the main packet to get the source files + // into the correct order and determine their filenames + if (!CreateSourceFileList()) + return eLogicError; + + // Determine the total number of DataBlocks for the recoverable source files + // The allocate the DataBlocks and assign them to each source file + if (!AllocateSourceBlocks()) + return eLogicError; + */ + // ---------- /Header ------------- + + // Create a verification hash table for all files for which we have not + // found a complete version of the file and for which we have + // a verification packet + if (!alreadyloaded) { + if (!PrepareVerificationHashTable()) + return eLogicError; + + // Compute the table for the sliding CRC computation + if (!ComputeWindowTable()) + return eLogicError; + + if (noiselevel > CommandLine::nlQuiet) + cout << endl << "Verifying source files:" << endl << endl; + + // Attempt to verify all of the source files + if (!VerifySourceFiles()) + return eFileIOError; + + if (completefilecountRecoverableFileCount()) + { + if (noiselevel > CommandLine::nlQuiet) + cout << endl << "Scanning extra files:" << endl << endl; + + // Scan any extra files specified on the command line + if (!VerifyExtraFiles(extrafiles)) + return eLogicError; + } + + // Find out how much data we have found + UpdateVerificationResults(); + alreadyloaded = true; + } + if (noiselevel > CommandLine::nlSilent) + cout << endl; + + // Check the verification results and report the results + if (!CheckVerificationResults()) + return eRepairNotPossible; + + // Are any of the files incomplete + if (completefilecountRecoverableFileCount()) + { + // Do we want to carry out a repair + if (dorepair) + { + if (noiselevel > CommandLine::nlSilent) + cout << endl; + + // Rename any damaged or missnamed target files. + if (!RenameTargetFiles()) + return eFileIOError; + + // Are we still missing any files + if (completefilecountRecoverableFileCount()) + { + // Work out which files are being repaired, create them, and allocate + // target DataBlocks to them, and remember them for later verification. + if (!CreateTargetFiles()) + return eFileIOError; + + // Work out which data blocks are available, which need to be copied + // directly to the output, and which need to be recreated, and compute + // the appropriate Reed Solomon matrix. + if (!ComputeRSmatrix()) + { + // Delete all of the partly reconstructed files + DeleteIncompleteTargetFiles(); + return eFileIOError; + } + + if (noiselevel > CommandLine::nlSilent) + cout << endl; + + // Allocate memory buffers for reading and writing data to disk. + if (!AllocateBuffers(commandline.GetMemoryLimit())) + { + // Delete all of the partly reconstructed files + DeleteIncompleteTargetFiles(); + return eMemoryError; + } + + // Set the total amount of data to be processed. + progress = 0; + totaldata = blocksize * sourceblockcount * (missingblockcount > 0 ? missingblockcount : 1); + + // Start at an offset of 0 within a block. + u64 blockoffset = 0; + while (blockoffset < blocksize) // Continue until the end of the block. + { + // Work out how much data to process this time. + size_t blocklength = (size_t)min((u64)chunksize, blocksize-blockoffset); + + // Read source data, process it through the RS matrix and write it to disk. + + if (!ProcessData(blockoffset, blocklength)) + { + // Delete all of the partly reconstructed files + DeleteIncompleteTargetFiles(); + return eFileIOError; + } + + // Advance to the need offset within each block + blockoffset += blocklength; + } + + if (noiselevel > CommandLine::nlSilent) + cout << endl << "Verifying repaired files:" << endl << endl; + + // Verify that all of the reconstructed target files are now correct + if (!VerifyTargetFiles()) + { + // Delete all of the partly reconstructed files + DeleteIncompleteTargetFiles(); + return eFileIOError; + } + } + + // Are all of the target files now complete? + if (completefilecountRecoverableFileCount()) + { + cerr << "Repair Failed." << endl; + return eRepairFailed; + } + else + { + if (noiselevel > CommandLine::nlSilent) + cout << endl << "Repair complete." << endl; + } + } + else + { + return eRepairPossible; + } + } + + return eSuccess; +} + +// Load the packets from the specified file +bool Par2Repairer::LoadPacketsFromFile(string filename) +{ + // Skip the file if it has already been processed + if (diskFileMap.Find(filename) != 0) + { + return true; + } + + DiskFile *diskfile = new DiskFile; + + // Open the file + if (!diskfile->Open(filename)) + { + // If we could not open the file, ignore the error and + // proceed to the next file + delete diskfile; + return true; + } + + if (noiselevel > CommandLine::nlSilent) + { + string path; + string name; + DiskFile::SplitFilename(filename, path, name); + cout << "Loading \"" << name << "\"." << endl; + sig_filename(name); + } + + // How many useable packets have we found + u32 packets = 0; + + // How many recovery packets were there + u32 recoverypackets = 0; + + // How big is the file + u64 filesize = diskfile->FileSize(); + if (filesize > 0) + { + // Allocate a buffer to read data into + // The buffer should be large enough to hold a whole + // critical packet (i.e. file verification, file description, main, + // and creator), but not necessarily a whole recovery packet. + size_t buffersize = (size_t)min((u64)1048576, filesize); + u8 *buffer = new u8[buffersize]; + + // Progress indicator + u64 progress = 0; + + // Start at the beginning of the file + u64 offset = 0; + + // Continue as long as there is at least enough for the packet header + while (offset + sizeof(PACKET_HEADER) <= filesize) + { + if (noiselevel > CommandLine::nlQuiet) + { + // Update a progress indicator + u32 oldfraction = (u32)(1000 * progress / filesize); + u32 newfraction = (u32)(1000 * offset / filesize); + if (oldfraction != newfraction) + { + cout << "Loading: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush; + progress = offset; + sig_progress(newfraction); + + if (cancelled) + { + break; + } + } + } + + // Attempt to read the next packet header + PACKET_HEADER header; + if (!diskfile->Read(offset, &header, sizeof(header))) + break; + + // Does this look like it might be a packet + if (packet_magic != header.magic) + { + offset++; + + // Is there still enough for at least a whole packet header + while (offset + sizeof(PACKET_HEADER) <= filesize) + { + // How much can we read into the buffer + size_t want = (size_t)min((u64)buffersize, filesize-offset); + + // Fill the buffer + if (!diskfile->Read(offset, buffer, want)) + { + offset = filesize; + break; + } + + // Scan the buffer for the magic value + u8 *current = buffer; + u8 *limit = &buffer[want-sizeof(PACKET_HEADER)]; + while (current <= limit && packet_magic != ((PACKET_HEADER*)current)->magic) + { + current++; + } + + // What file offset did we reach + offset += current-buffer; + + // Did we find the magic + if (current <= limit) + { + memcpy(&header, current, sizeof(header)); + break; + } + } + + // Did we reach the end of the file + if (offset + sizeof(PACKET_HEADER) > filesize) + { + break; + } + } + + // We have found the magic + + // Check the packet length + if (sizeof(PACKET_HEADER) > header.length || // packet length is too small + 0 != (header.length & 3) || // packet length is not a multiple of 4 + filesize < offset + header.length) // packet would extend beyond the end of the file + { + offset++; + continue; + } + + // Compute the MD5 Hash of the packet + MD5Context context; + context.Update(&header.setid, sizeof(header)-offsetof(PACKET_HEADER, setid)); + + // How much more do I need to read to get the whole packet + u64 current = offset+sizeof(PACKET_HEADER); + u64 limit = offset+header.length; + while (current < limit) + { + size_t want = (size_t)min((u64)buffersize, limit-current); + + if (!diskfile->Read(current, buffer, want)) + break; + + context.Update(buffer, want); + + current += want; + } + + // Did the whole packet get processed + if (currentClose(); + + // Did we actually find any interesting packets + if (packets > 0) + { + if (noiselevel > CommandLine::nlQuiet) + { + cout << "Loaded " << packets << " new packets"; + if (recoverypackets > 0) cout << " including " << recoverypackets << " recovery blocks"; + cout << endl; + } + + // Remember that the file was processed + bool success = diskFileMap.Insert(diskfile); + assert(success); + } + else + { + if (noiselevel > CommandLine::nlQuiet) + cout << "No new packets found" << endl; + delete diskfile; + } + + if (cancelled) + { + return false; + } + + return true; +} + +// Finish loading a recovery packet +bool Par2Repairer::LoadRecoveryPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + RecoveryPacket *packet = new RecoveryPacket; + + // Load the packet from disk + if (!packet->Load(diskfile, offset, header)) + { + delete packet; + return false; + } + + // What is the exponent value of this recovery packet + u32 exponent = packet->Exponent(); + + // Try to insert the new packet into the recovery packet map + pair::const_iterator, bool> location = recoverypacketmap.insert(pair(exponent, packet)); + + // Did the insert fail + if (!location.second) + { + // The packet must be a duplicate of one we already have + delete packet; + return false; + } + + return true; +} + +// Finish loading a file description packet +bool Par2Repairer::LoadDescriptionPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + DescriptionPacket *packet = new DescriptionPacket; + + // Load the packet from disk + if (!packet->Load(diskfile, offset, header)) + { + delete packet; + return false; + } + + // What is the fileid + const MD5Hash &fileid = packet->FileId(); + + // Look up the fileid in the source file map for an existing source file entry + map::iterator sfmi = sourcefilemap.find(fileid); + Par2RepairerSourceFile *sourcefile = (sfmi == sourcefilemap.end()) ? 0 :sfmi->second; + + // Was there an existing source file + if (sourcefile) + { + // Does the source file already have a description packet + if (sourcefile->GetDescriptionPacket()) + { + // Yes. We don't need another copy + delete packet; + return false; + } + else + { + // No. Store the packet in the source file + sourcefile->SetDescriptionPacket(packet); + return true; + } + } + else + { + // Create a new source file for the packet + sourcefile = new Par2RepairerSourceFile(packet, NULL); + + // Record the source file in the source file map + sourcefilemap.insert(pair(fileid, sourcefile)); + + return true; + } +} + +// Finish loading a file verification packet +bool Par2Repairer::LoadVerificationPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + VerificationPacket *packet = new VerificationPacket; + + // Load the packet from disk + if (!packet->Load(diskfile, offset, header)) + { + delete packet; + return false; + } + + // What is the fileid + const MD5Hash &fileid = packet->FileId(); + + // Look up the fileid in the source file map for an existing source file entry + map::iterator sfmi = sourcefilemap.find(fileid); + Par2RepairerSourceFile *sourcefile = (sfmi == sourcefilemap.end()) ? 0 :sfmi->second; + + // Was there an existing source file + if (sourcefile) + { + // Does the source file already have a verification packet + if (sourcefile->GetVerificationPacket()) + { + // Yes. We don't need another copy. + delete packet; + return false; + } + else + { + // No. Store the packet in the source file + sourcefile->SetVerificationPacket(packet); + + return true; + } + } + else + { + // Create a new source file for the packet + sourcefile = new Par2RepairerSourceFile(NULL, packet); + + // Record the source file in the source file map + sourcefilemap.insert(pair(fileid, sourcefile)); + + return true; + } +} + +// Finish loading the main packet +bool Par2Repairer::LoadMainPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + // Do we already have a main packet + if (0 != mainpacket) + return false; + + MainPacket *packet = new MainPacket; + + // Load the packet from disk; + if (!packet->Load(diskfile, offset, header)) + { + delete packet; + return false; + } + + mainpacket = packet; + + return true; +} + +// Finish loading the creator packet +bool Par2Repairer::LoadCreatorPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + // Do we already have a creator packet + if (0 != creatorpacket) + return false; + + CreatorPacket *packet = new CreatorPacket; + + // Load the packet from disk; + if (!packet->Load(diskfile, offset, header)) + { + delete packet; + return false; + } + + creatorpacket = packet; + + return true; +} + +// Load packets from other PAR2 files with names based on the original PAR2 file +bool Par2Repairer::LoadPacketsFromOtherFiles(string filename) +{ + // Split the original PAR2 filename into path and name parts + string path; + string name; + DiskFile::SplitFilename(filename, path, name); + + string::size_type where; + + // Trim ".par2" off of the end original name + + // Look for the last "." in the filename + while (string::npos != (where = name.find_last_of('.'))) + { + // Trim what follows the last . + string tail = name.substr(where+1); + name = name.substr(0,where); + + // Was what followed the last "." "par2" + if (0 == stricmp(tail.c_str(), "par2")) + break; + } + + // If what is left ends in ".volNNN-NNN" or ".volNNN+NNN" strip that as well + + // Is there another "." + if (string::npos != (where = name.find_last_of('.'))) + { + // What follows the "." + string tail = name.substr(where+1); + + // Scan what follows the last "." to see of it matches vol123-456 or vol123+456 + int n = 0; + string::const_iterator p; + for (p=tail.begin(); p!=tail.end(); ++p) + { + char ch = *p; + + if (0 == n) + { + if (tolower(ch) == 'v') { n++; } else { break; } + } + else if (1 == n) + { + if (tolower(ch) == 'o') { n++; } else { break; } + } + else if (2 == n) + { + if (tolower(ch) == 'l') { n++; } else { break; } + } + else if (3 == n) + { + if (isdigit(ch)) {} else if (ch == '-' || ch == '+') { n++; } else { break; } + } + else if (4 == n) + { + if (isdigit(ch)) {} else { break; } + } + } + + // If we matched then retain only what preceeds the "." + if (p == tail.end()) + { + name = name.substr(0,where); + } + } + + // Find files called "*.par2" or "name.*.par2" + + { + string wildcard = name.empty() ? "*.par2" : name + ".*.par2"; + list *files = DiskFile::FindFiles(path, wildcard); + + // Load packets from each file that was found + for (list::const_iterator s=files->begin(); s!=files->end(); ++s) + { + LoadPacketsFromFile(*s); + if (cancelled) + { + break; + } + } + + delete files; + if (cancelled) + { + return false; + } + } + + { + string wildcard = name.empty() ? "*.PAR2" : name + ".*.PAR2"; + list *files = DiskFile::FindFiles(path, wildcard); + + // Load packets from each file that was found + for (list::const_iterator s=files->begin(); s!=files->end(); ++s) + { + LoadPacketsFromFile(*s); + if (cancelled) + { + break; + } + } + + delete files; + if (cancelled) + { + return false; + } + } + + return true; +} + +// Load packets from any other PAR2 files whose names are given on the command line +bool Par2Repairer::LoadPacketsFromExtraFiles(const list &extrafiles) +{ + for (ExtraFileIterator i=extrafiles.begin(); i!=extrafiles.end(); i++) + { + string filename = i->FileName(); + + // If the filename contains ".par2" anywhere + if (string::npos != filename.find(".par2") || + string::npos != filename.find(".PAR2")) + { + LoadPacketsFromFile(filename); + if (cancelled) + { + break; + } + } + } + + if (cancelled) + { + return false; + } + + return true; +} + +// Check that the packets are consistent and discard any that are not +bool Par2Repairer::CheckPacketConsistency(void) +{ + // Do we have a main packet + if (0 == mainpacket) + { + // If we don't have a main packet, then there is nothing more that we can do. + // We cannot verify or repair any files. + + cerr << "Main packet not found." << endl; + return false; + } + + // Remember the block size from the main packet + blocksize = mainpacket->BlockSize(); + + // Check that the recovery blocks have the correct amount of data + // and discard any that don't + { + map::iterator rp = recoverypacketmap.begin(); + while (rp != recoverypacketmap.end()) + { + if (rp->second->BlockSize() == blocksize) + { + ++rp; + } + else + { + cerr << "Incorrect sized recovery block for exponent " << rp->second->Exponent() << " discarded" << endl; + + delete rp->second; + map::iterator x = rp++; + recoverypacketmap.erase(x); + } + } + } + + // Check for source files that have no description packet or where the + // verification packet has the wrong number of entries and discard them. + { + map::iterator sf = sourcefilemap.begin(); + while (sf != sourcefilemap.end()) + { + // Do we have a description packet + DescriptionPacket *descriptionpacket = sf->second->GetDescriptionPacket(); + if (descriptionpacket == 0) + { + // No description packet + + // Discard the source file + delete sf->second; + map::iterator x = sf++; + sourcefilemap.erase(x); + + continue; + } + + // Compute and store the block count from the filesize and blocksize + sf->second->SetBlockCount(blocksize); + + // Do we have a verification packet + VerificationPacket *verificationpacket = sf->second->GetVerificationPacket(); + if (verificationpacket == 0) + { + // No verification packet + + // That is ok, but we won't be able to use block verification. + + // Proceed to the next file. + ++sf; + + continue; + } + + // Work out the block count for the file from the file size + // and compare that with the verification packet + u64 filesize = descriptionpacket->FileSize(); + u32 blockcount = verificationpacket->BlockCount(); + + if ((filesize + blocksize-1) / blocksize != (u64)blockcount) + { + // The block counts are different! + + cerr << "Incorrectly sized verification packet for \"" << descriptionpacket->FileName() << "\" discarded" << endl; + + // Discard the source file + + delete sf->second; + map::iterator x = sf++; + sourcefilemap.erase(x); + + continue; + } + + // Everything is ok. + + // Proceed to the next file + ++sf; + } + } + + if (noiselevel > CommandLine::nlQuiet) + { + cout << "There are " + << mainpacket->RecoverableFileCount() + << " recoverable files and " + << mainpacket->TotalFileCount() - mainpacket->RecoverableFileCount() + << " other files." + << endl; + + cout << "The block size used was " + << blocksize + << " bytes." + << endl; + } + + return true; +} + +// Use the information in the main packet to get the source files +// into the correct order and determine their filenames +bool Par2Repairer::CreateSourceFileList(void) +{ + // For each FileId entry in the main packet + for (u32 filenumber=0; filenumberTotalFileCount(); filenumber++) + { + const MD5Hash &fileid = mainpacket->FileId(filenumber); + + // Look up the fileid in the source file map + map::iterator sfmi = sourcefilemap.find(fileid); + Par2RepairerSourceFile *sourcefile = (sfmi == sourcefilemap.end()) ? 0 :sfmi->second; + + if (sourcefile) + { + sourcefile->ComputeTargetFileName(searchpath); + } + + sourcefiles.push_back(sourcefile); + } + + return true; +} + +// Determine the total number of DataBlocks for the recoverable source files +// The allocate the DataBlocks and assign them to each source file +bool Par2Repairer::AllocateSourceBlocks(void) +{ + sourceblockcount = 0; + + u32 filenumber = 0; + vector::iterator sf = sourcefiles.begin(); + + // For each recoverable source file + while (filenumber < mainpacket->RecoverableFileCount() && sf != sourcefiles.end()) + { + // Do we have a source file + Par2RepairerSourceFile *sourcefile = *sf; + if (sourcefile) + { + sourceblockcount += sourcefile->BlockCount(); + } + else + { + // No details for this source file so we don't know what the + // total number of source blocks is +// sourceblockcount = 0; +// break; + } + + ++sf; + ++filenumber; + } + + // Did we determine the total number of source blocks + if (sourceblockcount > 0) + { + // Yes. + + // Allocate all of the Source and Target DataBlocks (which will be used + // to read and write data to disk). + + sourceblocks.resize(sourceblockcount); + targetblocks.resize(sourceblockcount); + + // Which DataBlocks will be allocated first + vector::iterator sourceblock = sourceblocks.begin(); + vector::iterator targetblock = targetblocks.begin(); + + u32 blocknumber = 0; + totalsize = 0; + + filenumber = 0; + sf = sourcefiles.begin(); + + while (filenumber < mainpacket->RecoverableFileCount() && sf != sourcefiles.end()) + { + Par2RepairerSourceFile *sourcefile = *sf; + + if (sourcefile) + { + totalsize += sourcefile->GetDescriptionPacket()->FileSize(); + u32 blockcount = sourcefile->BlockCount(); + + // Allocate the source and target DataBlocks to the sourcefile + sourcefile->SetBlocks(blocknumber, blockcount, sourceblock, targetblock, blocksize); + + blocknumber++; + + sourceblock += blockcount; + targetblock += blockcount; + } + + ++sf; + ++filenumber; + } + + blocksallocated = true; + + if (noiselevel > CommandLine::nlQuiet) + { + cout << "There are a total of " + << sourceblockcount + << " data blocks." + << endl; + + cout << "The total size of the data files is " + << totalsize + << " bytes." + << endl; + } + } + + return true; +} + +// Create a verification hash table for all files for which we have not +// found a complete version of the file and for which we have +// a verification packet +bool Par2Repairer::PrepareVerificationHashTable(void) +{ + // Choose a size for the hash table + verificationhashtable.SetLimit(sourceblockcount); + + // Will any files be block verifiable + blockverifiable = false; + + // For each source file + vector::iterator sf = sourcefiles.begin(); + while (sf != sourcefiles.end()) + { + // Get the source file + Par2RepairerSourceFile *sourcefile = *sf; + + if (sourcefile) + { + // Do we have a verification packet + if (0 != sourcefile->GetVerificationPacket()) + { + // Yes. Load the verification entries into the hash table + verificationhashtable.Load(sourcefile, blocksize); + + blockverifiable = true; + } + else + { + // No. We can only check the whole file + unverifiablesourcefiles.push_back(sourcefile); + } + } + + ++sf; + } + + return true; +} + +// Compute the table for the sliding CRC computation +bool Par2Repairer::ComputeWindowTable(void) +{ + if (blockverifiable) + { + GenerateWindowTable(blocksize, windowtable); + windowmask = ComputeWindowMask(blocksize); + } + + return true; +} + +static bool SortSourceFilesByFileName(Par2RepairerSourceFile *low, + Par2RepairerSourceFile *high) +{ + return low->TargetFileName() < high->TargetFileName(); +} + +// Attempt to verify all of the source files +bool Par2Repairer::VerifySourceFiles(void) +{ + bool finalresult = true; + + // Created a sorted list of the source files and verify them in that + // order rather than the order they are in the main packet. + vector sortedfiles; + + u32 filenumber = 0; + vector::iterator sf = sourcefiles.begin(); + while (sf != sourcefiles.end()) + { + // Do we have a source file + Par2RepairerSourceFile *sourcefile = *sf; + if (sourcefile) + { + sortedfiles.push_back(sourcefile); + } + else + { + // Was this one of the recoverable files + if (filenumber < mainpacket->RecoverableFileCount()) + { + cerr << "No details available for recoverable file number " << filenumber+1 << "." << endl << "Recovery will not be possible." << endl; + + // Set error but let verification of other files continue + finalresult = false; + } + else + { + cerr << "No details available for non-recoverable file number " << filenumber - mainpacket->RecoverableFileCount() + 1 << endl; + } + } + + ++sf; + } + + sort(sortedfiles.begin(), sortedfiles.end(), SortSourceFilesByFileName); + + // Start verifying the files + sf = sortedfiles.begin(); + while (sf != sortedfiles.end()) + { + if (cancelled) + { + return false; + } + + // Do we have a source file + Par2RepairerSourceFile *sourcefile = *sf; + + // What filename does the file use + string filename = sourcefile->TargetFileName(); + + // Check to see if we have already used this file + if (diskFileMap.Find(filename) != 0) + { + // The file has already been used! + + cerr << "Source file " << filenumber+1 << " is a duplicate." << endl; + + return false; + } + + DiskFile *diskfile = new DiskFile; + + // Does the target file exist + if (diskfile->Open(filename)) + { + // Yes. Record that fact. + sourcefile->SetTargetExists(true); + + // Remember that the DiskFile is the target file + sourcefile->SetTargetFile(diskfile); + + // Remember that we have processed this file + bool success = diskFileMap.Insert(diskfile); + assert(success); + // Do the actual verification + if (!VerifyDataFile(diskfile, sourcefile)) + finalresult = false; + + // We have finished with the file for now + diskfile->Close(); + + // Find out how much data we have found + UpdateVerificationResults(); + } + else + { + // The file does not exist. + delete diskfile; + + if (noiselevel > CommandLine::nlSilent) + { + string path; + string name; + DiskFile::SplitFilename(filename, path, name); + + cout << "Target: \"" << name << "\" - missing." << endl; + sig_done(name, 0, sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0); + } + } + + ++sf; + } + + return finalresult; +} + +// Scan any extra files specified on the command line +bool Par2Repairer::VerifyExtraFiles(const list &extrafiles) +{ + for (ExtraFileIterator i=extrafiles.begin(); + i!=extrafiles.end() && completefilecountRecoverableFileCount(); + ++i) + { + string filename = i->FileName(); + + // If the filename does not include ".par2" we are interested in it. + if (string::npos == filename.find(".par2") && + string::npos == filename.find(".PAR2")) + { + filename = DiskFile::GetCanonicalPathname(filename); + + // Has this file already been dealt with + if (diskFileMap.Find(filename) == 0) + { + DiskFile *diskfile = new DiskFile; + + // Does the file exist + if (!diskfile->Open(filename)) + { + delete diskfile; + continue; + } + + // Remember that we have processed this file + bool success = diskFileMap.Insert(diskfile); + assert(success); + + // Do the actual verification + VerifyDataFile(diskfile, 0); + // Ignore errors + + // We have finished with the file for now + diskfile->Close(); + + // Find out how much data we have found + UpdateVerificationResults(); + } + } + } + + return true; +} + +// Attempt to match the data in the DiskFile with the source file +bool Par2Repairer::VerifyDataFile(DiskFile *diskfile, Par2RepairerSourceFile *sourcefile) +{ + MatchType matchtype; // What type of match was made + MD5Hash hashfull; // The MD5 Hash of the whole file + MD5Hash hash16k; // The MD5 Hash of the files 16k of the file + + // Are there any files that can be verified at the block level + if (blockverifiable) + { + u32 count; + + // Scan the file at the block level. + + if (!ScanDataFile(diskfile, // [in] The file to scan + sourcefile, // [in/out] Modified in the match is for another source file + matchtype, // [out] + hashfull, // [out] + hash16k, // [out] + count)) // [out] + return false; + + switch (matchtype) + { + case eNoMatch: + // No data was found at all. + + // Continue to next test. + break; + case ePartialMatch: + { + // We found some data. + + // Return them. + return true; + } + break; + case eFullMatch: + { + // We found a perfect match. + + sourcefile->SetCompleteFile(diskfile); + + // Return the match + return true; + } + break; + } + } + + // We did not find a match for any blocks of data within the file, but if + // there are any files for which we did not have a verification packet + // we can try a simple match of the hash for the whole file. + + // Are there any files that cannot be verified at the block level + if (unverifiablesourcefiles.size() > 0) + { + // Would we have already computed the file hashes + if (!blockverifiable) + { + u64 filesize = diskfile->FileSize(); + + size_t buffersize = 1024*1024; + if (buffersize > min(blocksize, filesize)) + buffersize = (size_t)min(blocksize, filesize); + + char *buffer = new char[buffersize]; + + u64 offset = 0; + + MD5Context context; + + while (offset < filesize) + { + size_t want = (size_t)min((u64)buffersize, filesize-offset); + + if (!diskfile->Read(offset, buffer, want)) + { + delete [] buffer; + return false; + } + + // Will the newly read data reach the 16k boundary + if (offset < 16384 && offset + want >= 16384) + { + context.Update(buffer, (size_t)(16384-offset)); + + // Compute the 16k hash + MD5Context temp = context; + temp.Final(hash16k); + + // Is there more data + if (offset + want > 16384) + { + context.Update(&buffer[16384-offset], (size_t)(offset+want)-16384); + } + } + else + { + context.Update(buffer, want); + } + + offset += want; + } + + // Compute the file hash + MD5Hash hashfull; + context.Final(hashfull); + + // If we did not have 16k of data, then the 16k hash + // is the same as the full hash + if (filesize < 16384) + { + hash16k = hashfull; + } + } + + list::iterator sf = unverifiablesourcefiles.begin(); + + // Compare the hash values of each source file for a match + while (sf != unverifiablesourcefiles.end()) + { + sourcefile = *sf; + + // Does the file match + if (sourcefile->GetCompleteFile() == 0 && + diskfile->FileSize() == sourcefile->GetDescriptionPacket()->FileSize() && + hash16k == sourcefile->GetDescriptionPacket()->Hash16k() && + hashfull == sourcefile->GetDescriptionPacket()->HashFull()) + { + if (noiselevel > CommandLine::nlSilent) + cout << diskfile->FileName() << " is a perfect match for " << sourcefile->GetDescriptionPacket()->FileName() << endl; + + // Record that we have a perfect match for this source file + sourcefile->SetCompleteFile(diskfile); + + if (blocksallocated) + { + // Allocate all of the DataBlocks for the source file to the DiskFile + + u64 offset = 0; + u64 filesize = sourcefile->GetDescriptionPacket()->FileSize(); + + vector::iterator sb = sourcefile->SourceBlocks(); + + while (offset < filesize) + { + DataBlock &datablock = *sb; + + datablock.SetLocation(diskfile, offset); + datablock.SetLength(min(blocksize, filesize-offset)); + + offset += blocksize; + ++sb; + } + } + + // Return the match + return true; + } + + ++sf; + } + } + + return true; +} + +// Perform a sliding window scan of the DiskFile looking for blocks of data that +// might belong to any of the source files (for which a verification packet was +// available). If a block of data might be from more than one source file, prefer +// the one specified by the "sourcefile" parameter. If the first data block +// found is for a different source file then "sourcefile" is changed accordingly. +bool Par2Repairer::ScanDataFile(DiskFile *diskfile, // [in] + Par2RepairerSourceFile* &sourcefile, // [in/out] + MatchType &matchtype, // [out] + MD5Hash &hashfull, // [out] + MD5Hash &hash16k, // [out] + u32 &count) // [out] +{ + // Remember which file we wanted to match + Par2RepairerSourceFile *originalsourcefile = sourcefile; + + matchtype = eNoMatch; + + // Is the file empty + if (diskfile->FileSize() == 0) + { + // If the file is empty, then just return + return true; + } + + string path; + string name; + DiskFile::SplitFilename(diskfile->FileName(), path, name); + + sig_filename(name); + + string shortname; + if (name.size() > 56) + { + shortname = name.substr(0, 28) + "..." + name.substr(name.size()-28); + } + else + { + shortname = name; + } + + // Create the checksummer for the file and start reading from it + FileCheckSummer filechecksummer(diskfile, blocksize, windowtable, windowmask); + if (!filechecksummer.Start()) + return false; + + // Assume we will make a perfect match for the file + matchtype = eFullMatch; + + // How many matches have we had + count = 0; + + // How many blocks have already been found + u32 duplicatecount = 0; + + // Have we found data blocks in this file that belong to more than one target file + bool multipletargets = false; + + // Which block do we expect to find first + const VerificationHashEntry *nextentry = 0; + + u64 progress = 0; + + // Whilst we have not reached the end of the file + while (filechecksummer.Offset() < diskfile->FileSize()) + { + if (noiselevel > CommandLine::nlQuiet) + { + // Update a progress indicator + u32 oldfraction = (u32)(1000 * progress / diskfile->FileSize()); + u32 newfraction = (u32)(1000 * (progress = filechecksummer.Offset()) / diskfile->FileSize()); + if (oldfraction != newfraction) + { + cout << "Scanning: \"" << shortname << "\": " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush; + sig_progress(newfraction); + + if (cancelled) + { + break; + } + } + } + + // If we fail to find a match, it might be because it was a duplicate of a block + // that we have already found. + bool duplicate; + + // Look for a match + const VerificationHashEntry *currententry = verificationhashtable.FindMatch(nextentry, sourcefile, filechecksummer, duplicate); + + // Did we find a match + if (currententry != 0) + { + // Is this the first match + if (count == 0) + { + // Which source file was it + sourcefile = currententry->SourceFile(); + + // If the first match found was not actually the first block + // for the source file, or it was not at the start of the + // data file: then this is a partial match. + if (!currententry->FirstBlock() || filechecksummer.Offset() != 0) + { + matchtype = ePartialMatch; + } + } + else + { + // If the match found is not the one which was expected + // then this is a partial match + + if (currententry != nextentry) + { + matchtype = ePartialMatch; + } + + // Is the match from a different source file + if (sourcefile != currententry->SourceFile()) + { + multipletargets = true; + } + } + + if (blocksallocated) + { + // Record the match + currententry->SetBlock(diskfile, filechecksummer.Offset()); + } + + // Update the number of matches found + count++; + + // What entry do we expect next + nextentry = currententry->Next(); + + // Advance to the next block + if (!filechecksummer.Jump(currententry->GetDataBlock()->GetLength())) + return false; + } + else + { + // This cannot be a perfect match + matchtype = ePartialMatch; + + // Was this a duplicate match + if (duplicate) + { + duplicatecount++; + + // What entry would we expect next + nextentry = 0; + + // Advance one whole block + if (!filechecksummer.Jump(blocksize)) + return false; + } + else + { + // What entry do we expect next + nextentry = 0; + + // Advance 1 byte + if (!filechecksummer.Step()) + return false; + } + } + } + + if (cancelled) + { + return false; + } + + // Get the Full and 16k hash values of the file + filechecksummer.GetFileHashes(hashfull, hash16k); + + // Did we make any matches at all + if (count > 0) + { + // If this still might be a perfect match, check the + // hashes, file size, and number of blocks to confirm. + if (matchtype != eFullMatch || + count != sourcefile->GetVerificationPacket()->BlockCount() || + diskfile->FileSize() != sourcefile->GetDescriptionPacket()->FileSize() || + hashfull != sourcefile->GetDescriptionPacket()->HashFull() || + hash16k != sourcefile->GetDescriptionPacket()->Hash16k()) + { + matchtype = ePartialMatch; + + if (noiselevel > CommandLine::nlSilent) + { + // Did we find data from multiple target files + if (multipletargets) + { + // Were we scanning the target file or an extra file + if (originalsourcefile != 0) + { + cout << "Target: \"" + << name + << "\" - damaged, found " + << count + << " data blocks from several target files." + << endl; + } + else + { + cout << "File: \"" + << name + << "\" - found " + << count + << " data blocks from several target files." + << endl; + } + } + else + { + // Did we find data blocks that belong to the target file + if (originalsourcefile == sourcefile) + { + cout << "Target: \"" + << name + << "\" - damaged. Found " + << count + << " of " + << sourcefile->GetVerificationPacket()->BlockCount() + << " data blocks." + << endl; + } + // Were we scanning the target file or an extra file + else if (originalsourcefile != 0) + { + string targetname; + DiskFile::SplitFilename(sourcefile->TargetFileName(), path, targetname); + + cout << "Target: \"" + << name + << "\" - damaged. Found " + << count + << " of " + << sourcefile->GetVerificationPacket()->BlockCount() + << " data blocks from \"" + << targetname + << "\"." + << endl; + } + else + { + string targetname; + DiskFile::SplitFilename(sourcefile->TargetFileName(), path, targetname); + + cout << "File: \"" + << name + << "\" - found " + << count + << " of " + << sourcefile->GetVerificationPacket()->BlockCount() + << " data blocks from \"" + << targetname + << "\"." + << endl; + } + } + } + } + else + { + if (noiselevel > CommandLine::nlSilent) + { + // Did we match the target file + if (originalsourcefile == sourcefile) + { + cout << "Target: \"" << name << "\" - found." << endl; + } + // Were we scanning the target file or an extra file + else if (originalsourcefile != 0) + { + string targetname; + DiskFile::SplitFilename(sourcefile->TargetFileName(), path, targetname); + + cout << "Target: \"" + << name + << "\" - is a match for \"" + << targetname + << "\"." + << endl; + } + else + { + string targetname; + DiskFile::SplitFilename(sourcefile->TargetFileName(), path, targetname); + + cout << "File: \"" + << name + << "\" - is a match for \"" + << targetname + << "\"." + << endl; + } + } + } + } + else + { + matchtype = eNoMatch; + + if (noiselevel > CommandLine::nlSilent) + { + // We found not data, but did the file actually contain blocks we + // had already found in other files. + if (duplicatecount > 0) + { + cout << "File: \"" + << name + << "\" - found " + << duplicatecount + << " duplicate data blocks." + << endl; + } + else + { + cout << "File: \"" + << name + << "\" - no data found." + << endl; + } + } + } + sig_done(name,count, count>0 && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0); + sig_progress(1000.0); + return true; +} + +// Find out how much data we have found +void Par2Repairer::UpdateVerificationResults(void) +{ + availableblockcount = 0; + missingblockcount = 0; + + completefilecount = 0; + renamedfilecount = 0; + damagedfilecount = 0; + missingfilecount = 0; + + u32 filenumber = 0; + vector::iterator sf = sourcefiles.begin(); + + // Check the recoverable files + while (sf != sourcefiles.end() && filenumber < mainpacket->TotalFileCount()) + { + Par2RepairerSourceFile *sourcefile = *sf; + + if (sourcefile) + { + // Was a perfect match for the file found + if (sourcefile->GetCompleteFile() != 0) + { + // Is it the target file or a different one + if (sourcefile->GetCompleteFile() == sourcefile->GetTargetFile()) + { + completefilecount++; + } + else + { + renamedfilecount++; + } + + availableblockcount += sourcefile->BlockCount(); + } + else + { + // Count the number of blocks that have been found + vector::iterator sb = sourcefile->SourceBlocks(); + for (u32 blocknumber=0; blocknumberBlockCount(); ++blocknumber, ++sb) + { + DataBlock &datablock = *sb; + + if (datablock.IsSet()) + availableblockcount++; + } + + // Does the target file exist + if (sourcefile->GetTargetExists()) + { + damagedfilecount++; + } + else + { + missingfilecount++; + } + } + } + else + { + missingfilecount++; + } + + ++filenumber; + ++sf; + } + + missingblockcount = sourceblockcount - availableblockcount; +} + +// Check the verification results and report the results +bool Par2Repairer::CheckVerificationResults(void) +{ + // Is repair needed + if (completefilecount < mainpacket->RecoverableFileCount() || + renamedfilecount > 0 || + damagedfilecount > 0 || + missingfilecount > 0) + { + if (noiselevel > CommandLine::nlSilent) + cout << "Repair is required." << endl; + if (noiselevel > CommandLine::nlQuiet) + { + if (renamedfilecount > 0) cout << renamedfilecount << " file(s) have the wrong name." << endl; + if (missingfilecount > 0) cout << missingfilecount << " file(s) are missing." << endl; + if (damagedfilecount > 0) cout << damagedfilecount << " file(s) exist but are damaged." << endl; + if (completefilecount > 0) cout << completefilecount << " file(s) are ok." << endl; + + cout << "You have " << availableblockcount + << " out of " << sourceblockcount + << " data blocks available." << endl; + if (recoverypacketmap.size() > 0) + cout << "You have " << (u32)recoverypacketmap.size() + << " recovery blocks available." << endl; + } + + // Is repair possible + if (recoverypacketmap.size() >= missingblockcount) + { + if (noiselevel > CommandLine::nlSilent) + cout << "Repair is possible." << endl; + + if (noiselevel > CommandLine::nlQuiet) + { + if (recoverypacketmap.size() > missingblockcount) + cout << "You have an excess of " + << (u32)recoverypacketmap.size() - missingblockcount + << " recovery blocks." << endl; + + if (missingblockcount > 0) + cout << missingblockcount + << " recovery blocks will be used to repair." << endl; + else if (recoverypacketmap.size()) + cout << "None of the recovery blocks will be used for the repair." << endl; + } + + return true; + } + else + { + if (noiselevel > CommandLine::nlSilent) + { + cout << "Repair is not possible." << endl; + cout << "You need " << missingblockcount - recoverypacketmap.size() + << " more recovery blocks to be able to repair." << endl; + } + + return false; + } + } + else + { + if (noiselevel > CommandLine::nlSilent) + cout << "All files are correct, repair is not required." << endl; + + return true; + } + + return true; +} + +// Rename any damaged or missnamed target files. +bool Par2Repairer::RenameTargetFiles(void) +{ + u32 filenumber = 0; + vector::iterator sf = sourcefiles.begin(); + + // Rename any damaged target files + while (sf != sourcefiles.end() && filenumber < mainpacket->TotalFileCount()) + { + Par2RepairerSourceFile *sourcefile = *sf; + + // If the target file exists but is not a complete version of the file + if (sourcefile->GetTargetExists() && + sourcefile->GetTargetFile() != sourcefile->GetCompleteFile()) + { + DiskFile *targetfile = sourcefile->GetTargetFile(); + + // Rename it + diskFileMap.Remove(targetfile); + + if (!targetfile->Rename()) + return false; + + bool success = diskFileMap.Insert(targetfile); + assert(success); + + // We no longer have a target file + sourcefile->SetTargetExists(false); + sourcefile->SetTargetFile(0); + } + + ++sf; + ++filenumber; + } + + filenumber = 0; + sf = sourcefiles.begin(); + + // Rename any missnamed but complete versions of the files + while (sf != sourcefiles.end() && filenumber < mainpacket->TotalFileCount()) + { + Par2RepairerSourceFile *sourcefile = *sf; + + // If there is no targetfile and there is a complete version + if (sourcefile->GetTargetFile() == 0 && + sourcefile->GetCompleteFile() != 0) + { + DiskFile *targetfile = sourcefile->GetCompleteFile(); + + // Rename it + diskFileMap.Remove(targetfile); + + if (!targetfile->Rename(sourcefile->TargetFileName())) + return false; + + bool success = diskFileMap.Insert(targetfile); + assert(success); + + // This file is now the target file + sourcefile->SetTargetExists(true); + sourcefile->SetTargetFile(targetfile); + + // We have one more complete file + completefilecount++; + } + + ++sf; + ++filenumber; + } + + return true; +} + +// Work out which files are being repaired, create them, and allocate +// target DataBlocks to them, and remember them for later verification. +bool Par2Repairer::CreateTargetFiles(void) +{ + u32 filenumber = 0; + vector::iterator sf = sourcefiles.begin(); + + // Create any missing target files + while (sf != sourcefiles.end() && filenumber < mainpacket->TotalFileCount()) + { + Par2RepairerSourceFile *sourcefile = *sf; + + // If the file does not exist + if (!sourcefile->GetTargetExists()) + { + DiskFile *targetfile = new DiskFile; + string filename = sourcefile->TargetFileName(); + u64 filesize = sourcefile->GetDescriptionPacket()->FileSize(); + + // Create the target file + if (!targetfile->Create(filename, filesize)) + { + delete targetfile; + return false; + } + + // This file is now the target file + sourcefile->SetTargetExists(true); + sourcefile->SetTargetFile(targetfile); + + // Remember this file + bool success = diskFileMap.Insert(targetfile); + assert(success); + + u64 offset = 0; + vector::iterator tb = sourcefile->TargetBlocks(); + + // Allocate all of the target data blocks + while (offset < filesize) + { + DataBlock &datablock = *tb; + + datablock.SetLocation(targetfile, offset); + datablock.SetLength(min(blocksize, filesize-offset)); + + offset += blocksize; + ++tb; + } + + // Add the file to the list of those that will need to be verified + // once the repair has completed. + verifylist.push_back(sourcefile); + } + + ++sf; + ++filenumber; + } + + return true; +} + +// Work out which data blocks are available, which need to be copied +// directly to the output, and which need to be recreated, and compute +// the appropriate Reed Solomon matrix. +bool Par2Repairer::ComputeRSmatrix(void) +{ + inputblocks.resize(sourceblockcount); // The DataBlocks that will read from disk + copyblocks.resize(availableblockcount); // Those DataBlocks which need to be copied + outputblocks.resize(missingblockcount); // Those DataBlocks that will re recalculated + + vector::iterator inputblock = inputblocks.begin(); + vector::iterator copyblock = copyblocks.begin(); + vector::iterator outputblock = outputblocks.begin(); + + // Build an array listing which source data blocks are present and which are missing + vector present; + present.resize(sourceblockcount); + + vector::iterator sourceblock = sourceblocks.begin(); + vector::iterator targetblock = targetblocks.begin(); + vector::iterator pres = present.begin(); + + // Iterate through all source blocks for all files + while (sourceblock != sourceblocks.end()) + { + // Was this block found + if (sourceblock->IsSet()) + { +// // Open the file the block was found in. +// if (!sourceblock->Open()) +// return false; + + // Record that the block was found + *pres = true; + + // Add the block to the list of those which will be read + // as input (and which might also need to be copied). + *inputblock = &*sourceblock; + *copyblock = &*targetblock; + + ++inputblock; + ++copyblock; + } + else + { + // Record that the block was missing + *pres = false; + + // Add the block to the list of those to be written + *outputblock = &*targetblock; + ++outputblock; + } + + ++sourceblock; + ++targetblock; + ++pres; + } + + // Set the number of source blocks and which of them are present + if (!rs.SetInput(present)) + return false; + + // Start iterating through the available recovery packets + map::iterator rp = recoverypacketmap.begin(); + + // Continue to fill the remaining list of data blocks to be read + while (inputblock != inputblocks.end()) + { + // Get the next available recovery packet + u32 exponent = rp->first; + RecoveryPacket* recoverypacket = rp->second; + + // Get the DataBlock from the recovery packet + DataBlock *recoveryblock = recoverypacket->GetDataBlock(); + +// // Make sure the file is open +// if (!recoveryblock->Open()) +// return false; + + // Add the recovery block to the list of blocks that will be read + *inputblock = recoveryblock; + + // Record that the corresponding exponent value is the next one + // to use in the RS matrix + if (!rs.SetOutput(true, (u16)exponent)) + return false; + + ++inputblock; + ++rp; + } + + // If we need to, compute and solve the RS matrix + if (missingblockcount == 0) + return true; + + bool success = rs.Compute(noiselevel); + + return success; +} + +// Allocate memory buffers for reading and writing data to disk. +bool Par2Repairer::AllocateBuffers(size_t memorylimit) +{ + // Would single pass processing use too much memory + if (blocksize * missingblockcount > memorylimit) + { + // Pick a size that is small enough + chunksize = ~3 & (memorylimit / missingblockcount); + } + else + { + chunksize = (size_t)blocksize; + } + + // Allocate the two buffers + inputbuffer = new u8[(size_t)chunksize]; + outputbuffer = new u8[(size_t)chunksize * missingblockcount]; + + if (inputbuffer == NULL || outputbuffer == NULL) + { + cerr << "Could not allocate buffer memory." << endl; + return false; + } + + return true; +} + +// Read source data, process it through the RS matrix and write it to disk. +bool Par2Repairer::ProcessData(u64 blockoffset, size_t blocklength) +{ + u64 totalwritten = 0; + + // Clear the output buffer + memset(outputbuffer, 0, (size_t)chunksize * missingblockcount); + + vector::iterator inputblock = inputblocks.begin(); + vector::iterator copyblock = copyblocks.begin(); + u32 inputindex = 0; + + DiskFile *lastopenfile = NULL; + + // Are there any blocks which need to be reconstructed + if (missingblockcount > 0) + { + // For each input block + while (inputblock != inputblocks.end()) + { + // Are we reading from a new file? + if (lastopenfile != (*inputblock)->GetDiskFile()) + { + // Close the last file + if (lastopenfile != NULL) + { + lastopenfile->Close(); + } + + // Open the new file + lastopenfile = (*inputblock)->GetDiskFile(); + if (!lastopenfile->Open()) + { + return false; + } + } + + // Read data from the current input block + if (!(*inputblock)->ReadData(blockoffset, blocklength, inputbuffer)) + return false; + + // Have we reached the last source data block + if (copyblock != copyblocks.end()) + { + // Does this block need to be copied to the target file + if ((*copyblock)->IsSet()) + { + size_t wrote; + + // Write the block back to disk in the new target file + if (!(*copyblock)->WriteData(blockoffset, blocklength, inputbuffer, wrote)) + return false; + + totalwritten += wrote; + } + ++copyblock; + } + + // For each output block + for (u32 outputindex=0; outputindex CommandLine::nlQuiet) + { + // Update a progress indicator + u32 oldfraction = (u32)(1000 * progress / totaldata); + progress += blocklength; + u32 newfraction = (u32)(1000 * progress / totaldata); + + if (oldfraction != newfraction) + { + cout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush; + sig_progress(newfraction); + + if (cancelled) + { + break; + } + } + } + } + + if (cancelled) + { + break; + } + + ++inputblock; + ++inputindex; + } + } + else + { + // Reconstruction is not required, we are just copying blocks between files + + // For each block that might need to be copied + while (copyblock != copyblocks.end()) + { + // Does this block need to be copied + if ((*copyblock)->IsSet()) + { + // Are we reading from a new file? + if (lastopenfile != (*inputblock)->GetDiskFile()) + { + // Close the last file + if (lastopenfile != NULL) + { + lastopenfile->Close(); + } + + // Open the new file + lastopenfile = (*inputblock)->GetDiskFile(); + if (!lastopenfile->Open()) + { + return false; + } + } + + // Read data from the current input block + if (!(*inputblock)->ReadData(blockoffset, blocklength, inputbuffer)) + return false; + + size_t wrote; + if (!(*copyblock)->WriteData(blockoffset, blocklength, inputbuffer, wrote)) + return false; + totalwritten += wrote; + } + + if (noiselevel > CommandLine::nlQuiet) + { + // Update a progress indicator + u32 oldfraction = (u32)(1000 * progress / totaldata); + progress += blocklength; + u32 newfraction = (u32)(1000 * progress / totaldata); + + if (oldfraction != newfraction) + { + cout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush; + sig_progress(newfraction); + + if (cancelled) + { + break; + } + } + } + + if (cancelled) + { + break; + } + + ++copyblock; + ++inputblock; + } + } + + // Close the last file + if (lastopenfile != NULL) + { + lastopenfile->Close(); + } + + if (cancelled) + { + return false; + } + + if (noiselevel > CommandLine::nlQuiet) + cout << "Writing recovered data\r"; + + // For each output block that has been recomputed + vector::iterator outputblock = outputblocks.begin(); + for (u32 outputindex=0; outputindexWriteData(blockoffset, blocklength, outbuf, wrote)) + return false; + totalwritten += wrote; + + ++outputblock; + } + + if (noiselevel > CommandLine::nlQuiet) + cout << "Wrote " << totalwritten << " bytes to disk" << endl; + + return true; +} + +// Verify that all of the reconstructed target files are now correct +bool Par2Repairer::VerifyTargetFiles(void) +{ + bool finalresult = true; + + // Verify the target files in alphabetical order + sort(verifylist.begin(), verifylist.end(), SortSourceFilesByFileName); + + // Iterate through each file in the verification list + for (vector::iterator sf = verifylist.begin(); + sf != verifylist.end(); + ++sf) + { + Par2RepairerSourceFile *sourcefile = *sf; + DiskFile *targetfile = sourcefile->GetTargetFile(); + + // Close the file + if (targetfile->IsOpen()) + targetfile->Close(); + + // Mark all data blocks for the file as unknown + vector::iterator sb = sourcefile->SourceBlocks(); + for (u32 blocknumber=0; blocknumberBlockCount(); blocknumber++) + { + sb->ClearLocation(); + ++sb; + } + + // Say we don't have a complete version of the file + sourcefile->SetCompleteFile(0); + + // Re-open the target file + if (!targetfile->Open()) + { + finalresult = false; + continue; + } + + // Verify the file again + if (!VerifyDataFile(targetfile, sourcefile)) + finalresult = false; + + // Close the file again + targetfile->Close(); + + // Find out how much data we have found + UpdateVerificationResults(); + } + + return finalresult; +} + +// Delete all of the partly reconstructed files +bool Par2Repairer::DeleteIncompleteTargetFiles(void) +{ + vector::iterator sf = verifylist.begin(); + + // Iterate through each file in the verification list + while (sf != verifylist.end()) + { + Par2RepairerSourceFile *sourcefile = *sf; + if (sourcefile->GetTargetExists()) + { + DiskFile *targetfile = sourcefile->GetTargetFile(); + + // Close and delete the file + if (targetfile->IsOpen()) + targetfile->Close(); + targetfile->Delete(); + + // Forget the file + diskFileMap.Remove(targetfile); + delete targetfile; + + // There is no target file + sourcefile->SetTargetExists(false); + sourcefile->SetTargetFile(0); + } + + ++sf; + } + + return true; +} diff --git a/lib/par2/par2repairer.h b/lib/par2/par2repairer.h new file mode 100644 index 000000000..9a1ef5e73 --- /dev/null +++ b/lib/par2/par2repairer.h @@ -0,0 +1,190 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __PAR2REPAIRER_H__ +#define __PAR2REPAIRER_H__ + +#include "parheaders.h" + +class Par2Repairer +{ +public: + Par2Repairer(void); + ~Par2Repairer(void); + + Result PreProcess(const CommandLine &commandline); + Result Process(const CommandLine &commandline, bool dorepair); + +protected: + // Steps in verifying and repairing files: + + // Load packets from the specified file + bool LoadPacketsFromFile(string filename); + // Finish loading a recovery packet + bool LoadRecoveryPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + // Finish loading a file description packet + bool LoadDescriptionPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + // Finish loading a file verification packet + bool LoadVerificationPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + // Finish loading the main packet + bool LoadMainPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + // Finish loading the creator packet + bool LoadCreatorPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + + // Load packets from other PAR2 files with names based on the original PAR2 file + bool LoadPacketsFromOtherFiles(string filename); + + // Load packets from any other PAR2 files whose names are given on the command line + bool LoadPacketsFromExtraFiles(const list &extrafiles); + + // Check that the packets are consistent and discard any that are not + bool CheckPacketConsistency(void); + + // Use the information in the main packet to get the source files + // into the correct order and determine their filenames + bool CreateSourceFileList(void); + + // Determine the total number of DataBlocks for the recoverable source files + // The allocate the DataBlocks and assign them to each source file + bool AllocateSourceBlocks(void); + + // Create a verification hash table for all files for which we have not + // found a complete version of the file and for which we have + // a verification packet + bool PrepareVerificationHashTable(void); + + // Compute the table for the sliding CRC computation + bool ComputeWindowTable(void); + + // Attempt to verify all of the source files + bool VerifySourceFiles(void); + + // Scan any extra files specified on the command line + bool VerifyExtraFiles(const list &extrafiles); + + // Attempt to match the data in the DiskFile with the source file + bool VerifyDataFile(DiskFile *diskfile, Par2RepairerSourceFile *sourcefile); + + // Perform a sliding window scan of the DiskFile looking for blocks of data that + // might belong to any of the source files (for which a verification packet was + // available). If a block of data might be from more than one source file, prefer + // the one specified by the "sourcefile" parameter. If the first data block + // found is for a different source file then "sourcefile" is changed accordingly. + virtual bool ScanDataFile(DiskFile *diskfile, // [in] The file being scanned + Par2RepairerSourceFile* &sourcefile, // [in/out] The source file matched + MatchType &matchtype, // [out] The type of match + MD5Hash &hashfull, // [out] The full hash of the file + MD5Hash &hash16k, // [out] The hash of the first 16k + u32 &count); // [out] The number of blocks found + + // Find out how much data we have found + void UpdateVerificationResults(void); + + // Check the verification results and report the results + bool CheckVerificationResults(void); + + // Rename any damaged or missnamed target files. + bool RenameTargetFiles(void); + + // Work out which files are being repaired, create them, and allocate + // target DataBlocks to them, and remember them for later verification. + bool CreateTargetFiles(void); + + // Work out which data blocks are available, which need to be copied + // directly to the output, and which need to be recreated, and compute + // the appropriate Reed Solomon matrix. + bool ComputeRSmatrix(void); + + // Allocate memory buffers for reading and writing data to disk. + bool AllocateBuffers(size_t memorylimit); + + // Read source data, process it through the RS matrix and write it to disk. + bool ProcessData(u64 blockoffset, size_t blocklength); + + // Verify that all of the reconstructed target files are now correct + bool VerifyTargetFiles(void); + + // Delete all of the partly reconstructed files + bool DeleteIncompleteTargetFiles(void); + + // Signals + virtual void sig_filename(std::string filename) {} + virtual void sig_progress(double progress) {} + virtual void sig_headers(ParHeaders* headers) {} + virtual void sig_done(std::string filename, int available, int total) {} + +protected: + ParHeaders* headers; // Headers + bool alreadyloaded; // Already loaded ? + CommandLine::NoiseLevel noiselevel; // OnScreen display + + string searchpath; // Where to find files on disk + + bool firstpacket; // Whether or not a valid packet has been found. + MD5Hash setid; // The SetId extracted from the first packet. + + map recoverypacketmap; // One recovery packet for each exponent value. + MainPacket *mainpacket; // One copy of the main packet. + CreatorPacket *creatorpacket; // One copy of the creator packet. + + DiskFileMap diskFileMap; + + map sourcefilemap;// Map from FileId to SourceFile + vector sourcefiles; // The source files + vector verifylist; // Those source files that are being repaired + + u64 blocksize; // The block size. + u64 chunksize; // How much of a block can be processed. + u32 sourceblockcount; // The total number of blocks + u32 availableblockcount; // How many undamaged blocks have been found + u32 missingblockcount; // How many blocks are missing + + bool blocksallocated; // Whether or not the DataBlocks have been allocated + vector sourceblocks; // The DataBlocks that will be read from disk + vector targetblocks; // The DataBlocks that will be written to disk + + u32 windowtable[256]; // Table for sliding CRCs + u32 windowmask; // Maks for sliding CRCs + + bool blockverifiable; // Whether and files can be verified at the block level + VerificationHashTable verificationhashtable; // Hash table for block verification + list unverifiablesourcefiles; // Files that are not block verifiable + + u32 completefilecount; // How many files are fully verified + u32 renamedfilecount; // How many files are verified but have the wrong name + u32 damagedfilecount; // How many files exist but are damaged + u32 missingfilecount; // How many files are completely missing + + vector inputblocks; // Which DataBlocks will be read from disk + vector copyblocks; // Which DataBlocks will copied back to disk + vector outputblocks; // Which DataBlocks have to calculated using RS + + ReedSolomon rs; // The Reed Solomon matrix. + + void *inputbuffer; // Buffer for reading DataBlocks (chunksize) + void *outputbuffer; // Buffer for writing DataBlocks (chunksize * missingblockcount) + + u64 progress; // How much data has been processed. + u64 totaldata; // Total amount of data to be processed. + u64 totalsize; // Total data size + + bool cancelled; // repair cancelled +}; + +#endif // __PAR2REPAIRER_H__ diff --git a/lib/par2/par2repairersourcefile.cpp b/lib/par2/par2repairersourcefile.cpp new file mode 100644 index 000000000..21e86a5eb --- /dev/null +++ b/lib/par2/par2repairersourcefile.cpp @@ -0,0 +1,159 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +Par2RepairerSourceFile::Par2RepairerSourceFile(DescriptionPacket *_descriptionpacket, + VerificationPacket *_verificationpacket) +{ + firstblocknumber = 0; + + descriptionpacket = _descriptionpacket; + verificationpacket = _verificationpacket; + +// verificationhashtable = 0; + + targetexists = false; + targetfile = 0; + completefile = 0; +} + +Par2RepairerSourceFile::~Par2RepairerSourceFile(void) +{ + delete descriptionpacket; + delete verificationpacket; + +// delete verificationhashtable; +} + + +void Par2RepairerSourceFile::SetDescriptionPacket(DescriptionPacket *_descriptionpacket) +{ + descriptionpacket = _descriptionpacket; +} + +void Par2RepairerSourceFile::SetVerificationPacket(VerificationPacket *_verificationpacket) +{ + verificationpacket = _verificationpacket; +} + +void Par2RepairerSourceFile::ComputeTargetFileName(string path) +{ + // Get a version of the filename compatible with the OS + string filename = DiskFile::TranslateFilename(descriptionpacket->FileName()); + + // Strip the path from the filename + string::size_type where; + if (string::npos != (where = filename.find_last_of('\\')) + || string::npos != (where = filename.find_last_of('/')) +#ifdef WIN32 + || string::npos != (where = filename.find_last_of(':')) +#endif + ) + { + filename = filename.substr(where+1); + } + + targetfilename = path + filename; +} + +string Par2RepairerSourceFile::TargetFileName(void) const +{ + return targetfilename; +} + +void Par2RepairerSourceFile::SetTargetFile(DiskFile *diskfile) +{ + targetfile = diskfile; +} + +DiskFile* Par2RepairerSourceFile::GetTargetFile(void) const +{ + return targetfile; +} + +void Par2RepairerSourceFile::SetTargetExists(bool exists) +{ + targetexists = exists; +} + +bool Par2RepairerSourceFile::GetTargetExists(void) const +{ + return targetexists; +} + +void Par2RepairerSourceFile::SetCompleteFile(DiskFile *diskfile) +{ + completefile = diskfile; +} + +DiskFile* Par2RepairerSourceFile::GetCompleteFile(void) const +{ + return completefile; +} + +// Remember which source and target blocks will be used by this file +// and set their lengths appropriately +void Par2RepairerSourceFile::SetBlocks(u32 _blocknumber, + u32 _blockcount, + vector::iterator _sourceblocks, + vector::iterator _targetblocks, + u64 blocksize) +{ + firstblocknumber = _blocknumber; + blockcount = _blockcount; + sourceblocks = _sourceblocks; + targetblocks = _targetblocks; + + if (blockcount > 0) + { + u64 filesize = descriptionpacket->FileSize(); + + vector::iterator sb = sourceblocks; + for (u32 blocknumber=0; blocknumberFileSize() + blocksize-1) / blocksize); + } + else + { + blockcount = 0; + } +} diff --git a/lib/par2/par2repairersourcefile.h b/lib/par2/par2repairersourcefile.h new file mode 100644 index 000000000..8a40c12a9 --- /dev/null +++ b/lib/par2/par2repairersourcefile.h @@ -0,0 +1,106 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __PAR2REPAIRERSOURCEFILE_H__ +#define __PAR2REPAIRERSOURCEFILE_H__ + +enum MatchType +{ + eNoMatch = 0, + ePartialMatch, + eFullMatch +}; + +// The Par2RepairerSourceFile object is used during verification and repair +// to record details about a particular source file and the data blocks +// for that file. + +class Par2RepairerSourceFile +{ +public: + // Construct the object and set the description and verification packets + Par2RepairerSourceFile(DescriptionPacket *descriptionpacket, + VerificationPacket *verificationpacket); + ~Par2RepairerSourceFile(void); + + // Get/Set the description packet + DescriptionPacket* GetDescriptionPacket(void) const {return descriptionpacket;} + void SetDescriptionPacket(DescriptionPacket *descriptionpacket); + + // Get/Set the verification packet + VerificationPacket* GetVerificationPacket(void) const {return verificationpacket;} + void SetVerificationPacket(VerificationPacket *verificationpacket); + + // Record the details as to which data blocks belong to this source + // file and set the length of each allocated block correctly. + void SetBlocks(u32 _blocknumber, + u32 _blockcount, + vector::iterator _sourceblocks, + vector::iterator _targetblocks, + u64 blocksize); + + // Determine the block count from the file size and block size. + void SetBlockCount(u64 blocksize); + + // Set/Get which DiskFile will contain the final repaired version of the file + void SetTargetFile(DiskFile *diskfile); + DiskFile* GetTargetFile(void) const; + + // Set/Get whether or not the target file actually exists + void SetTargetExists(bool exists); + bool GetTargetExists(void) const; + + // Set/Get which DiskFile contains a full undamaged version of the source file + void SetCompleteFile(DiskFile *diskfile); + DiskFile* GetCompleteFile(void) const; + + // Compute/Get the filename for the final repaired version of the file + void ComputeTargetFileName(string path); + string TargetFileName(void) const; + + // Get the number of blocks that the file uses + u32 BlockCount(void) const {return blockcount;} + + // Get the relative block number of the first block in the file + u32 FirstBlockNumber(void) const {return firstblocknumber;} + + // Get the first source DataBlock for the file + vector::iterator SourceBlocks(void) const {return sourceblocks;} + + // Get the first target DataBlock for the file + vector::iterator TargetBlocks(void) const {return targetblocks;} + +protected: + DescriptionPacket *descriptionpacket; // The file description packet + VerificationPacket *verificationpacket; // The file verification packet + + u32 blockcount; // The number of DataBlocks in the file + u32 firstblocknumber; // The block number of the first DataBlock + + vector::iterator sourceblocks; // The first source DataBlock + vector::iterator targetblocks; // The first target DataBlock + + bool targetexists; // Whether the target file exists + DiskFile *targetfile; // The final version of the file + DiskFile *completefile; // A complete version of the file + + string targetfilename; // The filename of the target file +}; + +#endif // __PAR2REPAIRERSOURCEFILE_H__ diff --git a/lib/par2/parheaders.cpp b/lib/par2/parheaders.cpp new file mode 100644 index 000000000..372ff42f7 --- /dev/null +++ b/lib/par2/parheaders.cpp @@ -0,0 +1,32 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "parheaders.h" + +ParHeaders::ParHeaders(void) { + packets = -1; + recovery_block = -1; + recoverable_files = -1; + other_files = -1; + block_size = -1; + data_blocks = -1; + data_size = -1; + setid = ""; + chunk_size = -1; +} diff --git a/lib/par2/parheaders.h b/lib/par2/parheaders.h new file mode 100644 index 000000000..9fafff520 --- /dev/null +++ b/lib/par2/parheaders.h @@ -0,0 +1,36 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include +#include +class ParHeaders { + public: + std::string setid; + int packets; + int recovery_block; + int recoverable_files; + int other_files; + long long block_size; + int data_blocks; + long long data_size; + long long chunk_size; + + ParHeaders(void); + +}; diff --git a/lib/par2/recoverypacket.cpp b/lib/par2/recoverypacket.cpp new file mode 100644 index 000000000..8ca1e1963 --- /dev/null +++ b/lib/par2/recoverypacket.cpp @@ -0,0 +1,127 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +RecoveryPacket::RecoveryPacket(void) +{ + diskfile = NULL; + offset = 0; + packetcontext = NULL; +} + +RecoveryPacket::~RecoveryPacket(void) +{ + delete packetcontext; +} + +// Create a recovery packet. + +// The packet header can be almost completely filled in using the supplied +// information and computation of the hash of the packet started immediately. +// The hash will be updated as new data is written to the packet. + +void RecoveryPacket::Create(DiskFile *_diskfile, + u64 _offset, + u64 _blocksize, + u32 _exponent, + const MD5Hash &_setid) +{ + diskfile = _diskfile; + offset = _offset; + + // Record everything we know in the packet. + packet.header.magic = packet_magic; + packet.header.length = sizeof(packet) + _blocksize; + //packet.header.hash; // Not known yet. + packet.header.setid = _setid; + packet.header.type = recoveryblockpacket_type; + packet.exponent = _exponent; + + // Start computation of the packet hash + packetcontext = new MD5Context; + packetcontext->Update(&packet.header.setid, + sizeof(RECOVERYBLOCKPACKET)-offsetof(RECOVERYBLOCKPACKET, header.setid)); + + // Set the data block to immediatly follow the header on disk + datablock.SetLocation(_diskfile, _offset + sizeof(packet)); + datablock.SetLength(_blocksize); +} + +// Write data from the buffer to the data block on disk +bool RecoveryPacket::WriteData(u64 position, + size_t size, + const void *buffer) +{ + // Update the packet hash + packetcontext->Update(buffer, size); + + // Write the data to the data block + size_t wrote; + return datablock.WriteData(position, size, buffer, wrote); +} + +// Write the header of the packet to disk +bool RecoveryPacket::WriteHeader(void) +{ + // Finish computing the packet hash + packetcontext->Final(packet.header.hash); + + // Write the header to disk + return diskfile->Write(offset, &packet, sizeof(packet)); +} + +// Load the recovery packet from disk. +// +// The header of the packet will already have been read from disk. The only +// thing that actually needs to be read is the exponent value. +// The recovery data is not read from disk at this point. Its location +// is recovered in the DataBlock object. + +bool RecoveryPacket::Load(DiskFile *_diskfile, + u64 _offset, + PACKET_HEADER &_header) +{ + diskfile = _diskfile; + offset = _offset; + + // Is the packet actually large enough + if (_header.length <= sizeof(packet)) + { + return false; + } + + // Save the fixed header + packet.header = _header; + + // Set the data block to immediatly follow the header on disk + datablock.SetLocation(diskfile, offset + sizeof(packet)); + datablock.SetLength(packet.header.length - sizeof(packet)); + + // Read the rest of the packet header + return diskfile->Read(offset + sizeof(packet.header), &packet.exponent, sizeof(packet)-sizeof(packet.header)); +} diff --git a/lib/par2/recoverypacket.h b/lib/par2/recoverypacket.h new file mode 100644 index 000000000..f16b7e81e --- /dev/null +++ b/lib/par2/recoverypacket.h @@ -0,0 +1,100 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __RECOVERYPACKET_H__ +#define __RECOVERYPACKET_H__ + +// The RecoveryPacket object is used to access a specific recovery +// packet within a recovery file (for when creating them, or using +// them during a repair operation). + +// Because the actual recovery data for the packet may be large, the +// RecoveryPacket object only contains a copy of packet header and +// exponent value for the block and uses a DataBlock object for +// all accesses to the actual recovery data in the packet. + +class RecoveryPacket +{ +public: + RecoveryPacket(void); + ~RecoveryPacket(void); + +public: + // Create a recovery packet for a specified file. + void Create(DiskFile *diskfile, // Which file will the packet be stored in + u64 offset, // At what offset will the packet be stored + u64 blocksize, // How much recovery data will it contain + u32 exponent, // What exponent value will be used + const MD5Hash &setid); // What is the SetId + // Write some data to the recovery data block and update the recovery packet. + bool WriteData(u64 position, // Relative position within the data block + size_t size, // Size of data to write to block + const void *buffer); // Buffer containing the data to write + // Finish computing the hash of the recovery packet and write the header to disk. + bool WriteHeader(void); + +public: + // Load a recovery packet from a specified file + bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + +public: + // Get the lenght of the packet. + u64 PacketLength(void) const; + + // The the exponent of the packet. + u32 Exponent(void) const; + + // The length of the recovery data + u64 BlockSize(void) const; + + // The data block + DataBlock* GetDataBlock(void); + +protected: + DiskFile *diskfile; // The specific file that this packet is stored in + u64 offset; // The offset at which the packet is stored + + RECOVERYBLOCKPACKET packet; // The packet (excluding the actual recovery data) + + MD5Context *packetcontext; // MD5 Context used to compute the packet hash + + DataBlock datablock; // The recovery data block. +}; + +inline u64 RecoveryPacket::PacketLength(void) const +{ + return packet.header.length; +} + +inline u32 RecoveryPacket::Exponent(void) const +{ + return packet.exponent; +} + +inline u64 RecoveryPacket::BlockSize(void) const +{ + return packet.header.length - sizeof(packet); +} + +inline DataBlock* RecoveryPacket::GetDataBlock(void) +{ + return &datablock; +} + +#endif // __RECOVERYPACKET_H__ diff --git a/lib/par2/reedsolomon.cpp b/lib/par2/reedsolomon.cpp new file mode 100644 index 000000000..7ef804bce --- /dev/null +++ b/lib/par2/reedsolomon.cpp @@ -0,0 +1,371 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +u32 gcd(u32 a, u32 b) +{ + if (a && b) + { + while (a && b) + { + if (a>b) + { + a = a%b; + } + else + { + b = b%a; + } + } + + return a+b; + } + else + { + return 0; + } +} + +template <> bool ReedSolomon::SetInput(const vector &present) +{ + inputcount = (u32)present.size(); + + datapresentindex = new u32[inputcount]; + datamissingindex = new u32[inputcount]; + database = new G::ValueType[inputcount]; + + G::ValueType base = 1; + + for (unsigned int index=0; index bool ReedSolomon::SetInput(u32 count) +{ + inputcount = count; + + datapresentindex = new u32[inputcount]; + datamissingindex = new u32[inputcount]; + database = new G::ValueType[inputcount]; + + G::ValueType base = 1; + + for (unsigned int index=0; index bool ReedSolomon::Process(size_t size, u32 inputindex, const void *inputbuffer, u32 outputindex, void *outputbuffer) +{ + // Look up the appropriate element in the RS matrix + Galois8 factor = leftmatrix[outputindex * (datapresent + datamissing) + inputindex]; + + // Do nothing if the factor happens to be 0 + if (factor == 0) + return eSuccess; + +#ifdef LONGMULTIPLY + // The 8-bit long multiplication tables + Galois8 *table = glmt->tables; + + // Split the factor into Low and High bytes + unsigned int fl = (factor >> 0) & 0xff; + + // Get the four separate multiplication tables + Galois8 *LL = &table[(0*256 + fl) * 256 + 0]; // factor.low * source.low + + // Combine the four multiplication tables into two + unsigned int L[256]; + + unsigned int *pL = &L[0]; + + for (unsigned int i=0; i<256; i++) + { + *pL = *LL; + + pL++; + LL++; + } + + // Treat the buffers as arrays of 32-bit unsigned ints. + u32 *src4 = (u32 *)inputbuffer; + u32 *end4 = (u32 *)&((u8*)inputbuffer)[size & ~3]; + u32 *dst4 = (u32 *)outputbuffer; + + // Process the data + while (src4 < end4) + { + u32 s = *src4++; + + // Use the two lookup tables computed earlier + *dst4++ ^= (L[(s >> 0) & 0xff] ) + ^ (L[(s >> 8) & 0xff] << 8 ) + ^ (L[(s >> 16)& 0xff] << 16) + ^ (L[(s >> 24)& 0xff] << 24); + } + + // Process any left over bytes at the end of the buffer + if (size & 3) + { + u8 *src1 = &((u8*)inputbuffer)[size & ~3]; + u8 *end1 = &((u8*)inputbuffer)[size]; + u8 *dst1 = &((u8*)outputbuffer)[size & ~3]; + + // Process the data + while (src1 < end1) + { + u8 s = *src1++; + *dst1++ ^= L[s]; + } + } +#else + // Treat the buffers as arrays of 16-bit Galois values. + + Galois8 *src = (Galois8 *)inputbuffer; + Galois8 *end = (Galois8 *)&((u8*)inputbuffer)[size]; + Galois8 *dst = (Galois8 *)outputbuffer; + + // Process the data + while (src < end) + { + *dst++ += *src++ * factor; + } +#endif + + return eSuccess; +} + + + +//////////////////////////////////////////////////////////////////////////////////////////// + + + +// Set which of the source files are present and which are missing +// and compute the base values to use for the vandermonde matrix. +template <> bool ReedSolomon::SetInput(const vector &present) +{ + inputcount = (u32)present.size(); + + datapresentindex = new u32[inputcount]; + datamissingindex = new u32[inputcount]; + database = new G::ValueType[inputcount]; + + unsigned int logbase = 0; + + for (unsigned int index=0; index= G::Limit) + { + cerr << "Too many input blocks for Reed Solomon matrix." << endl; + return false; + } + G::ValueType base = G(logbase++).ALog(); + + database[index] = base; + } + + return true; +} + +// Record that the specified number of source files are all present +// and compute the base values to use for the vandermonde matrix. +template <> bool ReedSolomon::SetInput(u32 count) +{ + inputcount = count; + + datapresentindex = new u32[inputcount]; + datamissingindex = new u32[inputcount]; + database = new G::ValueType[inputcount]; + + unsigned int logbase = 0; + + for (unsigned int index=0; index= G::Limit) + { + cerr << "Too many input blocks for Reed Solomon matrix." << endl; + return false; + } + G::ValueType base = G(logbase++).ALog(); + + database[index] = base; + } + + return true; +} + +template <> bool ReedSolomon::Process(size_t size, u32 inputindex, const void *inputbuffer, u32 outputindex, void *outputbuffer) +{ + // Look up the appropriate element in the RS matrix + + Galois16 factor = leftmatrix[outputindex * (datapresent + datamissing) + inputindex]; + // Do nothing if the factor happens to be 0 + if (factor == 0) + return eSuccess; + +#ifdef LONGMULTIPLY + // The 8-bit long multiplication tables + Galois16 *table = glmt->tables; + + // Split the factor into Low and High bytes + unsigned int fl = (factor >> 0) & 0xff; + unsigned int fh = (factor >> 8) & 0xff; + + // Get the four separate multiplication tables + Galois16 *LL = &table[(0*256 + fl) * 256 + 0]; // factor.low * source.low + Galois16 *LH = &table[(1*256 + fl) * 256 + 0]; // factor.low * source.high + Galois16 *HL = &table[(1*256 + 0) * 256 + fh]; // factor.high * source.low + Galois16 *HH = &table[(2*256 + fh) * 256 + 0]; // factor.high * source.high + + // Combine the four multiplication tables into two + unsigned int L[256]; + unsigned int H[256]; + +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int *pL = &L[0]; + unsigned int *pH = &H[0]; +#else + unsigned int *pL = &H[0]; + unsigned int *pH = &L[0]; +#endif + + for (unsigned int i=0; i<256; i++) + { +#if __BYTE_ORDER == __LITTLE_ENDIAN + *pL = *LL + *HL; +#else + unsigned int temp = *LL + *HL; + *pL = (temp >> 8) & 0xff | (temp << 8) & 0xff00; +#endif + + pL++; + LL++; + HL+=256; + +#if __BYTE_ORDER == __LITTLE_ENDIAN + *pH = *LH + *HH; +#else + temp = *LH + *HH; + *pH = (temp >> 8) & 0xff | (temp << 8) & 0xff00; +#endif + + pH++; + LH++; + HH++; + } + + // Treat the buffers as arrays of 32-bit unsigned ints. + u32 *src = (u32 *)inputbuffer; + u32 *end = (u32 *)&((u8*)inputbuffer)[size]; + u32 *dst = (u32 *)outputbuffer; + + // Process the data + while (src < end) + { + u32 s = *src++; + + // Use the two lookup tables computed earlier +//#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 d = *dst ^ (L[(s >> 0) & 0xff] ) + ^ (H[(s >> 8) & 0xff] ) + ^ (L[(s >> 16)& 0xff] << 16) + ^ (H[(s >> 24)& 0xff] << 16); + *dst++ = d; +//#else +// *dst++ ^= (L[(s >> 8) & 0xff] ) +// ^ (H[(s >> 0) & 0xff] ) +// ^ (L[(s >> 24)& 0xff] << 16) +// ^ (H[(s >> 16)& 0xff] << 16); +//#endif + } +#else + // Treat the buffers as arrays of 16-bit Galois values. + + // BUG: This only works for __LITTLE_ENDIAN + Galois16 *src = (Galois16 *)inputbuffer; + Galois16 *end = (Galois16 *)&((u8*)inputbuffer)[size]; + Galois16 *dst = (Galois16 *)outputbuffer; + + // Process the data + while (src < end) + { + *dst++ += *src++ * factor; + } +#endif + + return eSuccess; +} + diff --git a/lib/par2/reedsolomon.h b/lib/par2/reedsolomon.h new file mode 100644 index 000000000..79a9bc1e3 --- /dev/null +++ b/lib/par2/reedsolomon.h @@ -0,0 +1,510 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __REEDSOLOMON_H__ +#define __REEDSOLOMON_H__ + +// The ReedSolomon object is used to calculate and store the matrix +// used during recovery block creation or data block reconstruction. +// +// During initialisation, one RSOutputRow object is created for each +// recovery block that either needs to be created or is available for +// use. + +class RSOutputRow +{ +public: + RSOutputRow(void) {}; + RSOutputRow(bool _present, u16 _exponent) : present(_present), exponent(_exponent) {} + +public: + bool present; + u16 exponent; +}; + +template +class ReedSolomon +{ +public: + typedef g G; + + ReedSolomon(void); + ~ReedSolomon(void); + + // Set which input blocks are present or missing + bool SetInput(const vector &present); // Some input blocks are present + bool SetInput(u32 count); // All input blocks are present + + // Set which output block are available or need to be computed + bool SetOutput(bool present, u16 exponent); + bool SetOutput(bool present, u16 lowexponent, u16 highexponent); + + // Compute the RS Matrix + bool Compute(CommandLine::NoiseLevel noiselevel); + + // Process a block of data + bool Process(size_t size, // The size of the block of data + u32 inputindex, // The column in the RS matrix + const void *inputbuffer, // Buffer containing input data + u32 outputindex, // The row in the RS matrix + void *outputbuffer); // Buffer containing output data + +protected: + // Perform Gaussian Elimination + bool GaussElim(CommandLine::NoiseLevel noiselevel, + unsigned int rows, + unsigned int leftcols, + G *leftmatrix, + G *rightmatrix, + unsigned int datamissing); + +protected: + u32 inputcount; // Total number of input blocks + + u32 datapresent; // Number of input blocks that are present + u32 datamissing; // Number of input blocks that are missing + u32 *datapresentindex; // The index numbers of the data blocks that are present + u32 *datamissingindex; // The index numbers of the data blocks that are missing + + typename G::ValueType *database;// The "base" value to use for each input block + + u32 outputcount; // Total number of output blocks + + u32 parpresent; // Number of output blocks that are present + u32 parmissing; // Number of output blocks that are missing + u32 *parpresentindex; // The index numbers of the output blocks that are present + u32 *parmissingindex; // The index numbers of the output blocks that are missing + + vector outputrows; // Details of the output blocks + + G *leftmatrix; // The main matrix + + // When the matrices are initialised: values of the form base ^ exponent are + // stored (where the base values are obtained from database[] and the exponent + // values are obtained from outputrows[]). + +#ifdef LONGMULTIPLY + GaloisLongMultiplyTable *glmt; // A multiplication table used by Process() +#endif +}; + +template +inline ReedSolomon::ReedSolomon(void) +{ + inputcount = 0; + + datapresent = 0; + datamissing = 0; + datapresentindex = 0; + datamissingindex = 0; + database = 0; + + outputrows.empty(); + + outputcount = 0; + + parpresent = 0; + parmissing = 0; + parpresentindex = 0; + parmissingindex = 0; + + leftmatrix = 0; + +#ifdef LONGMULTIPLY + glmt = new GaloisLongMultiplyTable; +#endif +} + +template +inline ReedSolomon::~ReedSolomon(void) +{ + delete [] datapresentindex; + delete [] datamissingindex; + delete [] database; + delete [] parpresentindex; + delete [] parmissingindex; + delete [] leftmatrix; + +#ifdef LONGMULTIPLY + delete glmt; +#endif +} + +u32 gcd(u32 a, u32 b); + +// Record whether the recovery block with the specified +// exponent values is present or missing. +template +inline bool ReedSolomon::SetOutput(bool present, u16 exponent) +{ + // Store the exponent and whether or not the recovery block is present or missing + outputrows.push_back(RSOutputRow(present, exponent)); + + outputcount++; + + // Update the counts. + if (present) + { + parpresent++; + } + else + { + parmissing++; + } + + return true; +} + +// Record whether the recovery blocks with the specified +// range of exponent values are present or missing. +template +inline bool ReedSolomon::SetOutput(bool present, u16 lowexponent, u16 highexponent) +{ + for (unsigned int exponent=lowexponent; exponent<=highexponent; exponent++) + { + if (!SetOutput(present, exponent)) + return false; + } + + return true; +} + +// Construct the Vandermonde matrix and solve it if necessary +template +inline bool ReedSolomon::Compute(CommandLine::NoiseLevel noiselevel) +{ + u32 outcount = datamissing + parmissing; + u32 incount = datapresent + datamissing; + + if (datamissing > parpresent) + { + cerr << "Not enough recovery blocks." << endl; + return false; + } + else if (outcount == 0) + { + cerr << "No output blocks." << endl; + return false; + } + + if (noiselevel > CommandLine::nlQuiet) + cout << "Computing Reed Solomon matrix." << endl; + + /* Layout of RS Matrix: + + parpresent + datapresent datamissing datamissing parmissing + / | \ / | \ + parpresent | (ppi[row])| | | (ppi[row])| | + datamissing | ^ | I | | ^ | 0 | + |(dpi[col]) | | |(dmi[col]) | | + +---------------------+-------------+ +---------------------+-----------+ + | (pmi[row])| | | (pmi[row])| | + parmissing | ^ | 0 | | ^ | I | + |(dpi[col]) | | |(dmi[col]) | | + \ | / \ | / + */ + + // Allocate the left hand matrix + + leftmatrix = new G[outcount * incount]; + memset(leftmatrix, 0, outcount * incount * sizeof(G)); + + // Allocate the right hand matrix only if we are recovering + + G *rightmatrix = 0; + if (datamissing > 0) + { + rightmatrix = new G[outcount * outcount]; + memset(rightmatrix, 0, outcount *outcount * sizeof(G)); + } + + // Fill in the two matrices: + + vector::const_iterator outputrow = outputrows.begin(); + + // One row for each present recovery block that will be used for a missing data block + for (unsigned int row=0; row CommandLine::nlQuiet) + { + int progress = row * 1000 / (datamissing+parmissing); + cout << "Constructing: " << progress/10 << '.' << progress%10 << "%\r" << flush; + } + + // Get the exponent of the next present recovery block + while (!outputrow->present) + { + outputrow++; + } + u16 exponent = outputrow->exponent; + + // One column for each present data block + for (unsigned int col=0; col 0) + { + // One column for each missing data block + for (unsigned int col=0; col CommandLine::nlQuiet) + { + int progress = (row+datamissing) * 1000 / (datamissing+parmissing); + cout << "Constructing: " << progress/10 << '.' << progress%10 << "%\r" << flush; + } + + // Get the exponent of the next missing recovery block + while (outputrow->present) + { + outputrow++; + } + u16 exponent = outputrow->exponent; + + // One column for each present data block + for (unsigned int col=0; col 0) + { + // One column for each missing data block + for (unsigned int col=0; col CommandLine::nlQuiet) + cout << "Constructing: done." << endl; + + // Solve the matrices only if recovering data + if (datamissing > 0) + { + // Perform Gaussian Elimination and then delete the right matrix (which + // will no longer be required). + bool success = GaussElim(noiselevel, outcount, incount, leftmatrix, rightmatrix, datamissing); + delete [] rightmatrix; + return success; + } + + return true; +} + +// Use Gaussian Elimination to solve the matrices +template +inline bool ReedSolomon::GaussElim(CommandLine::NoiseLevel noiselevel, unsigned int rows, unsigned int leftcols, G *leftmatrix, G *rightmatrix, unsigned int datamissing) +{ + if (noiselevel == CommandLine::nlDebug) + { + for (unsigned int row=0; row8?4:2) << setfill('0') + << (unsigned int)leftmatrix[row*leftcols+col]; + } + cout << ((row==0) ? " \\ /" : (row==rows-1) ? " / \\" : " | |"); + for (unsigned int col=0; col8?4:2) << setfill('0') + << (unsigned int)rightmatrix[row*rows+col]; + } + cout << ((row==0) ? " \\" : (row==rows-1) ? " /" : " | |"); + cout << endl; + + cout << dec << setw(0) << setfill(' '); + } + } + + // Because the matrices being operated on are Vandermonde matrices + // they are guaranteed not to be singular. + + // Additionally, because Galois arithmetic is being used, all calulations + // involve exact values with no loss of precision. It is therefore + // not necessary to carry out any row or column swapping. + + // Solve one row at a time + + int progress = 0; + + // For each row in the matrix + for (unsigned int row=0; row CommandLine::nlQuiet) + { + int newprogress = (row*rows+row2) * 1000 / (datamissing*rows); + if (progress != newprogress) + { + progress = newprogress; + cout << "Solving: " << progress/10 << '.' << progress%10 << "%\r" << flush; + } + } + + if (row != row2) + { + // Get the scaling factor for this row. + G scalevalue = rightmatrix[row2 * rows + row]; + + if (scalevalue == 1) + { + // If the scaling factor happens to be 1, just subtract rows + for (unsigned int col=0; col CommandLine::nlQuiet) + cout << "Solving: done." << endl; + if (noiselevel == CommandLine::nlDebug) + { + for (unsigned int row=0; row8?4:2) << setfill('0') + << (unsigned int)leftmatrix[row*leftcols+col]; + } + cout << ((row==0) ? " \\ /" : (row==rows-1) ? " / \\" : " | |"); + for (unsigned int col=0; col8?4:2) << setfill('0') + << (unsigned int)rightmatrix[row*rows+col]; + } + cout << ((row==0) ? " \\" : (row==rows-1) ? " /" : " | |"); + cout << endl; + + cout << dec << setw(0) << setfill(' '); + } + } + + return true; +} + + +#endif // __REEDSOLOMON_H__ diff --git a/lib/par2/verificationhashtable.cpp b/lib/par2/verificationhashtable.cpp new file mode 100644 index 000000000..802d13bdf --- /dev/null +++ b/lib/par2/verificationhashtable.cpp @@ -0,0 +1,107 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +VerificationHashTable::VerificationHashTable(void) +{ + hashmask = 0; + hashtable = 0; +} + +VerificationHashTable::~VerificationHashTable(void) +{ + // Destroy the hash table + if (hashtable) + { + for (unsigned int entry=0; entry<=hashmask; entry++) + { + delete hashtable[entry]; + } + } + + delete [] hashtable; +} + +// Allocate the hash table with a reasonable size +void VerificationHashTable::SetLimit(u32 limit) +{ + // Pick a good size for the hash table + hashmask = 256; + while (hashmask < limit && hashmask < 65536) + { + hashmask <<= 1; + } + + // Allocate and clear the hash table + hashtable = new VerificationHashEntry*[hashmask]; + memset(hashtable, 0, hashmask * sizeof(hashtable[0])); + + hashmask--; +} + +// Load data from a verification packet +void VerificationHashTable::Load(Par2RepairerSourceFile *sourcefile, u64 blocksize) +{ + VerificationHashEntry *preventry = 0; + + // Get information from the sourcefile + VerificationPacket *verificationpacket = sourcefile->GetVerificationPacket(); + u32 blockcount = verificationpacket->BlockCount(); + + // Iterate throught the data blocks for the source file and the verification + // entries in the verification packet. + vector::iterator sourceblocks = sourcefile->SourceBlocks(); + const FILEVERIFICATIONENTRY *verificationentry = verificationpacket->VerificationEntry(0); + u32 blocknumber = 0; + + while (blocknumberInsert(&hashtable[entry->Checksum() & hashmask]); + + // Make the previous entry point forwards to this one + if (preventry) + { + preventry->Next(entry); + } + preventry = entry; + + ++blocknumber; + ++sourceblocks; + ++verificationentry; + } +} diff --git a/lib/par2/verificationhashtable.h b/lib/par2/verificationhashtable.h new file mode 100644 index 000000000..04a9e9d86 --- /dev/null +++ b/lib/par2/verificationhashtable.h @@ -0,0 +1,445 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __VERIFICATIONHASHTABLE_H__ +#define __VERIFICATIONHASHTABLE_H__ + +class Par2RepairerSourceFile; +class VerificationHashTable; + +// The VerificationHashEntry objects form the nodes of a binary trees stored +// in a VerificationHashTable object. + +// There is one VerificationHashEntry object for each data block in the original +// source files. + +class VerificationHashEntry +{ +public: + VerificationHashEntry(Par2RepairerSourceFile *_sourcefile, + DataBlock *_datablock, + bool _firstblock, + const FILEVERIFICATIONENTRY *_verificationentry) + { + sourcefile = _sourcefile; + datablock = _datablock; + firstblock = _firstblock; + + crc = _verificationentry->crc; + hash = _verificationentry->hash; + + left = right = same = next = 0; + } + + ~VerificationHashEntry(void) + { + delete left; + delete right; + delete same; + } + + // Insert the current object is a child of the specified parent + void Insert(VerificationHashEntry **parent); + + // Search (starting at the specified parent) for an object with a matching crc + static const VerificationHashEntry* Search(const VerificationHashEntry *entry, u32 crc); + + // Search (starting at the specified parent) for an object with a matching hash + static const VerificationHashEntry* Search(const VerificationHashEntry *entry, const MD5Hash &hash); + + // Comparison operators for searching + bool operator <(const VerificationHashEntry &r) const + { + return (crc < r.crc) || ((crc == r.crc) && (hash < r.hash)); + } + bool operator >(const VerificationHashEntry &r) const + { + return (crc > r.crc) || ((crc == r.crc) && (hash > r.hash)); + } + bool operator ==(const VerificationHashEntry &r) const + { + return (crc == r.crc) && (hash == r.hash); + } + bool operator <=(const VerificationHashEntry &r) const {return !operator>(r);} + bool operator >=(const VerificationHashEntry &r) const {return !operator<(r);} + bool operator !=(const VerificationHashEntry &r) const {return !operator==(r);} + + // Data + Par2RepairerSourceFile* SourceFile(void) const {return sourcefile;} + const DataBlock* GetDataBlock(void) const {return datablock;} + bool FirstBlock(void) const {return firstblock;} + + // Set/Check the associated datablock + void SetBlock(DiskFile *diskfile, u64 offset) const; + bool IsSet(void) const; + + u32 Checksum(void) const {return crc;} + const MD5Hash& Hash(void) const {return hash;} + + VerificationHashEntry* Same(void) const {return same;} + VerificationHashEntry* Next(void) const {return next;} + void Next(VerificationHashEntry *_next) {next = _next;} + +protected: + // Data + Par2RepairerSourceFile *sourcefile; + DataBlock *datablock; + bool firstblock; + + u32 crc; + MD5Hash hash; + +protected: + // Binary tree + VerificationHashEntry *left; + VerificationHashEntry *right; + + // Linked list of entries with the same crc and hash + VerificationHashEntry *same; + + // Linked list of entries in sequence for same file + VerificationHashEntry *next; +}; + +inline void VerificationHashEntry::SetBlock(DiskFile *diskfile, u64 offset) const +{ + datablock->SetLocation(diskfile, offset); +} + +inline bool VerificationHashEntry::IsSet(void) const +{ + return datablock->IsSet(); +} + +// Insert a new entry in the tree +inline void VerificationHashEntry::Insert(VerificationHashEntry **parent) +{ + while (*parent) + { + if (**parent < *this) + { + parent = &(*parent)->right; + } + else if (**parent > *this) + { + parent = &(*parent)->left; + } + else + { + break; + } + } + + while (*parent) + { + parent = &(*parent)->same; + } + + *parent = this; +} + +// Search the tree for an entry with the correct crc +inline const VerificationHashEntry* VerificationHashEntry::Search(const VerificationHashEntry *entry, u32 crc) +{ + while (entry) + { + if (entry->crc < crc) + { + entry = entry->right; + } + else if (entry->crc > crc) + { + entry = entry->left; + } + else + { + break; + } + } + + return entry; +} + +// Search the tree for an entry with the correct hash +inline const VerificationHashEntry* VerificationHashEntry::Search(const VerificationHashEntry *entry, const MD5Hash &hash) +{ + u32 crc = entry->crc; + + while (entry) + { + if ((entry->crc < crc) || ((entry->crc == crc) && (entry->hash < hash))) + { + entry = entry->right; + } + else if ((entry->crc > crc) || ((entry->crc == crc) && (entry->hash > hash))) + { + entry = entry->left; + } + else + { + break; + } + } + + return entry; +} + +// The VerificationHashTable object contains all of the VerificationHashEntry objects +// and is used to find matches for blocks of data in a target file that is being +// scanned. + +// It is initialised by loading data from all available verification packets for the +// source files. + +class VerificationHashTable +{ +public: + VerificationHashTable(void); + ~VerificationHashTable(void); + + void SetLimit(u32 limit); + + // Load the data from the verification packet + void Load(Par2RepairerSourceFile *sourcefile, u64 blocksize); + + // Try to find a match. + // nextentry - The entry which we expect to find next. This is used + // when a sequence of matches are found. + // sourcefile - Which source file we would prefer to find a match for + // if there are more than one possible match (with the + // same crc and hash). + // checksummer - Provides the crc and hash values being tested. + // duplicate - Set on return if the match would have been valid except + // for the fact that the block has already been found. + const VerificationHashEntry* FindMatch(const VerificationHashEntry *nextentry, + const Par2RepairerSourceFile *sourcefile, + FileCheckSummer &checksummer, + bool &duplicate) const; + + // Look up based on the block crc + const VerificationHashEntry* Lookup(u32 crc) const; + + // Continue lookup based on the block hash + const VerificationHashEntry* Lookup(const VerificationHashEntry *entry, + const MD5Hash &hash); + +protected: + VerificationHashEntry **hashtable; + unsigned int hashmask; +}; + +// Search for an entry with the specified crc +inline const VerificationHashEntry* VerificationHashTable::Lookup(u32 crc) const +{ + if (hashmask) + { + return VerificationHashEntry::Search(hashtable[crc & hashmask], crc); + } + + return 0; +} + +// Search for an entry with the specified hash +inline const VerificationHashEntry* VerificationHashTable::Lookup(const VerificationHashEntry *entry, + const MD5Hash &hash) +{ + return VerificationHashEntry::Search(entry, hash); +} + +inline const VerificationHashEntry* VerificationHashTable::FindMatch(const VerificationHashEntry *suggestedentry, + const Par2RepairerSourceFile *sourcefile, + FileCheckSummer &checksummer, + bool &duplicate) const +{ + duplicate = false; + + // Get the current checksum from the checksummer + u32 crc = checksummer.Checksum(); + + MD5Hash hash; + bool havehash = false; + + // Do we know what the next entry should be + if (0 != suggestedentry) + { + // Is the suggested match supposed to be the last one in the file + if (suggestedentry->Next() == 0) + { + // How long should the last block be + u64 length = suggestedentry->GetDataBlock()->GetLength(); + + // Get a short checksum from the checksummer + u32 checksum = checksummer.ShortChecksum(length); + + // Is the checksum correct + if (checksum == suggestedentry->Checksum()) + { + // Get a short hash from the checksummer + hash = checksummer.ShortHash(length); + + // If the hash matches as well, then return it + if (hash == suggestedentry->Hash()) + { + return suggestedentry; + } + } + } + // If the suggested entry has not already been found, compare the checksum + else if (!suggestedentry->IsSet() && suggestedentry->Checksum() == crc) + { + // Get the hash value from the checksummer + havehash = true; + hash = checksummer.Hash(); + + // If the hash value matches, then return it. + if (hash == suggestedentry->Hash()) + { + return suggestedentry; + } + } + } + + // Look for other possible matches for the checksum + const VerificationHashEntry *nextentry = VerificationHashEntry::Search(hashtable[crc & hashmask], crc); + if (0 == nextentry) + return 0; + + // If we don't have the hash yet, get it + if (!havehash) + { + hash = checksummer.Hash(); + } + + // Look for an entry with a matching hash + nextentry = VerificationHashEntry::Search(nextentry, hash); + if (0 == nextentry) + return 0; + + // Is there one match with the same checksum and hash, or many + if (nextentry->Same() == 0) + { + // If the match is for a block that is part of a target file + // for which we already have a complete match, then don't + // return it. + if (nextentry->SourceFile()->GetCompleteFile() != 0) + { + duplicate = true; + return 0; + } + + // If we are close to the end of the file and the block + // length is wrong, don't return it because it is an + // invalid match + if (checksummer.ShortBlock() && checksummer.BlockLength() != nextentry->GetDataBlock()->GetLength()) + { + return 0; + } + + // If the match was at the start of the file and it is the first + // block for a target file, then return it. + if (nextentry->FirstBlock() && checksummer.Offset() == 0) + { + return nextentry; + } + + // Was this match actually the one which had originally been suggested + // but which has presumably already been found + if (nextentry == suggestedentry) + { + // Was the existing match in the same file as the new match + if (nextentry->IsSet() && + nextentry->GetDataBlock()->GetDiskFile() == checksummer.GetDiskFile()) + { + // Yes. Don't return it + duplicate = true; + return 0; + } + else + { + // No, it was in a different file. Return it. + // This ensures that we can find a perfect match for a target + // file even if some of the blocks had already been found + // in a different file. + return nextentry; + } + } + else + { + // return it only if it has not already been used + if (nextentry->IsSet()) + { + duplicate = true; + return 0; + } + + return nextentry; + } + } + + // Do we prefer to match entries for a particular source file + if (0 != sourcefile) + { + const VerificationHashEntry *currententry = nextentry; + nextentry = 0; + + // We don't want entries for the wrong source file, ones that + // have already been matched, or ones that are the wrong length + while (currententry && (currententry->SourceFile() != sourcefile || + currententry->IsSet() || + ((checksummer.ShortBlock() && checksummer.BlockLength()) != (currententry->GetDataBlock()->GetLength())) + ) + ) + { + // If we found an unused entry (which was presumably for the wrong + // source file) remember it (providing it is the correct length). + if (0 == nextentry && !(currententry->IsSet() || + ((checksummer.ShortBlock() && checksummer.BlockLength()) != (currententry->GetDataBlock()->GetLength())) + ) + ) + { + nextentry = currententry; + } + + currententry = currententry->Same(); + } + + // If we found an unused entry for the source file we want, return it + if (0 != currententry) + return currententry; + } + + // Check for an unused entry which is the correct length + while (nextentry && (nextentry->IsSet() || + ((checksummer.ShortBlock()) && (checksummer.BlockLength() != nextentry->GetDataBlock()->GetLength())) + ) + ) + { + nextentry = nextentry->Same(); + } + + // Return what we have found + if (nextentry == 0) + { + duplicate = true; + } + + return nextentry; +} + +#endif // __VERIFICATIONHASHTABLE_H__ diff --git a/lib/par2/verificationpacket.cpp b/lib/par2/verificationpacket.cpp new file mode 100644 index 000000000..2af203ee5 --- /dev/null +++ b/lib/par2/verificationpacket.cpp @@ -0,0 +1,103 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#include "par2cmdline.h" + +#ifdef _MSC_VER +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif +#endif + +// Create a packet large enough for the specified number of blocks + +bool VerificationPacket::Create(u32 _blockcount) +{ + blockcount = _blockcount; + + // Allocate a packet large enough to hold the required number of verification entries. + FILEVERIFICATIONPACKET *packet = (FILEVERIFICATIONPACKET*)AllocatePacket(sizeof(FILEVERIFICATIONPACKET) + blockcount * sizeof(FILEVERIFICATIONENTRY)); + + // Record everything we know in the packet. + packet->header.magic = packet_magic; + packet->header.length = packetlength; + //packet->header.hash; // Not known yet + //packet->header.setid; // Not known yet + packet->header.type = fileverificationpacket_type; + + //packet->fileid; // Not known yet + //packet->entries; // Not known yet + + return true; +} + +void VerificationPacket::FileId(const MD5Hash &fileid) +{ + assert(packetdata != 0); + + // Store the fileid in the packet. + ((FILEVERIFICATIONPACKET*)packetdata)->fileid = fileid; +} + +void VerificationPacket::SetBlockHashAndCRC(u32 blocknumber, const MD5Hash &hash, u32 crc) +{ + assert(packetdata != 0); + assert(blocknumber < blockcount); + + // Store the block hash and block crc in the packet. + FILEVERIFICATIONENTRY &entry = ((FILEVERIFICATIONPACKET*)packetdata)->entries[blocknumber]; + + entry.hash = hash; + entry.crc = crc; +} + +bool VerificationPacket::Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) +{ + // Is the packet large enough + if (header.length <= sizeof(FILEVERIFICATIONPACKET)) + { + return false; + } + + // Does the packet have a whole number of verification records + if (0 < ((header.length - sizeof(FILEVERIFICATIONPACKET)) % sizeof(FILEVERIFICATIONENTRY))) + { + return false; + } + + // Is the packet too large + if (header.length > sizeof(FILEVERIFICATIONPACKET) + 32768 * sizeof(FILEVERIFICATIONENTRY)) + { + return false; + } + + // Allocate the packet + FILEVERIFICATIONPACKET *packet = (FILEVERIFICATIONPACKET*)AllocatePacket((size_t)header.length); + packet->header = header; + + // How many blocks are there + blockcount = (u32)((((FILEVERIFICATIONPACKET*)packetdata)->header.length - sizeof(FILEVERIFICATIONPACKET)) / sizeof(FILEVERIFICATIONENTRY)); + + // Read the rest of the packet + return diskfile->Read(offset + sizeof(PACKET_HEADER), + &packet->fileid, + (size_t)packet->header.length - sizeof(PACKET_HEADER)); +} diff --git a/lib/par2/verificationpacket.h b/lib/par2/verificationpacket.h new file mode 100644 index 000000000..b30d298d9 --- /dev/null +++ b/lib/par2/verificationpacket.h @@ -0,0 +1,81 @@ +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline 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. +// +// par2cmdline 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef __VERIFICATIONPACKET_H__ +#define __VERIFICATIONPACKET_H__ + +// The file verification packet stores details that allow individual blocks +// of valid data within a damaged file to be identified. + +class VerificationPacket : public CriticalPacket +{ +public: + // Construct the packet + VerificationPacket(void) {}; + ~VerificationPacket(void) {}; + + // Create a packet large enough for the specified number of blocks + bool Create(u32 blockcount); + + // Set the fileid (computed from the file description packet). + void FileId(const MD5Hash &fileid); + + // Set the block hash and block crc for a specific data block. + void SetBlockHashAndCRC(u32 blocknumber, const MD5Hash &hash, u32 crc); + + // Load a verification packet from a specified file + bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); + + // Get the FileId + const MD5Hash& FileId(void) const; + + // Get the block count + u32 BlockCount(void) const; + + // Get a specific verification entry + const FILEVERIFICATIONENTRY* VerificationEntry(u32 blocknumber) const; + +protected: + u32 blockcount; +}; + +inline const MD5Hash& VerificationPacket::FileId(void) const +{ + assert(packetdata != 0); + + return ((FILEVERIFICATIONPACKET*)packetdata)->fileid; +} + +inline u32 VerificationPacket::BlockCount(void) const +{ + assert(packetdata != 0); + + return blockcount; +} + +inline const FILEVERIFICATIONENTRY* VerificationPacket::VerificationEntry(u32 blocknumber) const +{ + assert(packetdata != 0); + +// return &((FILEVERIFICATIONPACKET*)packetdata)->entries()[blocknumber]; + return &((FILEVERIFICATIONPACKET*)packetdata)->entries[blocknumber]; +} + + +#endif // __VERIFICATIONPACKET_H__ diff --git a/nzbget.conf b/nzbget.conf index be96ede17..206ee92b6 100644 --- a/nzbget.conf +++ b/nzbget.conf @@ -1099,12 +1099,6 @@ ParRepair=yes # Auto - a limited scan is performed first. If the par-checker # detects missing files, it scans other files in the # directory until all required files are found. -# -# NOTE: For par-check/repair NZBGet uses library libpar2. The widely -# used version 0.2 of the library has few bugs, sometimes causing -# a crash of the program. This is especially true when using "full" or -# "auto" par-scan. NZBGet is supplied with patches addressing these -# issues. Please apply the patches to libpar2 and recompile it. ParScan=auto # Quick file verification during par-check (yes, no). @@ -1113,8 +1107,7 @@ ParScan=auto # checksums calculated during download; quick verification is very fast # because it doesn't require the reading of files from disk, NZBGet # knows checksums of downloaded files and quickly compares them with -# checksums stored in the par-file. This feature requires a patched -# version of libpar2, see http://nzbget.net/libpar2 for details. +# checksums stored in the par-file. # # If the option is disabled the files are verified as usual. That's # slow. Use this if the quick verification doesn't work properly. @@ -1167,10 +1160,6 @@ HealthCheck=delete # affect the first stage of parcheck - verification of files. However the # verification speed is constant, it doesn't depend on files integrity and # therefore it is not necessary to limit the time needed for the first stage. -# -# NOTE: This option requires an extended version of libpar2 (the original -# version doesn't support the cancelling of repairing). Please refer to -# NZBGet's README for info on how to apply the patch to libpar2. ParTimeLimit=0 # Pause download queue during check/repair (yes, no). diff --git a/nzbget.vcproj b/nzbget.vcproj index d8cd6749b..4a87d6cff 100644 --- a/nzbget.vcproj +++ b/nzbget.vcproj @@ -38,7 +38,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +