From 87b108d6a1b6561092845e3738de9976afd457b1 Mon Sep 17 00:00:00 2001 From: Braden McDaniel Date: Fri, 1 Nov 2013 12:26:04 -0400 Subject: [PATCH] From clauflibitFriiDump-0.5.3.1 --- CMakeLists.txt | 161 ++++ Doxyfile | 284 ++++++ README.md | 4 - config.h.cmake | 42 + docs/AUTHORS | 41 + LICENSE => docs/COPYING | 45 +- docs/ChangeLog | 45 + docs/INSTALL | 1 + docs/NEWS | 53 ++ docs/README | 187 ++++ docs/README.technical | 234 +++++ docs/TODO | 13 + docs/options | 64 ++ libfriidump.pc.in | 11 + libfriidump/CMakeLists.txt | 105 +++ libfriidump/brickblocker.c | 22 + libfriidump/brickblocker.h | 20 + libfriidump/byteorder.h | 77 ++ libfriidump/constants.h | 46 + libfriidump/disc.c | 1675 +++++++++++++++++++++++++++++++++++ libfriidump/disc.h | 94 ++ libfriidump/dumper.c | 439 +++++++++ libfriidump/dumper.h | 60 ++ libfriidump/dvd_drive.c | 755 ++++++++++++++++ libfriidump/dvd_drive.h | 73 ++ libfriidump/ecma-267.c | 109 +++ libfriidump/ecma-267.h | 47 + libfriidump/hitachi.c | 147 +++ libfriidump/lite-on.c | 154 ++++ libfriidump/misc.c | 314 +++++++ libfriidump/misc.h | 140 +++ libfriidump/renesas.c | 132 +++ libfriidump/rs.c | 316 +++++++ libfriidump/rs.h | 10 + libfriidump/unscrambler.c | 370 ++++++++ libfriidump/unscrambler.h | 43 + libfriidump/vanilla_2064.c | 131 +++ libfriidump/vanilla_2384.c | 153 ++++ libfriidump/win32compat.c | 75 ++ libfriidump/win32compat.h | 68 ++ libmultihash/CMakeLists.txt | 77 ++ libmultihash/crc32.c | 120 +++ libmultihash/crc32.h | 53 ++ libmultihash/edonkey.c | 83 ++ libmultihash/edonkey.h | 36 + libmultihash/md4.c | 441 +++++++++ libmultihash/md4.h | 121 +++ libmultihash/md5.c | 477 ++++++++++ libmultihash/md5.h | 59 ++ libmultihash/multihash.c | 130 +++ libmultihash/multihash.h | 127 +++ libmultihash/sha1.c | 231 +++++ libmultihash/sha1.h | 39 + src/CMakeLists.txt | 50 ++ src/Makefile.am | 11 + src/Makefile.in | 455 ++++++++++ src/friidump.c | 760 ++++++++++++++++ src/getopt-win32.c | 130 +++ src/getopt-win32.h | 36 + src/getopt1.h | 130 +++ src/getopt_long-win32.c | 727 +++++++++++++++ templates/c | 20 + templates/h | 20 + 63 files changed, 11067 insertions(+), 26 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 Doxyfile delete mode 100644 README.md create mode 100644 config.h.cmake create mode 100644 docs/AUTHORS rename LICENSE => docs/COPYING (95%) create mode 100644 docs/ChangeLog create mode 100644 docs/INSTALL create mode 100644 docs/NEWS create mode 100644 docs/README create mode 100644 docs/README.technical create mode 100644 docs/TODO create mode 100644 docs/options create mode 100644 libfriidump.pc.in create mode 100644 libfriidump/CMakeLists.txt create mode 100644 libfriidump/brickblocker.c create mode 100644 libfriidump/brickblocker.h create mode 100644 libfriidump/byteorder.h create mode 100644 libfriidump/constants.h create mode 100644 libfriidump/disc.c create mode 100644 libfriidump/disc.h create mode 100644 libfriidump/dumper.c create mode 100644 libfriidump/dumper.h create mode 100644 libfriidump/dvd_drive.c create mode 100644 libfriidump/dvd_drive.h create mode 100644 libfriidump/ecma-267.c create mode 100644 libfriidump/ecma-267.h create mode 100644 libfriidump/hitachi.c create mode 100644 libfriidump/lite-on.c create mode 100644 libfriidump/misc.c create mode 100644 libfriidump/misc.h create mode 100644 libfriidump/renesas.c create mode 100644 libfriidump/rs.c create mode 100644 libfriidump/rs.h create mode 100644 libfriidump/unscrambler.c create mode 100644 libfriidump/unscrambler.h create mode 100644 libfriidump/vanilla_2064.c create mode 100644 libfriidump/vanilla_2384.c create mode 100644 libfriidump/win32compat.c create mode 100644 libfriidump/win32compat.h create mode 100644 libmultihash/CMakeLists.txt create mode 100644 libmultihash/crc32.c create mode 100644 libmultihash/crc32.h create mode 100644 libmultihash/edonkey.c create mode 100644 libmultihash/edonkey.h create mode 100644 libmultihash/md4.c create mode 100644 libmultihash/md4.h create mode 100644 libmultihash/md5.c create mode 100644 libmultihash/md5.h create mode 100644 libmultihash/multihash.c create mode 100644 libmultihash/multihash.h create mode 100644 libmultihash/sha1.c create mode 100644 libmultihash/sha1.h create mode 100644 src/CMakeLists.txt create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/friidump.c create mode 100644 src/getopt-win32.c create mode 100644 src/getopt-win32.h create mode 100644 src/getopt1.h create mode 100644 src/getopt_long-win32.c create mode 100644 templates/c create mode 100644 templates/h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..25a699b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,161 @@ +# The name of our project is "HELLO". CMakeLists files in this project can +# refer to the root source directory of the project as ${HELLO_SOURCE_DIR} and +# to the root binary directory of the project as ${HELLO_BINARY_DIR}. +project (FriiDump) + +if (MSVC) + # msvc2005 deprecated warnings + add_definitions (-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) +else (MSVC) + if (NOT WIN32) + add_definitions (-fPIC) + endif (NOT WIN32) +endif (MSVC) + + +include (TestBigEndian) + +test_big_endian (CMAKE_WORDS_BIGENDIAN) + + +include (CheckIncludeFiles) + +check_include_files (stdbool.h HAVE_STDBOOL_H) + + +include (CheckFunctionExists) + +check_function_exists (fseeko HAVE_FSEEKO) +check_function_exists (ftello HAVE_FTELLO) +check_function_exists (fseek64 HAVE_FSEEK64) +check_function_exists (ftell64 HAVE_FTELL64) + + +include(CheckTypeSize) + +set (CMAKE_REQUIRED_DEFINITIONS -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64) + +set (CMAKE_EXTRA_INCLUDE_FILES sys/types.h) +check_type_size ("off_t" OFF_T) +set (CMAKE_EXTRA_INCLUDE_FILES) + +set (CMAKE_EXTRA_INCLUDE_FILES stdio.h) +check_type_size ("fpos_t" FPOS_T) +set (CMAKE_EXTRA_INCLUDE_FILES) + +set (CMAKE_REQUIRED_DEFINITIONS) + + +option ( + DEBUG + "Enable debugging messages" + OFF +) + + +option ( + BUILD_STATIC_BINARY + "Build a static binary (has precedence over ALL_LIBS_SHARED)" + OFF +) + +option ( + BUILD_ALL_LIBS_SHARED + "Build all libraries as shared" + OFF +) + +if (BUILD_STATIC_BINARY) + set (libmultihash_type STATIC) + set (libfriidump_type STATIC) +elseif (BUILD_ALL_LIBS_SHARED) + set (libmultihash_type SHARED) + set (libfriidump_type SHARED) +else (BUILD_STATIC_BINARY) + # This is how we build libraries by default + set (libmultihash_type STATIC) + set (libfriidump_type SHARED) +endif (BUILD_STATIC_BINARY) + + +# set (CMAKE_BUILD_TYPE superoptimized) +set (CMAKE_C_FLAGS_SUPEROPTIMIZED "-march=athlon-xp -m3dnow -O3 -funroll-all-loops") + +set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall") + +# set (CMAKE_BUILD_TYPE release) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) +add_definitions(-DHAVE_CONFIG_H) +include_directories ( + ${FriiDump_BINARY_DIR} +) + +# Recurse into the "Hello" and "Demo" subdirectories. This does not actually +# cause another cmake executable to run. The same process will walk through +# the project's entire directory structure. +add_subdirectory (libmultihash) +add_subdirectory (libfriidump) +add_subdirectory (src) + + +if (WIN32) + install (FILES AUTHORS DESTINATION / RENAME Authors.txt) + #install (CODE "exec_program (${CMAKE_CURRENT_SOURCE_DIR}/utils/unix2dos.exe ${CMAKE_OUTPUT_BINARY_DIR} ARGS Authors.txt)") + install (FILES ChangeLog DESTINATION / RENAME ChangeLog.txt) + install (FILES COPYING DESTINATION / RENAME Copying.txt) + install (FILES README DESTINATION / RENAME ReadMe.txt) + install (FILES TODO DESTINATION / RENAME ToDo.txt) +endif (WIN32) + + +# CPack stuff +include (InstallRequiredSystemLibraries) + +set (CPACK_PACKAGE_NAME "friidump") +set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Dump Nintendo GameCube/Wii discs") +set (CPACK_PACKAGE_VENDOR "Arep") +set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/docs/README") +set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/docs/COPYING") +set (CPACK_PACKAGE_VERSION_MAJOR "0") +set (CPACK_PACKAGE_VERSION_MINOR "3") +set (CPACK_PACKAGE_VERSION_PATCH "0") +set (CPACK_PACKAGE_INSTALL_DIRECTORY "FriiDump ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") +set (CPACK_PACKAGE_EXECUTABLES "friidump" "FriiDump") + +set (CPACK_SOURCE_GENERATOR "TBZ2;ZIP") +set (CPACK_SOURCE_IGNORE_FILES + "/CVS/" + "/\\\\.svn/" + "~$" + "tags" + "\\\\.kdevses$" + "\\\\.kdevelop\\\\.pcs$" + "/BUILD.*/" + "/RELEASES/" + "/utils/" + "/doc.*/" +) +set (CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") + +if(WIN32 AND NOT UNIX) + set (CPACK_GENERATOR "NSIS;ZIP") + # There is a bug in NSI that does not handle full unix paths properly. Make + # sure there is at least one set of four (4) backlasshes. +# set (CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp") +# set (CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\MyExecutable.exe") + set (CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} installer") + set (CPACK_NSIS_HELP_LINK "http:\\\\\\\\wii.console-tribe.com") + set (CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.my-personal-home-page.com") + set (CPACK_NSIS_CONTACT "arep@no.net") + set (CPACK_NSIS_MODIFY_PATH ON) + + set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-Win32") +else(WIN32 AND NOT UNIX) + set (CPACK_GENERATOR "TBZ2") + set (CPACK_STRIP_FILES "bin/friidump;lib/libfriidump.so.1.0.0") +# set (CPACK_SOURCE_STRIP_FILES "") + set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-LinuxBin") +endif(WIN32 AND NOT UNIX) + +include (CPack) diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..9f2f12e --- /dev/null +++ b/Doxyfile @@ -0,0 +1,284 @@ +# Doxyfile 1.5.1-KDevelop + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = FriiDump +PROJECT_NUMBER = 0.3pre +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = YES +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +BUILTIN_STL_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = NO +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = NO +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = libfriidump \ + libmultihash +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY \ + *.C \ + *.H \ + *.tlh \ + *.diff \ + *.patch \ + *.moc \ + *.xpm \ + *.dox +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = doxygen-html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = YES +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = YES +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = YES diff --git a/README.md b/README.md deleted file mode 100644 index aaac001..0000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -friidump -======== - -FriiDump - Dump Nintendo Wii and GameCube discs diff --git a/config.h.cmake b/config.h.cmake new file mode 100644 index 0000000..67aedcf --- /dev/null +++ b/config.h.cmake @@ -0,0 +1,42 @@ +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#cmakedefine WORDS_BIGENDIAN ${CMAKE_WORDS_BIGENDIAN} + +/* Define to 1 if stdbool.h conforms to C99. */ +#cmakedefine HAVE_STDBOOL_H 1 + +/* Debug support */ +#cmakedefine DEBUG 1 + +#cmakedefine HAVE_FSEEKO +#cmakedefine HAVE_FTELLO +#cmakedefine HAVE_FSEEK64 +#cmakedefine HAVE_FTELL64 + +#cmakedefine HAVE_OFF_T +#ifdef HAVE_OFF_T +#cmakedefine OFF_T ${OFF_T} +#define SIZEOF_OFF_T OFF_T +#endif + +#cmakedefine HAVE_FPOS_T +#ifdef HAVE_FPOS_T +#cmakedefine FPOS_T ${FPOS_T} +#define SIZEOF_FPOS_T FPOS_T +#endif + + +/* Large file support - Can't this be done better with CMake!? What about on Windows? */ +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE 1 +#define _LARGE_FILES +#define HAVE_LARGEFILE_SUPPORT + +#if defined(_FILE_OFFSET_BITS) +# if (_FILE_OFFSET_BITS<64) +# undef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +#else +# define _FILE_OFFSET_BITS 64 +#endif \ No newline at end of file diff --git a/docs/AUTHORS b/docs/AUTHORS new file mode 100644 index 0000000..d57416c --- /dev/null +++ b/docs/AUTHORS @@ -0,0 +1,41 @@ +FriiDump came to existance thanks to the work by a lot of people, most of which +are probably not aware of this fact ;). So here is proper credit: + +The DVD seed bruteforcing algorithm and code were taken from unscrambler 0.4 +by Victor Muņoz (xt5@ingenieria-inversa.cl, +http://www.ingenieria-inversa.cl/?lp_lang_pref=en). + +The theoritical basis of the dumping methods were suggested in several comments +to Victor's post. The most important comments came from Victor himself (xt5), +FuzzyLogic and svpe. + +The code to actually perform the dumping was derived from the work of Kevin +East, AKA SeventhSon (kev@kev.nu, http://www.kev.nu/360/), which, in turn, +derives from work by a lot of other people. See his page for full details. + +Many hints were taken from RawDump, whose author is unknown. + +A program that helped me a lot to understand the drive cache behaviour is +PLScsi by Pat LaVarre (http://members.aol.com/plscsi/). + +Nintendo disc structure information was taken from: +- http://www.gc-linux.org/docs/yagcd.html +- http://www.wiili.org/index.php/GameCube_Optical_Disc + +Code to tell whether a Wii disc contains an update or was inspired by a program +by wiidevel@stacktic.org. Sorry but I cannot find the URL anymore :(. + +Other minor pieces of code were taken from tcpdump (www.tcpdump.org), Python +(www.python.org) and the glibc printf manpage. + +GDR8163B and Windows testing was performed by tasso85. + +Thanks also go out to the ConsoleTribe staff for giving me the possibility to +use their forum for the program support. + +Glue, endless hours spent understanding drive cache behaviour and rest of the +code are by me, Arep . + +Finally, obvious thanks go out to Nintendo for making all of their great +consoles, who constantly help me to waste the rest of my spare time I do not +spend coding ;). diff --git a/LICENSE b/docs/COPYING similarity index 95% rename from LICENSE rename to docs/COPYING index d7f1051..5b6e7c6 100644 --- a/LICENSE +++ b/docs/COPYING @@ -1,12 +1,12 @@ -GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to +the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - - GNU GENERAL PUBLIC LICENSE + + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -225,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN @@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it @@ -290,8 +290,8 @@ to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - {description} - Copyright (C) {year} {fullname} + + Copyright (C) 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 @@ -303,9 +303,10 @@ the "copyright" line and a pointer to where the full notice is found. 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. + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Also add information on how to contact you by electronic and paper mail. @@ -329,11 +330,11 @@ necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. - {signature of Ty Coon}, 1 April 1989 + , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General +library. If this is what you want to do, use the GNU Library General Public License instead of this License. diff --git a/docs/ChangeLog b/docs/ChangeLog new file mode 100644 index 0000000..4d1fa6f --- /dev/null +++ b/docs/ChangeLog @@ -0,0 +1,45 @@ +0.5.3 (15/03/2010) +- Fixed failing after 1st DL media layer with non-Hitachi methods. +- Fixed still hashing with 'nohash' parameter when resuming. +- Fixed resuming larger files (~4 GB). +- Fixed unscrambling larger files. +- Faster file unscrambling. +- Slight modifications to methods; + possible performance increase with Hitachi based devices. +- Restructured methods and added some new ones. +- Added layer break information. +- Added current position output when error occurs. +- Added SH-D162A, SH-D162B, SH-D162C & SH-D162D as supported. +> Jackal, gorelord4e, themabus +0.5.2 (10/01/2010) +- Corrected handling of standard DVDs + (type should be forced to 3, when dumping or unscrambling). +- Better response to 'speed' parameter. +- Uniform raw output for all devices: unscrambled data + headers. +- Slight performance increase (~1650 MB/h on LH-18A1H). +- Added LH-18A1P, LH-20A1H, LH-20A1P to list of supported devices. +> Jackal, themabus +0.5.1 (01/12/2009) +- New command 'vanilla 2384'. +- Restructured methods, some now support optional parameters. +- Ability to select standard DVDs as source. +- Limited recognized Lite-On drives to LH-18A1H. +> Jackal, themabus +0.5.0 (27/11/2009) +- Regions: Italy, France, Germany, Spain, Australia, PAL-X, PAL-Y. +- Updated publisher list from http://wiitdb.com/Company/HomePage +- Included GDR8082N & GDR8161B as supported Hitachi drives. +- Lite-On, Renesas & vanilla memory buffer access commands. + Lite-On tested on LH-18A1H, should work on many more + (LH*, SH, DH*, DW* & possibly other MediaTek drives) +- Shifted methods 1..4 to 0..3 and added new ones 4..6 + Associated known drives with default methods. +- Additional commandline parameters: + stop, speed, command, type, size +- Some minor changes and fixes. +> Jackal, Truman, themabus +0.4 (08/03/2008) +- Support for DL Wii DVDs. +> mado3689 +0.3 (06/10/2007) +- First public release. diff --git a/docs/INSTALL b/docs/INSTALL new file mode 100644 index 0000000..a195537 --- /dev/null +++ b/docs/INSTALL @@ -0,0 +1 @@ +See README. diff --git a/docs/NEWS b/docs/NEWS new file mode 100644 index 0000000..c5c3208 --- /dev/null +++ b/docs/NEWS @@ -0,0 +1,53 @@ +Since last official version original method 1 have been renamed to method 0 +and it undergone certain changes. Methods 2, 3, 4 have been renamed to 7, 8 +and 9 respectively. Method 0 should work with all drives as long as they +are supported by one of memory dump commands, so if drive is unrecognized it +is preferable to keep method at 0, and try all commands. If one of such +combinations turns out to work, you can proceed then testing other methods +with this commad. In case none of commands work, you could try to determine +drive's Read Buffer command's parameters with supplied 'BruteForce3C.exe'. + +Generally program's overall bahaviour regarding commandline haven't changed +and you should be able to use same options as with official versions, though +in case you were using unrecognized drive, which would nevertheless work with +Hitachi command, you'll need to set command to 2 now (e.g. --command 2) and +method to 7, 8 or 9. + +Performance have increased since official release and should be now about the +same as with 'RawDump'. + +Regarding supported drives: + +1. Hitachi-LG GDR8161B, GDR8162B, GDR8163B, GDR8164B, GDR8082N +Those drives can read GC/Wii media without swapping. Expected performance is +1600..1900 MB/h for *4B, *3B and 2100..2600 MB/h for *2B, *1B. Custom memory +dump command is used, which returns 2064 bytes of data. It was reproted that +they can not read other (e.g. PC) discs this way though, this needs +confirmation. + +2. Lite-On LH-18A1H, DVDRW LH-18A1P, DVDRW LH-20A1H, DVDRW LH-20A1P +Reading performance for PC DVDs can go up to 5000 MB/h, which means program's +core as well as new methods are capable to output data at least at this rate. +Reading performance for GC was about 1600..1700 MB/h so likely this slowdown is +caused by drive logic itself. Though I only had one GC game to test with, so +possibly better results can be achieved depending on media. Best results were +obtained, when using method 5 with parameter 16,27 (--method5=16,27). This +combination isn't set as default because it can cause noticable delays +depending on medium quality and to make methods more general for use with other +devices. Lite-On won't read GC/Wii DVDs at all without swapping. Lite-On +returns 2384 bytes of data (2064 + ECC) by means of vendor specific READ BUFFER +command. Tested with models LH-18A1H, LH-18A1P and LH-20A1H. + +3. Plextor +Plextor would return 2064 bytes of already unscrambled data with READ BUFFER +command. It works good with ordinary DVDs but due the lack of streamed reading +support is practically useless for GC/Wii dumping because of very low +performance. Works nevertheless and could be used for some experiments and +testing. Results from PX-760A. + +4. Toshiba Samsung SH-D162A, SH-D162B, SH-D162C, SH-D162D +Returns 2384 data bytes per sector like Lite-On does. Appears to support +streamed reading but performance with tested model (SH-D162D) was somewhat low +and unstable even with ordinary DVDs. Looks promising, if only good-working +method could be determined. Latest drives added, definitely need more testing +at this point. diff --git a/docs/README b/docs/README new file mode 100644 index 0000000..5e1eb77 --- /dev/null +++ b/docs/README @@ -0,0 +1,187 @@ +FriiDump - A program to dump Nintendo Wii and GameCube disc +=============================================================================== + +FriiDump is a program that lets you dump Nintendo Wii and GameCube disc from +your computer, without using original Nintendo hardware. It basically performs +the same functions as the famous "RawDump" program, but with a big difference, +which should be clear straight from its name: FriiDump is free software, where +"free" is to be intended both as in "free speech" and in "free beer". As such, +FriiDump is distributed with its sources. + +This leads to a number of good consequences: +- Having the sources available, it can be easily ported to different operating + systems and hardware platforms. At the moment it is developed under a + GNU/Linux system, but it also runs natively on Windows. A MacOS X version can + be easily created, although I don't have a Mac, so I can't do it myself. +- Also, having the sources and these being well-organized (I know I'm a modest + guy) allows support for new DVD-ROM drives to be added relatively easily. At + the moment the same drives as RawDump are supported, but this might improve + in the future, if anyone takes the effort... See README.technical for + details. +- The sources might also be used as a reference for several things regarding + Nintendo Wii/GameCube discs and the hacks used to read them on an ordinary + drive. + +Furthermore, FriiDump also features some functional improvements over RawDump: +- FriiDump can use 4 different methods to read the disc, with different + performance. +- FriiDump dumps a lot of useful information about the discs it dumps, + such as whether the disc contains an update or not, which can help avoid + bricking your Wii ;). +- FriiDump calculates the CRC32, MD4, MD5, SHA-1 and ED2K hashes of dumped + discs, so you can immediately know if your dump is good or not, by comparing + the hashes with the well-known ones available on several Internet sites. +- FriiDump comes in the form of a library and a command-line front-end, which + allows its functions to be easily reused in other programs. + +Unfortunately, there is also a main downfall: +- Even the fastest dump method used by FriiDump is not as fast as RawDump (but + not that much slower, either, see the table below). + +Anyway, I'm sure that people who cannot use RawDump (i.e.: GNU/Linux, *BSD and +MacOS X users) will be happy anyway. Besides, you get the sources, so you can +improve them yourself. + +Note that FriiDump is only useful to dump *original* Nintendo discs. To dump +backup copies you can use any DVD-dumping program (i.e.: dd under UNIX ;)). + +FriiDump came to existance thanks to the work by a lot of people, most of which +are probably not aware of this fact ;). Please see the AUTHORS file for the +credits. + + +=============================================================================== +Supported drives +=============================================================================== +At the moment the same drives as RawDump are supported. This is due to various +reasons, explained in the README.technical file, which also contains +information about what is needed to add support for more drives. + +Currently supported drives are: +- LG GDR-8161B (untested, but should work) +- LG GDR-8162B (ditto) +- LG GDR-8163B (HL-DT-ST/DVD-ROM GDR8163B) +- LG GDR-8164B (HL-DT-ST/DVD-ROM GDR8164B) + +Other drives might work, most likely those based on the Hitachi MN103 +microcontroller. If you find any of them, please report so that they can be +added to the compatibility list. + + +=============================================================================== +Installation +=============================================================================== +If you are a Windows user, probably you will have downloaded the binaries, +either zipped or together with an installer, so the installation should be +straightforward. + +If you downloaded the sources, you will need to compile them. FriiDump uses +CMake, for easy portability, so you will need to get it from cmake.org. On +Windows you will also need a compiler like Visual Studio (the only tested one, +so far) or CygWin/MinGW. On UNIX just do the following, from the directory +where you unpacked the sources into: + +$ mkdir BUILD +$ cd BUILD +$ cmake .. +$ make +$ make install + +Linux-specific note: You need root privileges to issue certain commands to the +DVD-ROM drive. Hence you have the following possibilities: +- Run FriiDump as root: discouraged. +- Run it through sudo: better but nevertheless discouraged. +- Set the setuid bit on the executable: this is the recommended way to run + FriiDump under Linux. This way, the code run with superuser privileges will + be reduced to a minimum, guaranteeing a certain level of security (note that + security-related bugs might exist anyway!!!). Also note that, even when the + setuid bit is set, the attempt to open the drive for reading will be done + after privileges have been dropped, so you will need explicit read access to + the DVD-ROM drive. Usually having the system administrator add you to the + "cdrom" group is enough. To set the setuid bit on the executable, run as + root: + + $ chown root:root /usr/local/bin/friidump + $ chmod u+s /usr/local/bin/friidump + + +=============================================================================== +Usage +=============================================================================== +FriiDump is a command-line program, so you will need to run it from a terminal +or a command-prompt under Windows. The basic usage is as follows: + +friidump -d -a + +where will usually be something like "/dev/hda" on Unix-like systems, +and something like "e:" for Windows users. With this command, the disc will be +dumped to an ISO image file with an automatically-chosen name. Drop -a and use +the -i option if you prefer to specify the filename yourself. If you want to +resume an existing dump, use -s. If you want to dump the disc to a raw format +image file, use -r. Note that you can create a raw and an ISO image at the same +time. + +Other options you might want to use are -1 through -4, to set the dump method, +although the default is method 4, which is the fastest one, so most likely you +will not need them. + +Finally, use -h for a listing of all available options. + + +=============================================================================== +Performance +=============================================================================== +As stated above, FriiDump is not as fast as RawDump. On my PC (Athlon64 3200+), +performance is as follows: + +------------------------------------------------------------------------------- +| Method | Dump speed | GameCube disc dump time | Wii disc dump time | +------------------------------------------------------------------------------- +| 1 | Too slow ;) | Eternity | More than eternity | +| 2 | ~570 MB/h | 2.5 hours | 8 hours | +| 3 | ~740 MB/h | 2 hours | 6 hours | +| 4 | ~1250 MB/h | 1.2 hours | 3.5 hours | +------------------------------------------------------------------------------- + + +=============================================================================== +Support +=============================================================================== +I'm releasing this program under the nickname of "Arep". This is because I am +not sure about the legal status of the program, and I do not want to encounter +any consequences. Actually, I'm pretty sure FriiDump goes against the DMCA, +being a program that circumvents copy-protection, but it might be objected that +the format used by Nintendo discs is not a copy-protection method, but just +their own, undocumented, disc format. Although, I think it can be freely used +in Europe and other coutries without laws similar to the DMCA. + +For the same reason, I am not putting an e-mail address here (that @no.net you +find in the program is obviously a pun), but support will be provided through +the forums of the Italian ConsoleTribe forum, at http://wii.console-tribe.com. +If you need help, just open a thread in any section there, even in English: I +will *not* reply, but you might stand assured I will read everything you write. +FriiDump users are encouraged to help each other there ;). + +Patches are welcome, too: just attach them to your post, and maybe put +something like "[PATCH]" in the topic subject, so that I can easily spot them. + +New releases will be announced on that forum, and also on QJ.net, if I find a +good way to notify them. + +If you want to donate to the project, do not do it, and donate to one of the +free Wii modchip projects out there, such as OpenWii, WiiFree or YAOSM. + + +=============================================================================== +Disclaimer +=============================================================================== +FriiDump is distributed under the GNU General Public License version 2. See the +COPYING file for details. + +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. + +Also, please note that this program is not meant to be used to spread game +piracy, but rather to be instrument to make backups of your own precious +legally-bought games. diff --git a/docs/README.technical b/docs/README.technical new file mode 100644 index 0000000..b599925 --- /dev/null +++ b/docs/README.technical @@ -0,0 +1,234 @@ +FriiDump Techincal info +=============================================================================== + +This document is a reworking and unification of information found all over the +net, regarding the structure of Nintendo Gamecube/Wii Optical Discs and how to +read them on an ordinary DVD-ROM drive. All the due credits can be found in the +AUTHORS file. + + +=============================================================================== +Nintendo Gamecube/Wii Optical Disc (GOD/WOD) structure +=============================================================================== + +In order to understand how a Gamecube or WII Optical Disk is made, let us +first take a look at a standard DVD-ROM. The complete standard is explained in +the ECMA-267 Standard. + +The user data stored on the DVD is divided in blocks, each 2048 bytes long. +Each 2048-byte block is then encapsulated in a 2064-byte structure, adding some +other data needed for error-correction and head positioning. A 2064-byte block +is called a "Data frame", and its logical layout is as follows: + + 4bytes 2bytes 6bytes 2048bytes 4bytes + - - - - - - - - - - - - - - - - - - - - - - - - - - +| ID | IED | CPR_MAI | User Data Frame | EDC | + - - - - - - - - - - - - - - - - - - - - - - - - - - + +- Identification Data (ID): Contains the PSN (Physical Sector Number), info + about the sector itself, like the layer, reflectivity, zone, etc. +- ID Error Detection Code (IED) +- Copyright Management Information (CPR_MAI): Its use is application-specific, + for instance it can be used to store a sector key in videos that use CSS, or + a scrambling key in the XBox and XBox360 Security Sectors. +- User Data: This is the data available for the end user. +- Error Detection Code (EDC): It is the checksum data for all the fields above, + its polinomial is x^32 + x^31 + x^4 + 1. + +For various reasons (not related to copy protection), the User Data Frame is +XOR'ed with a stream cipher generated by an 15bits LFSR (Linear Feedback Shift +Register), with bits 10 and 14 used as taps. The seeds are obtained from a +table of the ECMA-267 standard, the index of the seed is the 4 MSB of the last +byte of the "ID" field of the Data Frame. The same stream cipher is then used +by 16 consecutive Data Frames: for this and other reasons (again related to +error correction), data from the DVD are always read in 16-data frame blocks. + + 4bytes 2bytes 6bytes 2048bytes 4bytes + - - - - - - - - - - - - - - - - - - - - - - - - - - +| ID | IED | CPR_MAI | User Data Frame | EDC | + - - - - - - - - - - - - - - - - - - - - - - - - - - + ^ | 2048bytes cipher stream | + ^ - - - - - - - - - - + Scrambling + seed index + +Now, the first problem when dealing with Gamecube/Wii Optical Discs is that +they use a different (and yet unknown) set of seeds. This means that when an +ordinary DVD-ROM drive tries to read a GOD/WOD disc, it will unscramble the +User Data Frame with the wrong seed, causing the EDC check to fail and a read +error to be reported to the operating system, which means the inability to read +the disc. + +Furthermore, Gamecube/Wii Optical Disks use a slightly different structure for +the Data Frame, as shown in the following figure: + + 4bytes 2bytes 2048bytes 6bytes 4bytes + - - - - - - - - - - - - - - - - - - - - - - - - - - +| ID | IED | User Data Frame | CPR_MAI | EDC | + - - - - - - - - - - - - - - - - - - - - - - - - - - + | 2048bytes cipher stream | + - - - - - - - - - - + +Basically, the User Data Frame and the CPR_MAI fields are swapped, while the +scrambled bytes remain the same. + + +=============================================================================== +Tricks used to read a GOD/WOD on a standard DVD-ROM drive. +=============================================================================== +To cope with the above-mentioned problems, some methods have been suggested by +many people on the net. They vary in performance, but the basic idea is always +the same: + +1. Issue a read command for the sector of interest (this is actually a + 16-sector block, not a single sector, as stated above). +2. Let the read return failure. +3. At this moment, the DVD-ROM drive must have cached the data read from the + disc somewhere in its internal memory, to be able to unscramble them and to + check the EDC. So, we can dump the data from the drive's internal RAM. + +Unfortunately, this is not as easy as it seems, due to the fact that there is +no standard "Dump drive memory" command. This is probably implemented in many +drives for firmware debugging purposes, but, as such, it is a vendor-specific +command, which are usually undocumented and vary from vendor to vendor (or even +from drive model to drive model). + +This is where the work that many hackers around the world have done, in order +to crack the Microsoft XBox360 console, becomes useful. They needed some way to +read and write data to the XBox360 drive's internal memory, so they +disassembled the drive's firmware and patiently tried to understand what each +of the vendor-specific commands did. This way, they discovered that a certain +command could be used to read an arbitrary portion of the drive's internal RAM. +They also managed to map the RAM addresses, so that it is known where data +read from the disc are temporarily stored. + +Fortunately, the XBox360 drive is not too different from some retail DVD-ROM +drives, namely some models from LG, which means that the discovered command +works on them as well. Hence, we now have some sort of access to the disc data. + +Although, this comes at the price of speed: dumping the drive's internal memory +is a slow process, as it uses Programmed I/O (PIO) instead of Direct Memory +Access (DMA) to transfer data from the drive to the computer's memory. For this +reason, some have proposed the use of the "streaming read" command: it is a +standard command thought for those applications where the constant flow of data +is more important than its absolute correctness, such as audio or video +applications. Thus, this command does not perform the EDC on the data read from +the disc, but returns it immediately. Anyway, the command will only return the +User Data Frame, which means that only a portion of the data will be read this +way, while the rest (i.e.: the first 12 and the last 4 bytes) will have to be +read using the memory dump method. Nevertheless, this combined method will be +faster overall, as only some dozens of bytes have to be transfered through PIO, +instead of the whole sectors. + +We still have to cope with the unknown seeds. This problem can be easily solved +through the use of brute force: as there are only 15 bits to try (and commonly +only 17 seeds per GOD/WOD), this approach only takes about 30 seconds. The +bruteforce process is very simple: the LFSR is seeded from 0 to 7FFFh and for +each seed the corresponding stream cipher is generated and XOR'ed with the +proper section of the Data Frame and the EDC is computed. If the EDC is the same +as the one in the EDC field then we have the correct seed. + + +=============================================================================== +The FriiDump approach +=============================================================================== +FriiDump can use four different disc dump methods. They have been developed +gradually, empirically and heuristically, by experimenting with the PLScsi tool +and comparing the retrieved data with a known-good dump. In this section the +inner working of every dump method will be described. The code implementing the +different methods can be found in the "disc_read_sector_X" functions of disc.c. + +Please note that the desibed behaviour is that of my LG GDR8164B drive, which I +assume to be shared by similar drives. Other, more different drives, might +behave differently and require totally different methods. + +Also note that all of the methods read 16-sector blocks. + + +Method 1 + +This method is very slow. So slow that I have never dumped a whole disc with +it. Although, it served me to prove that I was going in the right direction and +that my efforts could eventually come to a working end. It also showed me that +the first versions of RawDump create bad dumps, sometimes. + +Basically this method is the same used by those early versions of RawDump, +which took 50+ hours to dump a whole disc. It works like this: + +1. Issue a read command for the required sector. This will cause the 16-sector + block to which the sector belongs to be placed at the beginning of the + drive's cache memory. Do not even bother to see what the read command + returns, as it will surely be a data read error. +2. Dump the 16-sector block. + + +Method 2 + +Method 2 is similar to method 1, but uses the above-mentioned "streaming read" +method, which somehow allows us to dump 5 blocks at a time. + +1. Issue a "streaming read" command for the required sector. This will cause + the 16-sector block to which the sector belongs to be placed at the + beginning of the drive's cache memory, together with the 4 following + 16-sector blocks. Do not bother to see what the read command returns. +2. Dump the 5 16-sector blocks. + + +Method 3 + +This is similar to the previous method, but instead of dumping the whole +sectors from memory, it uses the (not EDC checked) data returned by the +"streaming read" command. and completes it dumping only the missing bits from +memory. + +1. Issue a "streaming read" command for the required sector. This will cause + the 16-sector block to which the sector belongs to be placed at the + beginning of the drive's cache memory, together with the 4 following + 16-sector blocks. +2. For each sector of each block, reconstruct the whole Data Frame, as follows: + - Dump 12 bytes from memory. + - Use 2048 bytes returned by the read command. + - Dump 4 more bytes from memory. + +Note that this method has a small issue, as sometimes the beginning of the +cache will be dirty and contain invalid data, needing the sector to be read +again. As this seems to happen quite often, we always read a dummy sector +before the requested sector. + + +Method 4 + +Method 4 is just method 3 with a trick to use a single dump command for every +sector that has to be reconstructed, instead of two. It is the faster dump +method currently supported and, as such, the default one. + +1. Issue a "streaming read" command for the required sector. This will cause + the 16-sector block to which the sector belongs to be placed at the + beginning of the drive's cache memory, together with the 4 following + 16-sector blocks. +2. For each sector of each block, reconstruct the whole Data Frame, as follows: + - If this is the first sector of a block, dump 12 bytes from memory. + Otherwise, use the last 12 bytes of the preceding dump. + - Use 2048 bytes returned by the read command. + - Dump 16 bytes from memory, and use the first 4. This leaves 12 bytes to be + used for the reconstruction of the next sector. + +Note that the issue of method 3 applies to this method, too. + + +=============================================================================== +How to add support for a new drive +=============================================================================== +If you read all the above stuff, it should be clear that, in order to add +support for a new DVD-ROM drive, all that is needed is a way to dump the +drive's internal memory, in particular the portion where the data read from the +disc is cached. As explained above, this function might not be present in all +drives, and might not be easy to find or to use. In case you manage to discover +it, just copy the file "hitachi.c" to a new one, and modify it opportunely. + +Some modifications will also be needed in "dvd_drive.c", in order to add +autodetection for the new drive, in the "dvd_assign_functions" function. + +Apart from this, the cache behaviour of the new drive might not be the same +as that of the currently supported models, so the program architecture might +need radical changes. In this case, please report. diff --git a/docs/TODO b/docs/TODO new file mode 100644 index 0000000..2c83d17 --- /dev/null +++ b/docs/TODO @@ -0,0 +1,13 @@ +- Hashes are wrongly calculated on big-endian machines. This just needs fixing + of some #define's in the multihash library. +- Add MacOS X-specific code. All that is needed is a way to send MMC commands + to the DVD-ROM drive. Inspiration can be taken from the libcdio sources. If + only I had a Mac... Same goes for *BSD, anyway. +- Maybe write a graphical client. I will not do this as I am no GUI fanatic, + but I am sure many Windows users will still use RawDump as it has a GUI, + while we do not. This is actually quite easy, as the program is split into a + library (libfriidump) and a frontend. If anybody does it, please try to make + a portable GUI, maybe by using GTK+, qt, wxWidgets or whatever similar + toolkit you might find. +- Make FriiDump as fast as RawDump (i.e.: 2.5 hours for a Wii disc). +- Improve and better document the library API. diff --git a/docs/options b/docs/options new file mode 100644 index 0000000..4055e6d --- /dev/null +++ b/docs/options @@ -0,0 +1,64 @@ +FriiDump 0.5.3 - Copyright (C) 2007 Arep +This software comes with ABSOLUTELY NO WARRANTY. +This is free software, and you are welcome to redistribute it +under certain conditions; see COPYING for details. + +Official support forum: http://wii.console-tribe.com + +Forum for this UNOFFICIAL VERSION: http://forum.redump.org + + +Available command line options: + + -h, --help Show this help + -a, --autodump Dump the disc to an ISO file with an + automatically-generated name, resuming the dump + if possible + -g, --gui Use more verbose output that can be easily + parsed by a GUI frontend + -d, --device Dump disc from device + -p, --stop Instruct device to stop disc rotation + -c, --command Force memory dump command: + 0 - vanilla 2064 + 1 - vanilla 2384 + 2 - Hitachi + 3 - Lite-On + 4 - Renesas + -x, --speed Set streaming speed (1, 24, 32, 64, etc., + where 1 = 150 KiB/s and so on) + -T, --type Force disc type: + 0 - GameCube + 1 - Wii + 2 - Wii_DL + 3 - DVD + -S, --size Force disc size + -r, --raw Output to file in raw format (2064-byte + sectors) + -i, --iso Output to file in ISO format (2048-byte + sectors) + -u, --unscramble Convert (unscramble) raw image contained in + to ISO format + -H, --nohash Do not compute CRC32/MD5/SHA-1 hashes + for generated files + -s, --resume Resume partial dump + - General ----------------------------------- + -0, --method0[=,] Use dumping method 0 (Optional argument + specifies how many sectors to request from disc + and read from cache at a time. Values should be + separated with a comma. Default 16,16) + - Non-Streaming ----------------------------- + -1, --method1[=,] Use dumping method 1 (Default 16,16) + -2, --method2[=,] Use dumping method 2 (Default 16,16) + -3, --method3[=,] Use dumping method 3 (Default 16,16) + - Streaming --------------------------------- + -4, --method4[=,] Use dumping method 4 (Default 27,27) + -5, --method5[=,] Use dumping method 5 (Default 27,27) + -6, --method6[=,] Use dumping method 6 (Default 27,27) + - Hitachi ----------------------------------- + -7, --method7 Use dumping method 7 (Read and dump 5 blocks + at a time, using streaming read) + -8, --method8 Use dumping method 8 (Read and dump 5 blocks + at a time, using streaming read, using DMA) + -9, --method9 Use dumping method 9 (Read and dump 5 blocks + at a time, using streaming read, using DMA and + some speed tricks) diff --git a/libfriidump.pc.in b/libfriidump.pc.in new file mode 100644 index 0000000..43cd7c1 --- /dev/null +++ b/libfriidump.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: LibFriiDump +Description: Library to dump Nintendo GameCube/Wii discs +Version: @VERSION@ +URL: http://wii.console-tribe.com +Libs: -L${libdir} -lfriidump +Cflags: -I${includedir}/libfriidump diff --git a/libfriidump/CMakeLists.txt b/libfriidump/CMakeLists.txt new file mode 100644 index 0000000..759995f --- /dev/null +++ b/libfriidump/CMakeLists.txt @@ -0,0 +1,105 @@ +# Create a library called "Hello" which includes the source file "hello.cxx". +# The extension is already found. Any number of sources could be listed here. +add_library ( + friidumplib + ${libfriidump_type} + #SHARED + #STATIC + + brickblocker.h + brickblocker.c + byteorder.h + constants.h + disc.h + disc.c + dumper.h + dumper.c + dvd_drive.h + dvd_drive.c + hitachi.c + ecma-267.h + ecma-267.c + lite-on.c + misc.h + misc.c + renesas.c + rs.h + rs.c + unscrambler.h + unscrambler.c + vanilla_2064.c + vanilla_2384.c + win32compat.h + win32compat.c +) + +set_target_properties (friidumplib PROPERTIES OUTPUT_NAME "friidump") + +include_directories ( + ${FriiDump_SOURCE_DIR}/libmultihash +) + +# Make sure the linker can find the Hello library once it is built. +link_directories ( + ${FriiDump_BINARY_DIR}/libmultihash +) + +# Link the executable to the Hello library. +target_link_libraries ( + friidumplib + + multihashlib +) + +# Before making a release, the LTVERSION string should be modified. +# The string is of the form CURRENT:REVISION:AGE. +# +# CURRENT (C) +# The most recent interface number that this library implements. +# +# REVISION (R) +# The implementation number that this library implements. +# +# AGE (A) +# The difference between the newest and oldest interfaces that this +# library implements. In other works, the library implements all the +# interface numbers in the range from number 'CURRENT - AGE' to +# 'CURRENT'. +# +# This means that: +# +# - If interfaces have been changed or added, but binary compatibility has +# been preserved, change to C+1:0:A+1 +# +# - If binary compatibility has been broken (eg removed or changed +# interfaces) change to C+1:0:0 +# +# - If the interface is the same as the previous version, change to C:R+1:A +# +#set_target_properties (friidumplib PROPERTIES SOVERSION 1.0.0) + + +# Windows stuff to correctly build DLL or static library +#get_target_property (libfriidump_type friidumplib TYPE) +if (WIN32) + if (libfriidump_type STREQUAL "SHARED") +# MESSAGE ("Building libfriidump DLL") + ADD_DEFINITIONS (-DFRIIDUMPLIB_BUILD_DLL) + set_target_properties (friidumplib PROPERTIES DEFINE_SYMBOL FRIIDUMPLIB_EXPORTS) + + install ( + TARGETS friidumplib + RUNTIME DESTINATION / + #ARCHIVE DESTINATION lib + ) + endif (libfriidump_type STREQUAL "SHARED") +else (WIN32) + # Install stuff, only if a shared library is being built + if (libfriidump_type STREQUAL "SHARED") + install ( + TARGETS friidumplib + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + ) + endif (libfriidump_type STREQUAL "SHARED") +endif (WIN32) diff --git a/libfriidump/brickblocker.c b/libfriidump/brickblocker.c new file mode 100644 index 0000000..0280e39 --- /dev/null +++ b/libfriidump/brickblocker.c @@ -0,0 +1,22 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* wiidevel@stacktic.org */ diff --git a/libfriidump/brickblocker.h b/libfriidump/brickblocker.h new file mode 100644 index 0000000..884d583 --- /dev/null +++ b/libfriidump/brickblocker.h @@ -0,0 +1,20 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ diff --git a/libfriidump/byteorder.h b/libfriidump/byteorder.h new file mode 100644 index 0000000..b912ab3 --- /dev/null +++ b/libfriidump/byteorder.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "misc.h" +#include + +/*************************** BYTE SWAPPING MACROS ***************************/ +// WORDS_BIGENDIAN is defined by the AC_C_BIGENDIAN autoconf macro, in case. On Windows please (un)define manually. +#ifdef WORDS_BIGENDIAN //!< Swapping macros on big endian systems (Where host b. o. = network b. o.) + +/* This machine's order to big endian */ +#define bo_my2big_16(a) ((u_int16_t)(a)) +#define bo_my2big_32(a) ((u_int32_t)(a)) + +/* This machine's order to little endian */ +#define bo_my2little_16(a) ( \ + ((((u_int16_t) (a)) & 0x00FF) << 8) + \ + (((u_int16_t) (a)) >> 8) \ + ) +#define bo_my2little_32(a) ( \ + ((((u_int32_t) (a)) & 0x000000FF) << 24) + \ + ((((u_int32_t) (a)) & 0x0000FF00) << 8) + \ + ((((u_int32_t) (a)) & 0x00FF0000) >> 8) + \ + (((u_int32_t) (a)) >> 24) \ + ) +#else //!< Swapping macros on little endian systems + /* This machine's order to big endian */ +#define bo_my2big_16(a) ( \ + ((((u_int16_t) (a)) & 0x00FF) << 8) + \ + (((u_int16_t) (a)) >> 8) \ + ) +#define bo_my2big_32(a) ( \ + ((((u_int32_t) (a)) & 0x000000FF) << 24) + \ + ((((u_int32_t) (a)) & 0x0000FF00) << 8) + \ + ((((u_int32_t) (a)) & 0x00FF0000) >> 8) + \ + (((u_int32_t) (a)) >> 24) \ + ) + +/* This machine's order to little endian */ +#define bo_my2little_16(a) ((u_int16_t)(a)) +#define bo_my2little_32(a) ((u_int32_t)(a)) +#endif + +/* These will be handy */ +/* Big endian to this machine's order */ +#define bo_big2my_16(x) bo_my2big_16(x) +#define bo_big2my_32(x) bo_my2big_32(x) + +/* Little endian to this machine's order */ +#define bo_little2my_16(x) bo_my2little_16(x) +#define bo_little2my_32(x) bo_my2little_32(x) + +/* There are the most useful ones */ +#define my_htons(x) bo_my2big_16(x) +#define my_htonl(x) bo_my2big_32(x) +#define my_ntohs(x) my_htons(x) +#define my_ntohl(x) my_htonl(x) + +/************************ END OF BYTE SWAPPING MACROS ***********************/ diff --git a/libfriidump/constants.h b/libfriidump/constants.h new file mode 100644 index 0000000..fcf5152 --- /dev/null +++ b/libfriidump/constants.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/*! \file + * \brief Nintendo GameCube/Wii disc geometry constants. + * + * This file contains constants that describe the general layout of Nintendo GameCube/Wii discs and that can be used throughout the whole program. + */ + +#ifndef CONSTANTS_H_INCLUDED +#define CONSTANTS_H_INCLUDED + +/*! \brief Size of a scrambled sector */ +#define RAW_SECTOR_SIZE 2064 + +/*! \brief Size of an unscrambled sector */ +#define SECTOR_SIZE 2048 + +/*! \brief Number of sectors in a block */ +#define SECTORS_PER_BLOCK 16 + +/*! \brief Size of a scrambled block */ +#define RAW_BLOCK_SIZE (RAW_SECTOR_SIZE * SECTORS_PER_BLOCK) + +/*! \brief Size of an unscrambled block */ +#define BLOCK_SIZE (SECTOR_SIZE * SECTORS_PER_BLOCK) + +#endif diff --git a/libfriidump/disc.c b/libfriidump/disc.c new file mode 100644 index 0000000..6d76c2c --- /dev/null +++ b/libfriidump/disc.c @@ -0,0 +1,1675 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/*! \file + * \brief Analyser and dumper for Nintendo GameCube/Wii discs. + * + * The functions in this file can be used to retrieve information about a Nintendo GameCube/Wii optical disc. Information is both structural (i.e.: Number of + * sectors, partitions, etc) and game-related (i.e.: Game Title, version, etc). This is the main object that should be used by applications. + * + * Most of the disc structure information used in this file comes from http://www.gc-linux.org/docs/yagcd.html and + * http://www.wiili.org/index.php/GameCube_Optical_Disc . + */ + +#include "misc.h" +#include +#include +#include +//#include +#include "constants.h" +#include "byteorder.h" +#include "disc.h" +#include "dvd_drive.h" +#include "unscrambler.h" + +// #define cachedebug(...) debug (__VA_ARGS__); +#define cachedebug(...) + + +/* Cache always deals with 16-sector blocks. All numbers refer to the 16-sector blocks */ +#define DISC_MINIMUM_CACHE_SIZE 5 +#define DISC_DEFAULT_CACHE_SIZE 40 +#define CACHE_ENTRY_INVALID ((u_int32_t) -1) + + +#define DISC_GAMECUBE_SECTORS_NO 0x0AE0B0 /* 712880 */ +#define DISC_WII_SECTORS_NO_SL 0x230480 /* 2294912 */ +#define DISC_WII_SECTORS_NO_DL 0x3F69C0 /* 4155840 */ + + +#define MAX_READ_RETRIES 5 + +#define DEFAULT_READ_METHOD 0 +#define DEFAULT_READ_SECTOR disc_read_sector_0 + + +typedef int (*disc_read_sector_func) (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata); + +u_int8_t buf[1024*1024*4]; +u_int8_t buf_unscrambled[1024*1024*4]; + +//struct timeval tim; +//double t1, t2; + +/*! \brief A structure that represents a Nintendo GameCube/Wii optical disc. + */ +struct disc_s { + dvd_drive *dvd; //!< The structure for the DVD-drive the disc is inserted in. + disc_type type; //!< The disc type. + char system_id; //!< A letter identifying the target system. + char game_id[2 + 1]; //!< Two letters identifying the game. + disc_region region; //!< The disc region. + char maker[3]; //!< Two letters identifying the maker of the game. + u_int8_t version; //!< A number identifying the game version. + char *version_string; //!< The same as version, in a more human-understandable format. + char *title; //!< The game title. + bool has_update; //!< True if the game contains a system update (Only possible for Wii discs). + u_int32_t sectors_no; //!< The number of sectors of the disc. + u_int32_t layerbreak; //!< For dual-layer DVDs. + + u_int32_t sec_disc; + u_int32_t sec_mem; + u_int32_t max_cnt; + u_int32_t max_blk; + + /* Read function & stuff */ + int command; //!< Buffer access command ID. + int read_method; //!< The read method ID. +// int def_read_method; //!< Default read method ID. + disc_read_sector_func read_sector; //!< The actual function that will be used to perform read operations, corresponding to read_method. + bool unscrambling; //!< If true, raw data read from the disc will be unscrambled to assure it is error-free. Disabling this is only useful for raw performance tests. + unscrambler *u; //!< The unscrambler structure that will be used to perform the unscrambling. + + /* Read cache */ + u_int32_t cache_size; //!< The number of blocks that will be cached when read. + u_int8_t **raw_cache; //!< Memory area for raw sectors cache. + u_int8_t **cache; //!< Memory area for unscrambled sectors cache. + u_int32_t *cache_map; //!< Data structure used by the caching system to know which blocks are in memory. +}; + + +static void disc_cache_init (disc *d, u_int32_t size) { + u_int32_t i; + + if (size < DISC_MINIMUM_CACHE_SIZE) { + error ("Invalid cache size %u (must be >= %u)", size, DISC_MINIMUM_CACHE_SIZE); + exit (3); + } else { + d -> cache_size = size; + d -> cache = (u_int8_t **) malloc (sizeof (u_int8_t *) * size); + d -> raw_cache = (u_int8_t **) malloc (sizeof (u_int8_t *) * size); + for (i = 0; i < size; i++) { + d -> cache[i] = (u_int8_t *) malloc (sizeof (u_int8_t) * BLOCK_SIZE); + d -> raw_cache[i] = (u_int8_t *) malloc (sizeof (u_int8_t) * RAW_BLOCK_SIZE); + } + + d -> cache_map = (u_int32_t *) malloc (sizeof (u_int32_t) * size); + for (i = 0; i < size; i++) + d -> cache_map[i] = CACHE_ENTRY_INVALID; + } + + return; +} + + +static void disc_cache_destroy (disc *d) { + u_int32_t i; + + my_free (d -> cache_map); + + for (i = 0; i < d -> cache_size; i++) { + my_free (d -> cache[i]); + my_free (d -> raw_cache[i]); + } + my_free (d -> cache); + my_free (d -> raw_cache); + d -> cache_size = 0; + + return; +} + + +void disc_cache_add_block (disc *d, u_int32_t block, u_int8_t *data, u_int8_t *rawdata) { + u_int32_t pos; + u_int32_t cnt; + + pos = block % d -> cache_size; + //uniform unscrambled output + memcpy (d -> cache[pos], data, BLOCK_SIZE); + if (d -> type == DISC_TYPE_DVD) { + for (cnt = 0; cnt < SECTORS_PER_BLOCK; cnt++) { + memcpy (rawdata+(cnt*RAW_SECTOR_SIZE)+12, data+(cnt*SECTOR_SIZE), SECTOR_SIZE); + } + } else { + for (cnt = 0; cnt < SECTORS_PER_BLOCK; cnt++) { + memcpy (rawdata+(cnt*RAW_SECTOR_SIZE)+6, data+(cnt*SECTOR_SIZE), SECTOR_SIZE); + } + } + memcpy (d -> raw_cache[pos], rawdata, RAW_BLOCK_SIZE); + d -> cache_map[pos] = block; + + cachedebug ("Cached block %u (sectors %u-%u) at position %u", block, block * SECTORS_PER_BLOCK, (block + 1) * SECTORS_PER_BLOCK - 1, pos); + + return; +} + + +static bool disc_cache_lookup_block (disc *d, u_int32_t block, u_int8_t **data, u_int8_t **rawdata) { + u_int32_t pos; + bool out; + + pos = block % d -> cache_size; + + if (d -> cache_map[pos] == block) { + cachedebug ("Cache HIT for block %u", block); + if (data) + *data = d -> cache[pos]; + if (rawdata) + *rawdata = d -> raw_cache[pos]; + out = true; + } else { + cachedebug ("Cache MISS for block %u", block); + if (data) + *data = NULL; + if (rawdata) + *rawdata = NULL; + out = false; + } + + return (out); +} + + +static int disc_read_sector_generic (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata, u_int32_t method) { + bool out; + u_int32_t start_block; + int ret, retry; + u_int32_t step, cnt, max_cnt, max_blk; + u_int32_t block_len, block_size, _block_size, last_block_size, block_cnt; +//fprintf (stdout,"disc_read_sector_%d", method); + start_block = sector_no / SECTORS_PER_BLOCK; + + out = false; + step = d->sec_mem; + max_cnt = d->max_cnt; + max_blk = d->max_blk; + + block_size = step*2064; + last_block_size = block_size; + block_len = 1; + if (block_size > 27 * 2064) { + block_len = block_size / (27*2064); + if (block_size % (27*2064) != 0) block_len += 1; + block_size = 27*2064; + last_block_size = (step*2064) - (27*2064*(block_len-1)); + } + _block_size=block_size; + + for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { + /* Assume everything will turn out well */ + out = true; + + //Streaming read + if (retry < 3) { + cnt=0; + while (cnt <= max_cnt){ + + _block_size=block_size; + if (method == 0 || method == 1 || method == 4) { + if (sector_no+(cnt*step) +992 +16 <= d -> sectors_no) //smaller than last sector + dvd_read_sector_dummy (d -> dvd, sector_no+(cnt*step) +992, 16, NULL, NULL, 0); + else if (sector_no+(cnt*step) -992 >= 0) //larger than first sector + dvd_read_sector_dummy (d -> dvd, sector_no+(cnt*step) -992, 16, NULL, NULL, 0); + else dvd_flush_cache_READ12 (d -> dvd, sector_no+(cnt*step), NULL); + } + + if (method == 0 || method == 2 || method == 5) dvd_flush_cache_READ12 (d -> dvd, sector_no+(cnt*step), NULL); + if (method == 0 || method == 1 || method == 2 || method == 3) ret = dvd_read_sector_dummy (d -> dvd, sector_no+(cnt*step), d->sec_disc, NULL, &buf_unscrambled[0], 2064*step); + if (method == 4 || method == 5 || method == 6) ret = dvd_read_streaming (d -> dvd, sector_no+(cnt*step), d->sec_disc, NULL, &buf_unscrambled[0], 2064*step); + if (ret >= 0) { + for (block_cnt=0; block_cnt dvd, block_cnt*27*2064, 1, _block_size, &buf[(cnt*(2064 * step))+(block_cnt*27*2064)]) < 0) { + error ("Memdump failed"); + //retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ //no it's not! + out = false; + break; + } + if (block_cnt==block_len-1) _block_size = last_block_size; + } + if (!out) break; + //do this check only on 1st layer + else if (((buf[cnt*(2064*step)] & 1) == 0) && ((buf[cnt*(2064*step)+1]<<16)+(buf[cnt*(2064*step)+2]<<8)+(buf[cnt*(2064*step)+3]) != 0x30000 + sector_no+(cnt*step))) { + out = false; + break; + } + else cnt += 1; + } else { + error ("dvd_read_streaming() failed with %d", ret); + out = false; + break; + } + + } + + if (cnt < max_cnt) out = false; + else { +#ifdef DEBUG + if (d -> unscrambling) { +#endif + /* Try to unscramble all data to see if EDC fails */ + //for(cnt=0; cnt <= 4; cnt++) { + for(cnt=max_blk; cnt--;) { + if (!unscrambler_unscramble_16sectors (d -> u, sector_no+(cnt*16), &buf[cnt*(2064*16)], &buf_unscrambled[cnt*(2048*16)])) + out = false; + } +#ifdef DEBUG + } +#endif + } + if (out) { + /* If data were unscrambled correctly, add them to the cache */ + //for(cnt = 0; cnt <= 4; cnt++) { + for(cnt=max_blk; cnt--;) { + disc_cache_add_block (d, start_block+cnt, &buf_unscrambled[cnt*(2048*16)], &buf[cnt*(2064*16)]); + } + } + } //if (retry < 3) + + //Simple read on 4rth try + else { + if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector + dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); + else if (sector_no -992 >= 0) //larger than first sector + dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); + else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); + + dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); + ret = dvd_read_sector_dummy (d -> dvd, sector_no, SECTORS_PER_BLOCK, NULL, NULL, 0); + if (ret >= 0) { + if (dvd_memdump (d -> dvd, 0, 1, RAW_BLOCK_SIZE, buf) < 0) { + error ("Memdump failed"); + //retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ + out = false; + } + else if ( ((*(buf) & 1) == 0) && ((*(buf+1)<<16)+(*(buf+2)<<8)+(*(buf+3)) != 0x30000+sector_no) ) out = false; + else { +#ifdef DEBUG + if (d -> unscrambling) { +#endif + /* Try to unscramble all data to see if EDC fails */ + if (!unscrambler_unscramble_16sectors (d -> u, sector_no, buf, buf_unscrambled)) + out = false; +#ifdef DEBUG + } +#endif + } + if (out) { + /* If data were unscrambled correctly, add them to the cache */ + disc_cache_add_block (d, start_block, buf_unscrambled, buf); + } + } else { + error ("dvd_read_sector_dummy() failed with %d", ret); + out = false; + } + } //else + } //for + + if (!out) + error ("Too many retries, giving up"); + + return (out); +} + + +///////////////////////////// General ///////////////////////////// +static int disc_read_sector_0 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + return disc_read_sector_generic (d, sector_no, data, rawdata, 0); +} + + + +////////////////////////// Non-Streaming ////////////////////////// +static int disc_read_sector_1 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + return disc_read_sector_generic (d, sector_no, data, rawdata, 1); + +} + +static int disc_read_sector_2 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + return disc_read_sector_generic (d, sector_no, data, rawdata, 2); + +} + + +static int disc_read_sector_3 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + return disc_read_sector_generic (d, sector_no, data, rawdata, 3); + +} + + + +//////////////////////////// Streaming //////////////////////////// +static int disc_read_sector_4 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + return disc_read_sector_generic (d, sector_no, data, rawdata, 4); +} + + +static int disc_read_sector_5 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + return disc_read_sector_generic (d, sector_no, data, rawdata, 5); +} + + +static int disc_read_sector_6 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + return disc_read_sector_generic (d, sector_no, data, rawdata, 6); +} + + + +///////////////////////////// Hitachi ///////////////////////////// +static int disc_read_sector_7 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + bool out; + u_int32_t start_block; + int j, ret, retry; + u_int8_t buf[5][16 * 2064]; + u_int8_t buf_unscrambled[5][16 * 2048]; +//fprintf (stdout,"disc_read_sector_7"); + start_block = sector_no / SECTORS_PER_BLOCK; + + out = false; + for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { + /* Assume everything will turn out well */ + out = true; + + if (retry > 0) { + warning ("Read retry %d for sector %u", retry, sector_no); + + /* Try to reset in-memory data by seeking to a distant sector */ +// if (sector_no > 1000) +// dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0); +// else +// dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0); + if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector + dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); + else if (sector_no -992 >= 0) //larger than first sector + dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); + else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); + } + + if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, NULL, 0)) >= 0) { + for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { + if (dvd_memdump (d -> dvd, 0 + (j * 16 * 2064), 1, 16 * 2064, buf[j]) < 0) { /* Dumping in a single block is faster */ + error ("Memdump failed"); + out = false; + retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ + } else { +#ifdef DEBUG + if (d -> unscrambling) { +#endif + /* Try to unscramble all data to see if EDC fails */ + if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j])) + out = false; +#ifdef DEBUG + } +#endif + } + } + + if (out) { + /* It seems all data was unscrambled correctly, so cache them out */ + for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no; j++) + disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]); + + } + } else { + error ("dvd_read_sector_streaming() failed with %d", ret); + out = false; + } + } + + if (!out) + error ("Too many retries, giving up"); + + return (out); +} + + +static int disc_read_sector_8 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + bool out; + u_int32_t ram_offset; + int j, k, ret, retry; + u_int8_t *sect, buf[5][RAW_BLOCK_SIZE]; + u_int8_t readbuf[BLOCK_SIZE]; + u_int8_t buf_unscrambled[5][BLOCK_SIZE]; + u_int32_t start_block; +//fprintf (stdout,"disc_read_sector_8"); + start_block = sector_no / SECTORS_PER_BLOCK; + + out = false; + for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { + /* Assume everything will turn out well */ + out = true; + + if (retry > 0) { + warning ("Read retry %d for sector %u", retry, sector_no); + + /* Try to reset in-memory data by seeking to a distant sector */ +// if (sector_no > 1000) +// dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0); +// else +// dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0); + if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector + dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); + else if (sector_no -992 >= 0) //larger than first sector + dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); + else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); + } + + /* First READ command, this will cache 5 16-sector blocks. Immediately dump relevant data */ + if (sector_no > d -> sectors_no - 1000) + dvd_read_sector_streaming (d -> dvd, sector_no - 16 * 5 * 2, NULL, NULL, 0); + else + dvd_read_sector_streaming (d -> dvd, sector_no + 16 * 5, NULL, NULL, 0); + if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, readbuf, sizeof (readbuf))) >= 0) { + for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { + /* Reconstruct raw sectors */ + for (k = 0; k < 16; k++) { + sect = &buf[j][k * RAW_SECTOR_SIZE]; + ram_offset = (j * RAW_BLOCK_SIZE) + k * RAW_SECTOR_SIZE; + /* Get first 12 bytes (ID. IED and CPR_MAI fields) and last 4 bytes (EDC field) with memdump */ + if (dvd_memdump (d -> dvd, ram_offset, 1, 12, sect) < 0) { + error ("Memdump (1) failed"); + out = false; + retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ + } else if (dvd_memdump (d -> dvd, ram_offset + 2060, 1, 4, sect + 2060) < 0) { /* Dumping in a single block is faster */ + error ("Memdump (2) failed"); + out = false; + } + } + } + + /* Now the same for remaining 4 16-sector blocks */ + for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { + if (j == 0 || (ret = dvd_read_sector_streaming (d -> dvd, sector_no + j * 16, NULL, readbuf, sizeof (readbuf))) >= 0) { + /* Copy "user data" field which has been incorrectly unscrambled by the DVD drive firmware */ + for (k = 0; k < 16; k++) { + sect = &buf[j][k * RAW_SECTOR_SIZE]; + memcpy (sect + 12, readbuf + k * SECTOR_SIZE, SECTOR_SIZE); + } +#ifdef DEBUG + if (d -> unscrambling) { +#endif + /* Try to unscramble all data to see if EDC fails */ + if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j])) + out = false; +#ifdef DEBUG + } +#endif + } else { + error ("dvd_read_sector_streaming() failed with %d", ret); + out = false; + } + } + + if (out) { + /* It seems all data were unscrambled correctly, so cache them out */ + for (j = 0; j < 5 && sector_no + j * SECTORS_PER_BLOCK < d -> sectors_no; j++) + disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]); + } + } else { + error ("dvd_read_sector_streaming() failed with %d", ret); + out = false; + } + } + + if (!out) + error ("Too many retries, giving up"); + + return (out); +} + + +static int disc_read_sector_9 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + bool out; + u_int32_t ram_offset; + int j, k, ret, retry; + u_int8_t *sect, buf[5][RAW_BLOCK_SIZE]; + u_int8_t readbuf[BLOCK_SIZE], tmp[16]; + u_int8_t buf_unscrambled[5][BLOCK_SIZE]; + u_int32_t start_block; +//fprintf (stdout,"disc_read_sector_9"); + start_block = sector_no / SECTORS_PER_BLOCK; + + out = false; + for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { + /* Assume everything will turn out well */ + out = true; + + if (retry > 0) { + warning ("Read retry %d for sector %u", retry, sector_no); + + /* Try to reset in-memory data by seeking to a distant sector */ +// if (sector_no > 1000) +// dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0); +// else +// dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0); + if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector + dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); + else if (sector_no -992 >= 0) //larger than first sector + dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); + else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); + } + + /* First READ command, this will cache 5 16-sector blocks. Immediately dump relevant data */ + if (sector_no > d -> sectors_no - 1000) + dvd_read_sector_streaming (d -> dvd, sector_no - 16 * 5 * 2, NULL, NULL, 0); + else + dvd_read_sector_streaming (d -> dvd, sector_no + 16 * 5, NULL, NULL, 0); + if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, readbuf, BLOCK_SIZE)) >= 0) { + for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { + /* Reconstruct raw sectors */ + for (k = 0; k < 16; k++) { + sect = &buf[j][k * RAW_SECTOR_SIZE]; + ram_offset = (j * RAW_BLOCK_SIZE) + k * RAW_SECTOR_SIZE; + /* Get first 12 bytes (ID. IED and CPR_MAI fields) and last 4 bytes (EDC field) with memdump */ + if (j == 0 && k == 0) { + if (dvd_memdump (d -> dvd, ram_offset, 1, 12, sect) < 0) { + error ("Memdump (1) failed"); + out = false; + retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ + } + } else { + memcpy (sect, tmp + 4, 12); + } + + if (out && dvd_memdump (d -> dvd, ram_offset + 2060, 1, 16, tmp) < 0) { /* Dumping in a single block is faster */ + error ("Memdump (2) failed"); + out = false; + } else { + memcpy (sect + 2060, tmp, 4); + } + } + } + + /* Now the same for remaining 4 16-sector blocks */ + for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { + if (j == 0 || (ret = dvd_read_sector_streaming (d -> dvd, sector_no + j * 16, NULL, readbuf, BLOCK_SIZE)) >= 0) { + /* Copy "user data" field which has been incorrectly unscrambled by the DVD drive firmware */ + for (k = 0; k < 16; k++) { + sect = &buf[j][k * RAW_SECTOR_SIZE]; + memcpy (sect + 12, readbuf + k * SECTOR_SIZE, SECTOR_SIZE); + } +#ifdef DEBUG + if (d -> unscrambling) { +#endif + /* Try to unscramble all data to see if EDC fails */ + if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j])) + out = false; +#ifdef DEBUG + } +#endif + } else { + error ("dvd_read_sector_streaming() failed with %d", ret); + out = false; + } + } + + if (out) { + /* It seems all data were unscrambled correctly, so cache them out */ + for (j = 0; j < 5 && sector_no + j * SECTORS_PER_BLOCK < d -> sectors_no; j++) + disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]); + } + } else { + error ("dvd_read_sector_streaming() failed with %d", ret); + out = false; + } + } + + if (!out) + error ("Too many retries, giving up"); + + return (out); +} + + +/* We could also use the 'System ID' (first byte of the image) to tell the discs apart */ +static disc_type disc_detect_type (disc *d, u_int32_t forced_type, u_int32_t sectors_no) { + req_sense sense; + + if (forced_type==0) { + d -> type = DISC_TYPE_GAMECUBE; + d -> sectors_no = DISC_GAMECUBE_SECTORS_NO; + } else if (forced_type==1) { + d -> type = DISC_TYPE_WII; + d -> sectors_no = DISC_WII_SECTORS_NO_SL; + } else if (forced_type==2) { + d -> type = DISC_TYPE_WII_DL; + d -> sectors_no = DISC_WII_SECTORS_NO_DL; + //dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL); + } else if (forced_type==3) { + d -> type = DISC_TYPE_DVD; + if (sectors_no == -1) dvd_get_size(d->dvd, &(d -> sectors_no), NULL); + dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL); + } else { + + /* Try to read a sector beyond the end of GameCube discs */ + if (!dvd_read_sector_dummy (d -> dvd, DISC_GAMECUBE_SECTORS_NO + 100, SECTORS_PER_BLOCK, &sense, NULL, 0) && sense.sense_key == 0x05 && sense.asc == 0x21) { + d -> type = DISC_TYPE_GAMECUBE; + d -> sectors_no = DISC_GAMECUBE_SECTORS_NO; + } else { + if (!dvd_read_sector_dummy (d -> dvd, DISC_WII_SECTORS_NO_SL + 100, SECTORS_PER_BLOCK, &sense, NULL, 0) && sense.sense_key == 0x05 && sense.asc == 0x21) { + d -> type = DISC_TYPE_WII; + d -> sectors_no = DISC_WII_SECTORS_NO_SL; + } else { + d -> type = DISC_TYPE_WII_DL; + d -> sectors_no = DISC_WII_SECTORS_NO_DL; + //dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL); + } + } + + } + if (sectors_no != -1) d -> sectors_no = sectors_no; + + return (d -> type); +} + + +/** + * Reads a sector from the disc (or from the cache), using the preset read method. + * @param d The disc structure. + * @param sector_no The requested sector number. + * @param data A buffer to hold the unscrambled sector data (or NULL). + * @param rawdata A buffer to hold the raw sector data (or NULL). + * @return + */ +int disc_read_sector (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { + u_int32_t block; + u_int8_t *cdata, *crawdata; + int out; + + /* Unscrambled data cannot be requested if unscrambling was disabled */ + MY_ASSERT (!(data && !d -> unscrambling)); + + block = sector_no / SECTORS_PER_BLOCK; + + /* See if sector is in cache */ + if (!(out = disc_cache_lookup_block (d, block, &cdata, &crawdata))) { + /* Requested block is not in cache, try to read it from media */ + out = d -> read_sector (d, sector_no, data, rawdata); + + /* Now requested sector is in cache, for sure ;) */ + if (out) + MY_ASSERT (disc_cache_lookup_block (d, block, &cdata, &crawdata)); + } + + if (out) { + if (data) + *data = cdata + (sector_no % SECTORS_PER_BLOCK) * SECTOR_SIZE; + if (rawdata) + *rawdata = crawdata + (sector_no % SECTORS_PER_BLOCK) * RAW_SECTOR_SIZE; + } else { + if (data) + *data = NULL; + if (rawdata) + *rawdata = NULL; + } + + return (out); +} + + +static bool disc_analyze (disc *d) { + u_int8_t *buf; + char tmp[0x03E0 + 1]; + bool unscramble_old, out; + + /* Force unscrambling for this read */ + unscramble_old = d -> unscrambling; + disc_set_unscrambling (d, true); + + if (disc_read_sector (d, 0, &buf, NULL)) { + /* System ID */ + d -> system_id = buf[0]; +// if (d -> system_id == 'G') { +// d -> type = DISC_TYPE_GAMECUBE; +// d -> sectors_no = DISC_GAMECUBE_SECTORS_NO; +// } else if (d -> system_id == 'R') { +// d -> type = DISC_TYPE_WII; +// d -> sectors_no = DISC_WII_SECTORS_NO; +// } else { +// error ("Unknown system ID: '%c'", d -> system_id); +// MY_ASSERT (false); +// } + + /* Game ID */ + strncpy (d -> game_id, (char *) buf + 1, 2); + d -> game_id[2] = '\0'; + + /* Region */ + switch (buf[3]) { + case 'P': + d -> region = DISC_REGION_PAL; + break; + case 'E': + d -> region = DISC_REGION_NTSC; + break; + case 'J': + d -> region = DISC_REGION_JAPAN; + break; + case 'U': + d -> region = DISC_REGION_AUSTRALIA; + break; + case 'F': + d -> region = DISC_REGION_FRANCE; + break; + case 'D': + d -> region = DISC_REGION_GERMANY; + break; + case 'I': + d -> region = DISC_REGION_ITALY; + break; + case 'S': + d -> region = DISC_REGION_SPAIN; + break; + case 'X': + d -> region = DISC_REGION_PAL_X; + break; + case 'Y': + d -> region = DISC_REGION_PAL_Y; + break; + default: + d -> region = DISC_REGION_UNKNOWN; + break; + } + + /* Maker code */ + strncpy (d -> maker, (char *) buf + 4, 2); + d -> maker[2] = '\0'; + + /* Version */ + d -> version = buf[7]; + snprintf (tmp, sizeof (tmp), "1.%02u", d -> version); + my_strdup (d -> version_string, tmp); + + /* Game title */ + memcpy (tmp, buf + 0x0020, sizeof (tmp) - 1); + tmp[sizeof (tmp) - 1] = '\0'; + strtrimr (tmp); + my_strdup (d -> title, tmp); + + out = true; + } else { + error ("Cannot analyze disc"); + out = false; + } + + disc_set_unscrambling (d, unscramble_old); + + return (out); +} + + +static char disc_type_strings[4][15] = { + "GameCube", + "Wii", + "Wii_DL", + "DVD" +}; + +/** + * Retrieves the disc type. + * @param d The disc structure. + * @param dt This will be set to the disc type. + * @param dt_s This will point to a string describing the disc type. + * @return A string describing the disc type. + */ +char *disc_get_type (disc *d, disc_type *dt, char **dt_s) { + if (dt) + *dt = d -> type; + + if (dt_s) { + if (d -> type < DISC_TYPE_DVD) + *dt_s = disc_type_strings[d -> type]; + else + *dt_s = disc_type_strings[DISC_TYPE_DVD]; + } + + return (*dt_s); +} + + +/** + * Retrieves the disc game ID. + * @param d The disc structure. + * @param gid_s This will point to a string containing the game ID. + * @return A string containing the game ID. + */ +char *disc_get_gameid (disc *d, char **gid_s) { + if (gid_s) + *gid_s = d -> game_id; + + return (*gid_s); +} + + +static char disc_region_strings[11][15] = { + "Europe/PAL", + "USA/NTSC", + "Japan/NTSC", + "Australia/PAL", + "France/PAL", + "Germany/PAL", + "Italy/PAL", + "Spain/PAL", + "Europe(X)/PAL", + "Europe(Y)/PAL", + "Unknown" +}; + +/** + * Retrieves the disc region. + * @param d The disc structure. + * @param dr This will be set to the disc region. + * @param dr_s This will point to a string describing the disc region. + * @return A string describing the disc region. + */ +char *disc_get_region (disc *d, disc_region *dr, char **dr_s) { + if (dr) + *dr = d -> region; + + if (dr_s) { + if (d -> region < DISC_REGION_UNKNOWN) + *dr_s = disc_region_strings[d -> region]; + else + *dr_s = disc_region_strings[DISC_REGION_UNKNOWN]; + } + + return (*dr_s); +} + + +/* The following list has been derived from http://wiitdb.com/Company/HomePage */ +static struct { + char *code; + char *name; +} makers[] = { + {"0A", "Jaleco"}, + {"0B", "Coconuts Japan"}, + {"0C", "Coconuts Japan / G.X.Media"}, + {"0D", "Micronet"}, + {"0E", "Technos"}, + {"0F", "Mebio Software"}, + {"0G", "Shouei System"}, + {"0H", "Starfish"}, + {"0J", "Mitsui Fudosan / Dentsu"}, + {"0L", "Warashi Inc."}, + {"0N", "Nowpro"}, + {"0P", "Game Village"}, + {"0Q", "IE Institute"}, + {"01", "Nintendo"}, + {"02", "Rocket Games / Ajinomoto"}, + {"03", "Imagineer-Zoom"}, + {"04", "Gray Matter"}, + {"05", "Zamuse"}, + {"06", "Falcom"}, + {"07", "Enix"}, + {"08", "Capcom"}, + {"09", "Hot B Co."}, + {"1A", "Yanoman"}, + {"1C", "Tecmo Products"}, + {"1D", "Japan Glary Business"}, + {"1E", "Forum / OpenSystem"}, + {"1F", "Virgin Games (Japan)"}, + {"1G", "SMDE"}, + {"1J", "Daikokudenki"}, + {"1P", "Creatures Inc."}, + {"1Q", "TDK Deep Impresion"}, + {"2A", "Culture Brain"}, + {"2C", "Palsoft"}, + {"2D", "Visit Co.,Ltd."}, + {"2E", "Intec"}, + {"2F", "System Sacom"}, + {"2G", "Poppo"}, + {"2H", "Ubisoft Japan"}, + {"2J", "Media Works"}, + {"2K", "NEC InterChannel"}, + {"2L", "Tam"}, + {"2M", "Jordan"}, + {"2N", "Smilesoft / Rocket"}, + {"2Q", "Mediakite"}, + {"3B", "Arcade Zone Ltd"}, + {"3C", "Entertainment International / Empire Software"}, + {"3D", "Loriciel"}, + {"3E", "Gremlin Graphics"}, + {"3F", "K.Amusement Leasing Co."}, + {"4B", "Raya Systems"}, + {"4C", "Renovation Products"}, + {"4D", "Malibu Games"}, + {"4F", "Eidos"}, + {"4G", "Playmates Interactive"}, + {"4J", "Fox Interactive"}, + {"4K", "Time Warner Interactive"}, + {"4Q", "Disney Interactive"}, + {"4S", "Black Pearl"}, + {"4U", "Advanced Productions"}, + {"4X", "GT Interactive"}, + {"4Y", "RARE"}, + {"4Z", "Crave Entertainment"}, + {"5A", "Mindscape / Red Orb Entertainment"}, + {"5B", "Romstar"}, + {"5C", "Taxan"}, + {"5D", "Midway / Tradewest"}, + {"5F", "American Softworks"}, + {"5G", "Majesco Sales Inc"}, + {"5H", "3DO"}, + {"5K", "Hasbro"}, + {"5L", "NewKidCo"}, + {"5M", "Telegames"}, + {"5N", "Metro3D"}, + {"5P", "Vatical Entertainment"}, + {"5Q", "LEGO Media"}, + {"5S", "Xicat Interactive"}, + {"5T", "Cryo Interactive"}, + {"5W", "Red Storm Entertainment"}, + {"5X", "Microids"}, + {"5Z", "Data Design / Conspiracy / Swing"}, + {"6B", "Laser Beam"}, + {"6E", "Elite Systems"}, + {"6F", "Electro Brain"}, + {"6G", "The Learning Company"}, + {"6H", "BBC"}, + {"6J", "Software 2000"}, + {"6K", "UFO Interactive Games"}, + {"6L", "BAM! Entertainment"}, + {"6M", "Studio 3"}, + {"6Q", "Classified Games"}, + {"6S", "TDK Mediactive"}, + {"6U", "DreamCatcher"}, + {"6V", "JoWood Produtions"}, + {"6W", "Sega"}, + {"6X", "Wannado Edition"}, + {"6Y", "LSP (Light & Shadow Prod.)"}, + {"6Z", "ITE Media"}, + {"7A", "Triffix Entertainment"}, + {"7C", "Microprose Software"}, + {"7D", "Sierra / Universal Interactive"}, + {"7F", "Kemco"}, + {"7G", "Rage Software"}, + {"7H", "Encore"}, + {"7J", "Zoo"}, + {"7K", "Kiddinx"}, + {"7L", "Simon & Schuster Interactive"}, + {"7M", "Asmik Ace Entertainment Inc."}, + {"7N", "Empire Interactive"}, + {"7Q", "Jester Interactive"}, + {"7S", "Rockstar Games"}, + {"7T", "Scholastic"}, + {"7U", "Ignition Entertainment"}, + {"7V", "Summitsoft"}, + {"7W", "Stadlbauer"}, + {"8B", "BulletProof Software (BPS)"}, + {"8C", "Vic Tokai Inc."}, + {"8E", "Character Soft"}, + {"8F", "I'Max"}, + {"8G", "Saurus"}, + {"8J", "General Entertainment"}, + {"8N", "Success"}, + {"8P", "Sega Japan"}, + {"9A", "Nichibutsu / Nihon Bussan"}, + {"9B", "Tecmo"}, + {"9C", "Imagineer"}, + {"9F", "Nova"}, + {"9G", "Take2 / Den'Z / Global Star"}, + {"9H", "Bottom Up"}, + {"9J", "TGL (Technical Group Laboratory)"}, + {"9L", "Hasbro Japan"}, + {"9N", "Marvelous Entertainment"}, + {"9P", "Keynet Inc."}, + {"9Q", "Hands-On Entertainment"}, + {"12", "Infocom"}, + {"13", "Electronic Arts Japan"}, + {"15", "Cobra Team"}, + {"16", "Human / Field"}, + {"17", "KOEI"}, + {"18", "Hudson Soft"}, + {"19", "S.C.P."}, + {"20", "Destination Software / Zoo Games / KSS"}, + {"21", "Sunsoft / Tokai Engineering"}, + {"22", "POW (Planning Office Wada) / VR1 Japan"}, + {"23", "Micro World"}, + {"25", "San-X"}, + {"26", "Enix"}, + {"27", "Loriciel / Electro Brain"}, + {"28", "Kemco Japan"}, + {"29", "Seta"}, + {"30", "Viacom"}, + {"31", "Carrozzeria"}, + {"32", "Dynamic"}, + {"34", "Magifact"}, + {"35", "Hect"}, + {"36", "Codemasters"}, + {"37", "Taito / GAGA Communications"}, + {"38", "Laguna"}, + {"39", "Telstar / Event / Taito"}, + {"40", "Seika Corp."}, + {"41", "Ubi Soft Entertainment"}, + {"42", "Sunsoft US"}, + {"44", "Life Fitness"}, + {"46", "System 3"}, + {"47", "Spectrum Holobyte"}, + {"49", "IREM"}, + {"50", "Absolute Entertainment"}, + {"51", "Acclaim"}, + {"52", "Activision"}, + {"53", "American Sammy"}, + {"54", "Take 2 Interactive / GameTek"}, + {"55", "Hi Tech"}, + {"56", "LJN LTD."}, + {"58", "Mattel"}, + {"60", "Titus"}, + {"61", "Virgin Interactive"}, + {"62", "Maxis"}, + {"64", "LucasArts Entertainment"}, + {"67", "Ocean"}, + {"68", "Bethesda Softworks"}, + {"69", "Electronic Arts"}, + {"70", "Atari (Infogrames)"}, + {"71", "Interplay"}, + {"72", "JVC (US)"}, + {"73", "Parker Brothers"}, + {"75", "Sales Curve (Storm / SCI)"}, + {"78", "THQ"}, + {"79", "Accolade"}, + {"80", "Misawa"}, + {"81", "Teichiku"}, + {"82", "Namco Ltd."}, + {"83", "LOZC"}, + {"84", "KOEI"}, + {"86", "Tokuma Shoten Intermedia"}, + {"87", "Tsukuda Original"}, + {"88", "DATAM-Polystar"}, + {"90", "Takara Amusement"}, + {"91", "Chun Soft"}, + {"92", "Video System / Mc O' River"}, + {"93", "BEC"}, + {"95", "Varie"}, + {"96", "Yonezawa / S'pal"}, + {"97", "Kaneko"}, + {"99", "Marvelous Entertainment"}, + {"A0", "Telenet"}, + {"A1", "Hori"}, + {"A4", "Konami"}, + {"A5", "K.Amusement Leasing Co."}, + {"A6", "Kawada"}, + {"A7", "Takara"}, + {"A9", "Technos Japan Corp."}, + {"AA", "JVC / Victor"}, + {"AC", "Toei Animation"}, + {"AD", "Toho"}, + {"AF", "Namco"}, + {"AG", "Media Rings Corporation"}, + {"AH", "J-Wing"}, + {"AJ", "Pioneer LDC"}, + {"AK", "KID"}, + {"AL", "Mediafactory"}, + {"AP", "Infogrames / Hudson"}, + {"AQ", "Kiratto. Ludic Inc"}, + {"B0", "Acclaim Japan"}, + {"B1", "ASCII"}, + {"B2", "Bandai"}, + {"B4", "Enix"}, + {"B6", "HAL Laboratory"}, + {"B7", "SNK"}, + {"B9", "Pony Canyon"}, + {"BA", "Culture Brain"}, + {"BB", "Sunsoft"}, + {"BC", "Toshiba EMI"}, + {"BD", "Sony Imagesoft"}, + {"BF", "Sammy"}, + {"BG", "Magical"}, + {"BH", "Visco"}, + {"BJ", "Compile"}, + {"BL", "MTO Inc."}, + {"BN", "Sunrise Interactive"}, + {"BP", "Global A Entertainment"}, + {"BQ", "Fuuki"}, + {"C0", "Taito"}, + {"C2", "Kemco"}, + {"C3", "Square"}, + {"C4", "Tokuma Shoten"}, + {"C5", "Data East"}, + {"C6", "Tonkin House / Tokyo Shoseki"}, + {"C8", "Koei"}, + {"CA", "Konami / Ultra / Palcom"}, + {"CB", "NTVIC / VAP"}, + {"CC", "Use Co.,Ltd."}, + {"CD", "Meldac"}, + {"CE", "Pony Canyon / FCI"}, + {"CF", "Angel / Sotsu Agency / Sunrise"}, + {"CG", "Yumedia / Aroma Co., Ltd"}, + {"CJ", "Boss"}, + {"CK", "Axela / Crea-Tech"}, + {"CL", "Sekaibunka-Sha / Sumire Kobo / Marigul Management Inc."}, + {"CM", "Konami Computer Entertainment Osaka"}, + {"CN", "NEC Interchannel"}, + {"CP", "Enterbrain"}, + {"CQ", "From Software"}, + {"D0", "Taito / Disco"}, + {"D1", "Sofel"}, + {"D2", "Quest / Bothtec"}, + {"D3", "Sigma"}, + {"D4", "Ask Kodansha"}, + {"D6", "Naxat"}, + {"D7", "Copya System"}, + {"D8", "Capcom Co., Ltd."}, + {"D9", "Banpresto"}, + {"DA", "Tomy"}, + {"DB", "LJN Japan"}, + {"DD", "NCS"}, + {"DE", "Human Entertainment"}, + {"DF", "Altron"}, + {"DG", "Jaleco"}, + {"DH", "Gaps Inc."}, + {"DN", "Elf"}, + {"DQ", "Compile Heart"}, + {"E0", "Jaleco"}, + {"E2", "Yutaka"}, + {"E3", "Varie"}, + {"E4", "T&ESoft"}, + {"E5", "Epoch"}, + {"E7", "Athena"}, + {"E8", "Asmik"}, + {"E9", "Natsume"}, + {"EA", "King Records"}, + {"EB", "Atlus"}, + {"EC", "Epic / Sony Records"}, + {"EE", "IGS (Information Global Service)"}, + {"EG", "Chatnoir"}, + {"EH", "Right Stuff"}, + {"EL", "Spike"}, + {"EM", "Konami Computer Entertainment Tokyo"}, + {"EN", "Alphadream Corporation"}, + {"EP", "Sting"}, + {"ES", "Star-Fish"}, + {"F0", "A Wave"}, + {"F1", "Motown Software"}, + {"F2", "Left Field Entertainment"}, + {"F3", "Extreme Ent. Grp."}, + {"F4", "TecMagik"}, + {"F9", "Cybersoft"}, + {"FB", "Psygnosis"}, + {"FE", "Davidson / Western Tech."}, + {"FK", "The Game Factory"}, + {"FL", "Hip Games"}, + {"FM", "Aspyr"}, + {"FP", "Mastiff"}, + {"FQ", "iQue"}, + {"FR", "Digital Tainment Pool"}, + {"FS", "XS Games / Jack Of All Games"}, + {"FT", "Daiwon"}, + {"G0", "Alpha Unit"}, + {"G1", "PCCW Japan"}, + {"G2", "Yuke's Media Creations"}, + {"G4", "KiKi Co Ltd"}, + {"G5", "Open Sesame Inc"}, + {"G6", "Sims"}, + {"G7", "Broccoli"}, + {"G8", "Avex"}, + {"G9", "D3 Publisher"}, + {"GB", "Konami Computer Entertainment Japan"}, + {"GD", "Square-Enix"}, + {"GE", "KSG"}, + {"GF", "Micott & Basara Inc."}, + {"GH", "Orbital Media"}, + {"GJ", "Detn8 Games"}, + {"GL", "Gameloft / Ubi Soft"}, + {"GM", "Gamecock Media Group"}, + {"GN", "Oxygen Games"}, + {"GT", "505 Games"}, + {"GY", "The Game Factory"}, + {"H1", "Treasure"}, + {"H2", "Aruze"}, + {"H3", "Ertain"}, + {"H4", "SNK Playmore"}, + {"HJ", "Genius Products"}, + {"HY", "Reef Entertainment"}, + {"HZ", "Nordcurrent"}, + {"IH", "Yojigen"}, + {"J9", "AQ Interactive"}, + {"JF", "Arc System Works"}, + {"JW", "Atari"}, + {"K6", "Nihon System"}, + {"KB", "NIS America"}, + {"KM", "Deep Silver"}, + {"LH", "Trend Verlag / East Entertainment"}, + {"LT", "Legacy Interactive"}, + {"MJ", "Mumbo Jumbo"}, + {"MR", "Mindscape"}, + {"MS", "Milestone / UFO Interactive"}, + {"MT", "Blast !"}, + {"N9", "Terabox"}, + {"NK", "Neko Entertainment / Diffusion / Naps team"}, + {"NP", "Nobilis"}, + {"NR", "Data Design / Destineer Studios"}, + {"PL", "Playlogic"}, + {"RM", "Rondomedia"}, + {"RS", "Warner Bros. Interactive Entertainment Inc."}, + {"RT", "RTL Games"}, + {"RW", "RealNetworks"}, + {"S5", "Southpeak Interactive"}, + {"SP", "Blade Interactive Studios"}, + {"SV", "SevenGames"}, + {"TK", "Tasuke / Works"}, + {"UG", "Metro 3D / Data Design"}, + {"VN", "Valcon Games"}, + {"VP", "Virgin Play"}, + {"WR", "Warner Bros. Interactive Entertainment Inc."}, + {"XJ", "Xseed Games"}, + {"XS", "Aksys Games"}, + {NULL, NULL} +}; + +/** + * Retrieves the disk maker. + * @param d The disc structure. + * @param m This will point to a string containing the disc maker ID. + * @param m_s This will point to a string describing the disc maker. + * @return A string describing the disc maker. + */ +char *disc_get_maker (disc *d, char **m, char **m_s) { + u_int32_t i; + + if (m) + *m = d -> maker; + + if (m_s) { + for (i = 0; makers[i].code; i++) { + if (strcasecmp (d -> maker, makers[i].code) == 0) { + *m_s = makers[i].name; + break; + } + } + if (!makers[i].code) { + *m_s = "Unknown"; + } + } + + return (*m_s); +} + + +/** + * Retrieves the disc version. + * @param d The disc structure. + * @param v This will contain the version ID. + * @param v_s This will point to a string describing the disc version. + * @return A string describing the disc version. + */ +char *disc_get_version (disc *d, u_int8_t *v, char **v_s) { + if (v) + *v = d -> version; + + if (v_s) + *v_s = d -> version_string; + + return (*v_s); +} + + +/** + * Retrieves the disc game title. + * @param d The disc structure. + * @param t_s This will point to a string describing the disc title. + * @return A string describing the disc title. + */ +char *disc_get_title (disc *d, char **t_s) { + if (t_s) + *t_s = d -> title; + + return (*t_s); +} + + +/** + * Retrieves if the disc has an update. + * @param d The disc structure. + * @return True if the disc contains an update, false otherwise. + */ +bool disc_get_update (disc *d) { + return (d -> has_update); +} + + +/** + * Retrieves the number of sectors of the disc. + * @param d The disc structure. + * @return The number of sectors. + */ +u_int32_t disc_get_sectors_no (disc *d) { + return (d -> sectors_no); +} + +u_int32_t disc_get_layerbreak (disc *d) { + return (d -> layerbreak); +} + +u_int32_t disc_get_command (disc *d) { + return (d -> command); +} + +u_int32_t disc_get_method (disc *d) { + return (d -> read_method); +} + +u_int32_t disc_get_def_method (disc *d) { + return dvd_get_def_method(d -> dvd);//(d -> def_read_method); +} + +u_int32_t disc_get_sec_disc (disc *d) { + return (d -> sec_disc); +} + +u_int32_t disc_get_sec_mem (disc *d) { + return (d -> sec_mem); +} + +/* wiidevel@stacktic.org */ +static bool disc_check_update (disc *d) { + u_int8_t *buf; + u_int32_t x; + bool unscramble_old; + + if (d -> type == DISC_TYPE_WII || d -> type == DISC_TYPE_WII_DL) { + /* Force unscrambling for this read */ + unscramble_old = d -> unscrambling; + disc_set_unscrambling (d, true); + + /* We need to read offset 0x50004 of the disc. Sector 160 has offset 0x50000 */ + if (disc_read_sector (d, 160, &buf, NULL)) { + x = my_ntohl (*(u_int32_t *) (buf + 4)); + if (x == 0xA5BED6AE) + d -> has_update = false; + else + d -> has_update = true; + } else { + error ("disc_check_update() failed"); + } + + disc_set_unscrambling (d, unscramble_old); + } else { + /* GameCube discs never have an update, as actually the GC firmware cannot be upgrade */ + d -> has_update = false; + } + + return (d -> has_update); +} + + +/** + * Sets the disc read method. + * @param d The disc structure. + * @param method The requested method. + * @return True if the method was set correctly, false otherwise (i. e.: method too small/big). + */ +bool disc_set_read_method (disc *d, int method) { + bool out; + u_int32_t deviation; + u_int32_t counter; + u_int32_t cnt1; + + d -> command = dvd_get_command(d -> dvd); +// d -> def_read_method = dvd_get_def_method(d -> dvd); + d -> read_method = method; + + out = true; + switch (method) { + case 0: + d -> read_sector = disc_read_sector_0; + break; + case 1: + d -> read_sector = disc_read_sector_1; + break; + case 2: + d -> read_sector = disc_read_sector_2; + break; + case 3: + d -> read_sector = disc_read_sector_3; + break; + case 4: + d -> read_sector = disc_read_sector_4; + break; + case 5: + d -> read_sector = disc_read_sector_5; + break; + case 6: + d -> read_sector = disc_read_sector_6; + break; + case 7: + d -> read_sector = disc_read_sector_7; + break; + case 8: + d -> read_sector = disc_read_sector_8; + break; + case 9: + d -> read_sector = disc_read_sector_9; + break; + default: + switch (dvd_get_def_method(d -> dvd)) { + case 0: + d -> read_method = 0; + d -> read_sector = disc_read_sector_0; + break; + case 1: + d -> read_method = 1; + d -> read_sector = disc_read_sector_1; + break; + case 2: + d -> read_method = 2; + d -> read_sector = disc_read_sector_2; + break; + case 3: + d -> read_method = 3; + d -> read_sector = disc_read_sector_3; + break; + case 4: + d -> read_method = 4; + d -> read_sector = disc_read_sector_4; + break; + case 5: + d -> read_method = 5; + d -> read_sector = disc_read_sector_5; + break; + case 6: + d -> read_method = 6; + d -> read_sector = disc_read_sector_6; + break; + case 7: + d -> read_method = 7; + d -> read_sector = disc_read_sector_7; + break; + case 8: + d -> read_method = 8; + d -> read_sector = disc_read_sector_8; + break; + case 9: + d -> read_method = 9; + d -> read_sector = disc_read_sector_9; + break; + default: + d -> read_method = DEFAULT_READ_METHOD; + d -> read_sector = DEFAULT_READ_SECTOR; + break; + } + } + + if (d->sec_disc==-1) { + if ((d->read_method == 4) || (d->read_method == 5) || (d->read_method == 6)) + d->sec_disc=27; + else + d->sec_disc=16; + } + if (d->sec_mem==-1) { + if ((d->read_method == 4) || (d->read_method == 5) || (d->read_method == 6)) + d->sec_mem=27; + else + d->sec_mem=16; + } + + deviation = d->sec_mem % SECTORS_PER_BLOCK; + counter=0; + + if (deviation>3) { + cnt1=deviation; + while (1==1) { + cnt1+=deviation; + counter++; + if (cnt1%SECTORS_PER_BLOCK<=1) break; + } + } + d -> max_cnt = counter; + d -> max_blk = ((d->sec_mem*(d->max_cnt+1))-((d->sec_mem*(d->max_cnt+1)) % SECTORS_PER_BLOCK)) / 16; + + if (out) { + debug ("Read method set to %d", d -> read_method); + } else { + error ("Cannot set read method\n"); + } + + return (out); +} + + +/** + * Controls the unscrambling process. + * @param d The disc structure. + * @param unscramble If true, every raw sectors read will be unscrambled to check if they are error-free, otherwise read data will be returned as-is. + */ +void disc_set_unscrambling (disc *d, bool unscramble) { + d -> unscrambling = unscramble; + debug ("Sectors unscrambling %s", unscramble ? "enabled" : "disabled"); + + return; +} + + +static void disc_crack_seeds (disc *d) { + int i; + + /* As a Nintendo GameCube/Wii disc should not have too many keys, 20 should be enough */ + debug ("Retrieving all DVD seeds"); + for (i = 0; i < 20 * 16; i += 16) + disc_read_sector (d, i, NULL, NULL); + + return; +} + + +/** + * Creates a new structure representing a Nintendo GameCube/Wii optical disc. + * @param dvd_device The CD/DVD-ROM device, in OS-dependent format (i.e.: /dev/something on Unix, x: on Windows). + * @return The newly-created structure, to be used with the other commands. + */ +disc *disc_new (char *dvd_device, u_int32_t command) { + dvd_drive *dvd; + disc *d; + + if ((dvd = dvd_drive_new (dvd_device, command))) { + d = (disc *) malloc (sizeof (disc)); + memset (d, 0, sizeof (disc)); + d -> dvd = dvd; + d -> u = unscrambler_new (); + disc_set_unscrambling (d, true); // Unscramble by default + disc_set_read_method (d, DEFAULT_READ_METHOD); + disc_cache_init (d, DISC_DEFAULT_CACHE_SIZE); + } else { + d = NULL; + } + + return (d); +} + + +bool disc_init (disc *d, u_int32_t disctype, u_int32_t sectors_no) { + bool out; + + d -> sectors_no = 1000; // TODO + disc_detect_type (d, disctype, sectors_no); + disc_crack_seeds (d); +// unscrambler_set_bruteforce (d -> u, false); // Disabling bruteforcing will allow us to detect errors more quickly + unscrambler_set_bruteforce (d -> u, true); + if (d -> type==DISC_TYPE_DVD) { + my_strdup (d -> title, "DVD"+'\0'); + out = true; + } + else if (disc_analyze (d)) { + disc_check_update (d); + out = true; + } else { + out = false; + } + + return (out); +} + + +/** + * Frees resources used by a disc structure and destroys it. + * @param d The disc structure. + * @return NULL. + */ +void *disc_destroy (disc *d) { + disc_cache_destroy (d); + unscrambler_destroy (d -> u); + my_free (d -> version_string); + my_free (d -> title); + dvd_drive_destroy (d -> dvd); + my_free (d); + + return (NULL); +} + + +char *disc_get_drive_model_string (disc *d) { + return (dvd_get_model_string (d -> dvd)); +} + + +bool disc_get_drive_support_status (disc *d) { + return (dvd_get_support_status (d -> dvd)); +} + +void disc_set_speed (disc *d, u_int32_t speed) { + if (speed != -1) dvd_set_speed (d -> dvd, speed, NULL); +} + +void disc_set_streaming_speed (disc *d, u_int32_t speed) { + if (speed != -1) dvd_set_streaming (d -> dvd, speed, NULL); +} + +bool disc_stop_unit (disc *d, bool start) { + if (dvd_stop_unit (d -> dvd, start, NULL) == 0) return true; + else return false; +} + +void init_range (disc *d, u_int32_t sec_disc, u_int32_t sec_mem) { + if ((sec_disc>=1)&&(sec_disc<=100)) d->sec_disc = sec_disc; + else d->sec_disc = -1; + if ((sec_mem>=16)&&(sec_mem<=100)) d->sec_mem = sec_mem; + else d->sec_mem = -1; +} \ No newline at end of file diff --git a/libfriidump/disc.h b/libfriidump/disc.h new file mode 100644 index 0000000..537ca32 --- /dev/null +++ b/libfriidump/disc.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef DISC_H_INCLUDED +#define DISC_H_INCLUDED + +#include "misc.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + DISC_TYPE_GAMECUBE, + DISC_TYPE_WII, + DISC_TYPE_WII_DL, + DISC_TYPE_DVD +} disc_type; + + +typedef enum { + DISC_REGION_PAL, + DISC_REGION_NTSC, + DISC_REGION_JAPAN, + DISC_REGION_AUSTRALIA, + DISC_REGION_FRANCE, + DISC_REGION_GERMANY, + DISC_REGION_ITALY, + DISC_REGION_SPAIN, + DISC_REGION_PAL_X, + DISC_REGION_PAL_Y, + DISC_REGION_UNKNOWN +} disc_region; + + +typedef struct disc_s disc; + + +/* Functions */ +FRIIDUMPLIB_EXPORT disc *disc_new (char *dvd_device, u_int32_t command); +FRIIDUMPLIB_EXPORT bool disc_init (disc *d, u_int32_t forced_type, u_int32_t sectors_no); +FRIIDUMPLIB_EXPORT void *disc_destroy (disc *d); +FRIIDUMPLIB_EXPORT int disc_read_sector (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata); +FRIIDUMPLIB_EXPORT bool disc_set_read_method (disc *d, int method); +FRIIDUMPLIB_EXPORT void disc_set_unscrambling (disc *d, bool unscramble); +FRIIDUMPLIB_EXPORT void disc_set_speed (disc *d, u_int32_t speed); +FRIIDUMPLIB_EXPORT void disc_set_streaming_speed (disc *d, u_int32_t speed); +FRIIDUMPLIB_EXPORT bool disc_stop_unit (disc *d, bool start); +FRIIDUMPLIB_EXPORT void init_range (disc *d, u_int32_t sec_disc, u_int32_t sec_mem); + +/* Getters */ +FRIIDUMPLIB_EXPORT u_int32_t disc_get_sectors_no (disc *d); +FRIIDUMPLIB_EXPORT u_int32_t disc_get_layerbreak (disc *d); +FRIIDUMPLIB_EXPORT u_int32_t disc_get_command (disc *d); +FRIIDUMPLIB_EXPORT u_int32_t disc_get_method (disc *d); +FRIIDUMPLIB_EXPORT u_int32_t disc_get_def_method (disc *d); +FRIIDUMPLIB_EXPORT u_int32_t disc_get_sec_disc (disc *d); +FRIIDUMPLIB_EXPORT u_int32_t disc_get_sec_mem (disc *d); + +FRIIDUMPLIB_EXPORT char *disc_get_type (disc *d, disc_type *dt, char **dt_s); +FRIIDUMPLIB_EXPORT char *disc_get_gameid (disc *d, char **gid_s); +FRIIDUMPLIB_EXPORT char *disc_get_region (disc *d, disc_region *dr, char **dr_s); +FRIIDUMPLIB_EXPORT char *disc_get_maker (disc *d, char **m, char **m_s); +FRIIDUMPLIB_EXPORT char *disc_get_version (disc *d, u_int8_t *v, char **v_s); +FRIIDUMPLIB_EXPORT char *disc_get_title (disc *d, char **t_s); +FRIIDUMPLIB_EXPORT bool disc_get_update (disc *d); + +FRIIDUMPLIB_EXPORT char *disc_get_drive_model_string (disc *d); +FRIIDUMPLIB_EXPORT bool disc_get_drive_support_status (disc *d); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libfriidump/dumper.c b/libfriidump/dumper.c new file mode 100644 index 0000000..87aeeab --- /dev/null +++ b/libfriidump/dumper.c @@ -0,0 +1,439 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://www.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "misc.h" +#include +#include +#include +#include +#include "constants.h" +#include "disc.h" +#include "dumper.h" + +#ifndef WIN32 +#include +#include +#endif + +struct dumper_s { + disc *dsk; + char *outfile_raw; + u_int32_t start_sector_raw; + FILE *fp_raw; + char *outfile_iso; + u_int32_t start_sector_iso; + FILE *fp_iso; + u_int32_t start_sector; + bool hashing; + bool flushing; + + multihash hash_raw; + multihash hash_iso; + + progress_func progress; + void *progress_data; +}; + + +/** + * Tries to open the output file for writing and to find out if it contains valid data so that the dump can continue. + * @param dvd + * @param outfile + * @param[out] fp The file pointer to write to. + * @param[out] start_sector The sector to start reading from. + * @return true if the dumping can start/continue, false otherwise (for instance if outfile cannot be written to). + */ +bool dumper_set_raw_output_file (dumper *dmp, char *outfile_raw, bool resume) { + bool out; + my_off_t filesize; + FILE *fp; + + if (!outfile_raw) { + /* Raw output disabled */ + out = true; + dmp -> start_sector_raw = -1; + my_free (dmp -> outfile_raw); + dmp -> outfile_raw = NULL; + } else if (dmp -> outfile_raw) { + error ("Raw output file already defined"); + out = false; + } else if (!(fp = fopen (outfile_raw, "rb"))) { /** @todo Maybe we could not open file for permission problems */ + /* Raw output file does not exist, start from scratch */ + out = true; + dmp -> start_sector_raw = 0; + my_strdup (dmp -> outfile_raw, outfile_raw); + } else if (resume) { + /* Raw output file exists and resume was requested, so see how many dumped sectors it contains */ + my_fseek (fp, 0, SEEK_END); + filesize = my_ftell (fp); + fclose (fp); + out = true; + dmp -> start_sector_raw = (u_int32_t) (filesize / RAW_SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK; + debug ("Raw output can restart from sector %u", dmp -> start_sector_raw); + my_strdup (dmp -> outfile_raw, outfile_raw); + } else { + /* Raw output file exists but resume was not requested, error */ + fclose (fp); + error ("Raw output file exists, but resume was not requested."); + out = false; + dmp -> start_sector_raw = -1; + my_free (dmp -> outfile_raw); + dmp -> outfile_raw = NULL; + } + + return (out); +} + + +bool dumper_set_iso_output_file (dumper *dmp, char *outfile_iso, bool resume) { + bool out; + my_off_t filesize; + FILE *fp; + + if (!outfile_iso) { + /* Raw output disabled */ + out = true; + dmp -> start_sector_iso = -1; + my_free (dmp -> outfile_iso); + dmp -> outfile_iso = NULL; + } else if (dmp -> outfile_iso) { + error ("ISO output file already defined"); + out = false; + } else if (!(fp = fopen (outfile_iso, "rb"))) { /** @todo Maybe we could not open file for permission problems */ + /* Raw output file does not exist, start from scratch */ + out = true; + dmp -> start_sector_iso = 0; + my_strdup (dmp -> outfile_iso, outfile_iso); + } else if (resume) { + /* Raw output file exists and resume was requested, so see how many dumped sectors it contains */ + my_fseek (fp, 0, SEEK_END); + filesize = my_ftell (fp); + fclose (fp); + out = true; + dmp -> start_sector_iso = (u_int32_t) (filesize / SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK; + debug ("ISO output can restart from sector %u", dmp -> start_sector_iso); + my_strdup (dmp -> outfile_iso, outfile_iso); + } else { + /* Raw output file exists but resume was not requested, error */ + fclose (fp); + error ("ISO output file exists, but resume was not requested."); + out = false; + dmp -> start_sector_iso = -1; + my_free (dmp -> outfile_iso); + dmp -> outfile_iso = NULL; + } + + return (out); +} + + +bool dumper_prepare (dumper *dmp) { + bool out; + u_int8_t buf[RAW_SECTOR_SIZE]; + size_t r; + u_int32_t i; + + /* Outputting to both files, resume must start from the file with the least sectors. Hopefully they will have the same number of sectors, anyway... */ + if (dmp -> outfile_raw && dmp -> outfile_iso && dmp -> start_sector_raw != dmp -> start_sector_iso) { + if (dmp -> start_sector_raw < dmp -> start_sector_iso) + dmp -> start_sector = dmp -> start_sector_raw; + else + dmp -> start_sector = dmp -> start_sector_iso; + } else if (dmp -> outfile_raw) { + dmp -> start_sector = dmp -> start_sector_raw; + } else if (dmp -> outfile_iso) { + dmp -> start_sector = dmp -> start_sector_iso; + } else { + MY_ASSERT (0); + } + + /* Prepare hashes */ + if (dmp -> hashing) { + multihash_init (&(dmp -> hash_raw)); + multihash_init (&(dmp -> hash_iso)); + } + + /* Setup raw output file */ + if (dmp -> outfile_raw) { + dmp -> fp_raw = fopen (dmp -> outfile_raw, "a+b"); + + if (dmp -> hashing) { + debug ("Calculating hashes for pre-existing raw dump data"); + if (dmp -> start_sector > 0) { + for (i = 0; i < dmp -> start_sector && (r = fread (buf, RAW_SECTOR_SIZE, 1, dmp -> fp_raw)) > 0; i++) + multihash_update (&(dmp -> hash_raw), buf, RAW_SECTOR_SIZE); + MY_ASSERT (r > 0); + } + } + + /* Now call fseek as file will only be written, from now on */ + if (my_fseek (dmp -> fp_raw, dmp -> start_sector * RAW_SECTOR_SIZE, SEEK_SET) == 0 && + ftruncate (fileno (dmp -> fp_raw), (int64_t) dmp -> start_sector * RAW_SECTOR_SIZE) == 0) { + out = true; + debug ("Writing to file \"%s\" in raw format (fseeked() to %lld)", dmp -> outfile_raw, my_ftell (dmp -> fp_raw)); + } else { + out = false; + fclose (dmp -> fp_raw); + dmp -> fp_raw = NULL; + } + } else { + dmp -> fp_raw = NULL; + } + + /* Setup ISO output file */ + if (dmp -> outfile_iso) { + dmp -> fp_iso = fopen (dmp -> outfile_iso, "a+b"); + + if (dmp -> hashing) { + debug ("Calculating hashes for pre-existing ISO dump data"); + if (dmp -> start_sector > 0) { + for (i = 0; i < dmp -> start_sector && (r = fread (buf, SECTOR_SIZE, 1, dmp -> fp_iso)) > 0; i++) + multihash_update (&(dmp -> hash_iso), buf, SECTOR_SIZE); + MY_ASSERT (r > 0); + } + } + + if (my_fseek (dmp -> fp_iso, dmp -> start_sector * SECTOR_SIZE, SEEK_SET) == 0 && + ftruncate (fileno (dmp -> fp_iso), (int64_t) dmp -> start_sector * SECTOR_SIZE) == 0) { + out = true; + debug ("Writing to file \"%s\" in ISO format (fseeked() to %lld)", dmp -> outfile_iso, my_ftell (dmp -> fp_iso)); + } else { + out = false; + fclose (dmp -> fp_iso); + dmp -> fp_iso = NULL; + } + } else { + dmp -> fp_iso = NULL; + } + + return (out); +} + + +int dumper_dump (dumper *dmp, u_int32_t *current_sector) { + bool out; + u_int8_t *rawbuf, *isobuf; + u_int32_t i, sectors_no, last_sector; +#ifdef DEBUGaa + bool no_unscrambling; +#endif + +#ifdef DEBUGaa + no_unscrambling = dd -> no_unscrambling; + if (fp_iso && no_unscrambling) { + warning ("Output to ISO format requested, ignoring no_unscrambling!"); + no_unscrambling = false; + } +#endif + + sectors_no = disc_get_sectors_no (dmp -> dsk); +#if 0 + if (dd -> start_sector != -1) { + if (dd -> start_sector >= sectors_no) { + error ("Cannot start dumping from sector %u as the inserted disc only has %u sectors\n", dd -> start_sector, sectors_no); + out = false; + } else { + warning ("Start sector forced to %u\n", dd -> start_sector); + ss = dd -> start_sector; + + if (fp_iso) + fseek (fp_iso, ss * 2048, SEEK_SET); + if (fp_raw) + fseek (fp_raw, ss * 2064, SEEK_SET); + } + } +#endif + + if (true) { + debug ("Starting dump process from sector %u...\n", dmp -> start_sector); + + /* First call to progress function */ + if (dmp -> progress) + dmp -> progress (true, dmp -> start_sector, sectors_no, dmp -> progress_data); + + last_sector=sectors_no-1; + + for (i = dmp -> start_sector, out = true; i < sectors_no && out; i++) { + disc_read_sector (dmp -> dsk, i, &isobuf, &rawbuf); + + if (dmp -> fp_raw) { + clearerr (dmp -> fp_raw); + + if (!rawbuf) { + error ("NULL buffer"); + out = false; + *(current_sector) = i; + } + else fwrite (rawbuf, RAW_SECTOR_SIZE, 1, dmp -> fp_raw); + if (ferror (dmp -> fp_raw)) { + error ("fwrite() to raw output file failed"); + out = false; + *(current_sector) = i; + } + + if (dmp -> flushing) + fflush (dmp -> fp_raw); + + if (dmp -> hashing && out) + multihash_update (&(dmp -> hash_raw), rawbuf, RAW_SECTOR_SIZE); + } + + if (dmp -> fp_iso) { + clearerr (dmp -> fp_iso); + + if (!isobuf) { + error ("NULL buffer"); + out = false; + *(current_sector) = i; + } + else fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_iso); + + if (ferror (dmp -> fp_iso)) { + error ("fwrite() to ISO output file failed"); + out = false; + *(current_sector) = i; + } + + if (dmp -> flushing) + fflush (dmp -> fp_iso); + + if (dmp -> hashing && out) + multihash_update (&(dmp -> hash_iso), isobuf, SECTOR_SIZE); + } + + if ((i % 320 == 0) || (i == last_sector)) { //speedhack + if (dmp -> progress) + dmp -> progress (false, i + 1, sectors_no, dmp -> progress_data); /* i + 1 'cause sectors range from 0 to N */ + } + } + + if (dmp -> hashing) { + multihash_finish (&(dmp -> hash_raw)); + multihash_finish (&(dmp -> hash_iso)); + } + + if (dmp -> fp_raw) + fclose (dmp -> fp_raw); + if (dmp -> fp_iso) + fclose (dmp -> fp_iso); + if (out) { + + + } + } + + return (out); +} + + +dumper *dumper_new (disc *d) { + dumper *dmp; + + dmp = (dumper *) malloc (sizeof (dumper)); + memset (dmp, 0, sizeof (dumper)); + dmp -> dsk = d; + dumper_set_hashing (dmp, true); + dumper_set_flushing (dmp, true); + + return (dmp); +} + + +void dumper_set_progress_callback (dumper *dmp, progress_func progress, void *progress_data) { + dmp -> progress = progress; + dmp -> progress_data = progress_data; + + return; +} + + +void dumper_set_hashing (dumper *dmp, bool h) { + dmp -> hashing = h; + debug ("Hashing %s", h ? "enabled" : "disabled"); + + return; +} + + +void dumper_set_flushing (dumper *dmp, bool f) { + dmp -> flushing = f; + debug ("Flushing %s", f ? "enabled" : "disabled"); + + return; +} + +void *dumper_destroy (dumper *dmp) { + my_free (dmp -> outfile_raw); + my_free (dmp -> outfile_iso); + my_free (dmp); + + return (NULL); +} + + +char *dumper_get_iso_crc32 (dumper *dmp) { + return ((dmp -> hash_iso).crc32_s); +} + + +char *dumper_get_raw_crc32 (dumper *dmp) { + return ((dmp -> hash_raw).crc32_s); +} + + +char *dumper_get_iso_md4 (dumper *dmp) { + return ((dmp -> hash_iso).md4_s); +} + + +char *dumper_get_raw_md4 (dumper *dmp) { + return ((dmp -> hash_raw).md4_s); +} + + +char *dumper_get_iso_md5 (dumper *dmp) { + return ((dmp -> hash_iso).md5_s); +} + + +char *dumper_get_raw_md5 (dumper *dmp) { + return ((dmp -> hash_raw).md5_s); +} + + +char *dumper_get_iso_ed2k (dumper *dmp) { + return ((dmp -> hash_iso).ed2k_s); +} + + +char *dumper_get_raw_ed2k (dumper *dmp) { + return ((dmp -> hash_raw).ed2k_s); +} + + +char *dumper_get_iso_sha1 (dumper *dmp) { + return ((dmp -> hash_iso).sha1_s); +} + + +char *dumper_get_raw_sha1 (dumper *dmp) { + return ((dmp -> hash_raw).sha1_s); +} diff --git a/libfriidump/dumper.h b/libfriidump/dumper.h new file mode 100644 index 0000000..7c9bd7b --- /dev/null +++ b/libfriidump/dumper.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://www.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef DUMPER_H_INCLUDED +#define DUMPER_H_INCLUDED + +#include "misc.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dumper_s dumper; + +typedef void (*progress_func) (bool start, u_int32_t current_sector, u_int32_t total_sectors, void *progress_data); + +FRIIDUMPLIB_EXPORT bool dumper_set_raw_output_file (dumper *dmp, char *outfile_raw, bool resume); +FRIIDUMPLIB_EXPORT bool dumper_set_iso_output_file (dumper *dmp, char *outfile_iso, bool resume); +FRIIDUMPLIB_EXPORT bool dumper_prepare (dumper *dmp); +FRIIDUMPLIB_EXPORT int dumper_dump (dumper *dmp, u_int32_t *current_sector); +FRIIDUMPLIB_EXPORT dumper *dumper_new (disc *d); +FRIIDUMPLIB_EXPORT void dumper_set_progress_callback (dumper *dmp, progress_func progress, void *progress_data); +FRIIDUMPLIB_EXPORT void dumper_set_hashing (dumper *dmp, bool h); +FRIIDUMPLIB_EXPORT void dumper_set_flushing (dumper *dmp, bool f); +FRIIDUMPLIB_EXPORT void *dumper_destroy (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_iso_crc32 (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_raw_crc32 (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_iso_md4 (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_raw_md4 (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_iso_md5 (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_raw_md5 (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_iso_ed2k (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_raw_ed2k (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_iso_sha1 (dumper *dmp); +FRIIDUMPLIB_EXPORT char *dumper_get_raw_sha1 (dumper *dmp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libfriidump/dvd_drive.c b/libfriidump/dvd_drive.c new file mode 100644 index 0000000..6a4f8fc --- /dev/null +++ b/libfriidump/dvd_drive.c @@ -0,0 +1,755 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/*! \file + * \brief A class to send raw MMC commands to a CD/DVD-ROM drive. + * + * This class can be used to send raw MMC commands to a CD/DVD-ROM drive. It uses own structures and data types to represent the commands, which are + * then transformed in the proper OS-dependent structures when the command is executed, achieving portability. Currently Linux and Windows are supported, but + * all that is needed to add support to a new OS is a proper dvd_execute_cmd() function, so it should be very easy. I hope that someone can add + * compatibility with MacOS X and *BSD: libcdio is a good place to understand how it should be done :). Actally, we could have used libcdio right from the start, + * but I didn't want to add a dependency on a library that cannot be easily found in binary format for all the target OS's. + * + * This file contains code derived from the work of Kevin East (SeventhSon), kev@kev.nu, http://www.kev.nu/360/ , which, in turn, derives from work by + * a lot of other people. See his page for full details. + */ + +#include "rs.h" +#include "misc.h" +#include +#include +//#include +#include +#include +#include +#include "dvd_drive.h" +#include "disc.h" + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#endif + + +/*! \brief Timeout for MMC commands. + * + * This must be expressed in seconds (Windows uses seconds, right?). + */ +#define MMC_CMD_TIMEOUT 10 + + +/* Imported drive-specific functions */ +int vanilla_2064_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf); +int vanilla_2384_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf); +int hitachi_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf); +int liteon_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf); +int renesas_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf); + + +/*! \brief A structure that represents a CD/DVD-ROM drive. + */ +struct dvd_drive_s { + /* Device special file */ + char *device; //!< The path to the drive (i.e.: /dev/something on Unix, x: on Windows). + + /* Data about the drive */ + char *vendor; //!< The drive vendor. + char *prod_id; //!< The drive product ID. + char *prod_rev; //!< The drive product revision (Usually firmware version). + char *model_string; //!< The above three strings, joined in a single one. + u_int32_t def_method; + u_int32_t command; + + /* Device-dependent internal memory dump function */ + /*! The intended area should start where sector data is stored upon a READ command. Here we assume that sectors are + * stored one after the other, as heuristics showed it is the case for the Hitachi MN103-based drives, but this model + * might be changed in the future, if we get support for other drives. + */ + dvd_drive_memdump_func memdump; //!< A pointer to a function that is able to dump the drive's internal memory area. + bool supported; //!< True if the drive is a supported model, false otherwise. + + + /* File descriptor & stuff used to access drive */ +#ifdef WIN32 + HANDLE fd; //!< The HANDLE to interact with the drive on Windows. +#else + int fd; //!< The file descriptor to interact with the drive on Unix. +#endif +}; + + +/** \brief Supported MMC commands. + */ +enum mmc_commands_e { + SPC_INQUIRY = 0x12, + MMC_READ_12 = 0xA8, +}; + + +/** + * Initializes a structure representing an MMC command. + * @param mmc A pointer to the MMC command structure. + * @param buf The buffer where results of the MMC command execution provided by the drive should be stored, or NULL if no buffer will be provided. + * @param len The length of the buffer (ignored in case buf is NULL). + * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed, or NULL. + */ +void dvd_init_command (mmc_command *mmc, u_int8_t *buf, int len, req_sense *sense) { + memset (mmc, 0, sizeof (mmc_command)); + if (buf) + memset (buf, 0, len); + mmc -> buffer = buf; + mmc -> buflen = buf ? len : 0; + mmc -> sense = sense; + + return; +} + + +#ifdef WIN32 + +/* Doc is under the UNIX function */ +int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) { + SCSI_PASS_THROUGH_DIRECT *sptd; + unsigned char sptd_sense[sizeof (*sptd) + 18], *sense; + DWORD bytes; + int out; + + sptd = (SCSI_PASS_THROUGH_DIRECT *) sptd_sense; + sense = &sptd_sense[sizeof (*sptd)]; + + memset (sptd, 0, sizeof (sptd_sense)); + memcpy (sptd -> Cdb, mmc -> cmd, sizeof (mmc -> cmd)); + sptd -> Length = sizeof (SCSI_PASS_THROUGH); + sptd -> CdbLength = 12; + sptd -> SenseInfoLength = 18; + sptd -> DataIn = SCSI_IOCTL_DATA_IN; + sptd -> DataBuffer = mmc -> buffer; + // Quick hack: Windows hates sptd->DataTransferLength = 1, so we set it to 2 and ignore the second byte. + if (mmc -> buflen == 1) // TODO + sptd -> DataTransferLength = 2; + else + sptd -> DataTransferLength = mmc -> buflen; + sptd -> TimeOutValue = MMC_CMD_TIMEOUT; + sptd -> SenseInfoOffset = sizeof (*sptd); + + //fprintf (stdout,"mmc->cmd[00] = %d \n",mmc->cmd[00]); + //Set streaming hack + if (mmc->cmd[00]==0xB6) { + sptd -> DataIn = SCSI_IOCTL_DATA_OUT; + sptd -> DataTransferLength = 28; + } + + if (!DeviceIoControl (dvd -> fd, IOCTL_SCSI_PASS_THROUGH_DIRECT, sptd, sizeof (*sptd) + 18, sptd, sizeof (*sptd) + 18, &bytes, NULL) && !ignore_errors) { + out = -1; /* Failure */ + error ("Execution of MMC command failed: %s", strerror (errno)); +// error ("DeviceIOControl() failed with %d\n", GetLastError()); + debug ("Command was: "); + hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd)); + debug ("Sense data: %02X/%02X/%02X\n", sense[2] & 0x0F, sense[12], sense[13]); + } else { + out = 0; + } + + if (mmc -> sense) { + mmc -> sense -> sense_key = sense[2]; + mmc -> sense -> asc = sense[12]; + mmc -> sense -> ascq = sense[13]; + } + + return (out); +} + +#else + +/** + * Executes an MMC command. + * @param dvd The DVD drive the command should be exectued on. + * @param mmc The command to be executed. + * @param ignore_errors If set to true, no error will be printed if the command fails. + * @return 0 if the command was executed successfully, < 0 otherwise. + */ +int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) { + int out; + struct cdrom_generic_command cgc; + struct request_sense sense; + +#if 0 + debug ("Executing MMC command: "); + hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd)); +#endif + + /* Init Linux-format MMC command */ + memset (&cgc, 0, sizeof (struct cdrom_generic_command)); + memcpy (cgc.cmd, mmc -> cmd, sizeof (mmc -> cmd)); + cgc.buffer = (unsigned char *) mmc -> buffer; + cgc.buflen = mmc -> buflen; + cgc.data_direction = CGC_DATA_READ; + cgc.timeout = MMC_CMD_TIMEOUT * 1000; /* Linux uses milliseconds */ + cgc.sense = &sense; + if (ioctl (dvd -> fd, CDROM_SEND_PACKET, &cgc) < 0 && !ignore_errors) { + out = -1; /* Failure */ + error ("Execution of MMC command failed: %s", strerror (errno)); + debug ("Command was:"); + hex_and_ascii_print ("", cgc.cmd, sizeof (cgc.cmd)); + debug ("Sense data: %02X/%02X/%02X", sense.sense_key, sense.asc, sense.ascq); + } else { + out = 0; + } + + if (mmc -> sense) { + mmc -> sense -> sense_key = sense.sense_key; + mmc -> sense -> asc = sense.asc; + mmc -> sense -> ascq = sense.ascq; + } + + return (out); +} +#endif + + +/** + * Sends an INQUIRY command to the drive to retrieve drive identification strings. + * @param dvd The DVD drive the command should be exectued on. + * @return 0 if the command was executed successfully, < 0 otherwise. + */ +static int dvd_get_drive_info (dvd_drive *dvd) { + mmc_command mmc; + int out; + u_int8_t buf[36]; + char tmp[36 * 4]; + + dvd_init_command (&mmc, buf, sizeof (buf), NULL); + mmc.cmd[0] = SPC_INQUIRY; + mmc.cmd[4] = sizeof (buf); + if ((out = dvd_execute_cmd (dvd, &mmc, false)) >= 0) { + my_strndup (dvd -> vendor, buf + 8, 8); + strtrimr (dvd -> vendor); + my_strndup (dvd -> prod_id, buf + 16, 16); + strtrimr (dvd -> prod_id); + my_strndup (dvd -> prod_rev, buf + 32, 4); + strtrimr (dvd -> prod_rev); + snprintf (tmp, sizeof (tmp), "%s/%s/%s", dvd -> vendor, dvd -> prod_id, dvd -> prod_rev); + my_strdup (dvd -> model_string, tmp); + + debug ("DVD drive is \"%s\"", dvd -> model_string); + } else { + error ("Cannot identify DVD drive\n"); + } + + return (out); +} + + +/** + * Assigns the proper memory dump functions to a dvd_drive object, according to vendor, model and other parameters. Actually this scheme probably needs to + * to be improved, but it is enough for the moment. + * @param dvd The DVD drive the command should be exectued on. + */ +static void dvd_assign_functions (dvd_drive *dvd, u_int32_t command) { + dvd -> def_method = 0; + if (strcmp (dvd -> vendor, "HL-DT-ST") == 0 && ( +// strcmp (dvd -> prod_id, "DVDRAM GSA-T10N") == 0 || + strcmp (dvd -> prod_id, "DVD-ROM GDR8082N") == 0 || + strcmp (dvd -> prod_id, "DVD-ROM GDR8161B") == 0 || + strcmp (dvd -> prod_id, "DVD-ROM GDR8162B") == 0 || + strcmp (dvd -> prod_id, "DVD-ROM GDR8163B") == 0 || + strcmp (dvd -> prod_id, "DVD-ROM GDR8164B") == 0 + )) { + debug ("Hitachi MN103-based DVD drive detected, using Hitachi memory dump command"); + dvd -> memdump = &hitachi_dvd_dump_mem; + dvd -> command = 2; + dvd -> supported = true; + dvd -> def_method = 9; + + } else if (strcmp (dvd -> vendor, "LITE-ON") == 0 && ( + strcmp (dvd ->prod_id, "DVDRW LH-18A1H") == 0 || + strcmp (dvd ->prod_id, "DVDRW LH-18A1P") == 0 || + strcmp (dvd ->prod_id, "DVDRW LH-20A1H") == 0 || + strcmp (dvd ->prod_id, "DVDRW LH-20A1P") == 0 + )) { + debug ("Lite-On DVD drive detected, using Lite-On memory dump command"); + dvd -> memdump = &liteon_dvd_dump_mem; + dvd -> command = 3; + dvd -> supported = true; + dvd -> def_method = 5; + + } else if (strcmp (dvd -> vendor, "TSSTcorp") == 0 && ( + strcmp (dvd ->prod_id, "DVD-ROM SH-D162A") == 0 || + strcmp (dvd ->prod_id, "DVD-ROM SH-D162B") == 0 || + strcmp (dvd ->prod_id, "DVD-ROM SH-D162C") == 0 || + strcmp (dvd ->prod_id, "DVD-ROM SH-D162D") == 0 + )) { + debug ("Toshiba Samsung DVD drive detected, using vanilla 2384 memory dump command"); + dvd -> memdump = &vanilla_2384_dvd_dump_mem; + dvd -> command = 1; + dvd -> supported = true; + dvd -> def_method = 0; + + } else if (strcmp (dvd -> vendor, "PLEXTOR") == 0) { + debug ("Plextor DVD drive detected, using vanilla 2064 memory dump command"); + dvd -> memdump = &vanilla_2064_dvd_dump_mem; + dvd -> command = 0; + dvd -> supported = true; + dvd -> def_method = 2; + + } else { + /* This is an unsupported drive (yet). */ + dvd -> memdump = &vanilla_2064_dvd_dump_mem; + dvd -> command = 0; + dvd -> supported = false; + } + + if (command!=-1) { + dvd -> command = command; + if (command == 0) dvd -> memdump = &vanilla_2064_dvd_dump_mem; + else if (command == 1) dvd -> memdump = &vanilla_2384_dvd_dump_mem; + else if (command == 2) dvd -> memdump = &hitachi_dvd_dump_mem; + else if (command == 3) dvd -> memdump = &liteon_dvd_dump_mem; + else if (command == 4) dvd -> memdump = &renesas_dvd_dump_mem; + } + + //init Reed-Solomon for Lite-On + generate_gf(); + gen_poly(); + + return; +} + + +/** + * Creates a new structure representing a CD/DVD-ROM drive. + * @param device The CD/DVD-ROM device, in OS-dependent format (i.e.: /dev/something on Unix, x: on Windows). + * @return The newly-created structure, to be used with the other commands, or NULL if the drive could not be initialized. + */ +dvd_drive *dvd_drive_new (char *device, u_int32_t command) { + dvd_drive *dvd; +#ifdef WIN32 + HANDLE fd; + char dev[40]; +#else + int fd; +#endif + + /* Force the dropping of privileges: in our model, privileges are only used to execute memory dump commands, the user + must gain access to the device somehow else (i. e. get added to the "cdrom" group or similar things) */ + drop_euid (); + + debug ("Trying to open DVD device %s", device); +#ifdef WIN32 + sprintf (dev, "\\\\.\\%c:", device[0]); + if ((fd = CreateFile (dev, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { + error ("Cannot open drive: %d", GetLastError ()); +#else + if ((fd = open (device, O_RDONLY | O_NONBLOCK)) < 0) { + perror ("Cannot open drive"); +#endif + dvd = NULL; + } else { + debug ("Opened successfully"); + drop_euid (); + dvd = (dvd_drive *) malloc (sizeof (dvd_drive)); + my_strdup (dvd -> device, device); + dvd -> fd = fd; + dvd_get_drive_info (dvd); + dvd_assign_functions (dvd, command); + } + + return (dvd); +} + + +/** + * Frees resources used by a DVD drive structure and destroys it. + * @param dvd The DVD drive structure to be destroyed. + * @return NULL. + */ +void *dvd_drive_destroy (dvd_drive *dvd) { + if (dvd) { +#ifdef WIN32 + CloseHandle (dvd -> fd); +#else + close (dvd -> fd); +#endif + my_free (dvd -> device); + my_free (dvd -> vendor); + my_free (dvd -> prod_id); + my_free (dvd -> prod_rev); + my_free (dvd); + } + + return (NULL); +} + + +/** + * Executes the drive-dependent function to dump the drive sector cache, and returns the dumped data. + * @param dvd The DVD drive the command should be exectued on. + * @param block_off The offset to start dumping, WRT the beginning of the sector cache. + * @param block_len The number of blocks to dump. + * @param block_size The block size to be used for dumping. + * @param buf A buffer where to store the dumped data. Note that this must be able to hold at least block_len * block_size bytes. + * @return 0 if the command was executed successfully, < 0 otherwise. + */ +int dvd_memdump (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) { + int out; + + /* Upgrade privileges and call actual dump functions */ + upgrade_euid (); + out = dvd -> memdump (dvd, block_off, block_len, block_size, buf); + drop_euid (); + + return (out); +} + + +/** + * Issues a READ(12) command without bothering to return the results. Uses the FUA (Force Unit Access bit) so that the requested sectors are actually read + * at the beginning of the cache and can be dumped later. + * @param dvd The DVD drive the command should be exectued on. + * @param sector The sector to be read. What will be cached is the 16-sectors block to which the sector belongs. + * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed. + * @return 0 if the command was executed successfully, < 0 otherwise. + */ +int dvd_read_sector_dummy (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) { + mmc_command mmc; + int out; + u_int8_t intbuf[64 * 1024], *buf; + size_t bufsize; + + /* We need some buffer, be it provided externally or not */ + if (extbuf) { + buf = extbuf; + bufsize = extbufsize; + } else { + buf = intbuf; + bufsize = sizeof (intbuf); + } + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[0] = MMC_READ_12; + mmc.cmd[1] = 0x08; /* FUA bit set */ + mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */ + mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16); + mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8); + mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF); + mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */ + mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16); + mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8); + mmc.cmd[9] = (u_int8_t) (sectors & 0x000000FF); + out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */ + + return (out); +} + + +/** + * Issues a READ(12) command using the STREAMING bit, which causes the requested 16-sector block to be read into memory, + * together with the following four. This way we will be able to dump 5 sector with a single READ request. + * + * Note the strange need for a big buffer even though we must only pass 0x10 as the transfer length, otherwise the drive will hang (!?). + * @param dvd The DVD drive the command should be exectued on. + * @param sector The sector to be read. What will be cached is the 16-sectors block to which the sector belongs, and the following 4 blocks. + * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed. + * @param extbuf A buffer where to store the read data, or NULL. + * @param extbufsize The size of the buffer. + * @return + */ +int dvd_read_sector_streaming (dvd_drive *dvd, u_int32_t sector, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) { + mmc_command mmc; + int out; + u_int8_t intbuf[2048 * 16], *buf; + size_t bufsize; + + /* We need some buffer, be it provided externally or not */ + if (extbuf) { + buf = extbuf; + bufsize = extbufsize; + } else { + buf = intbuf; + bufsize = sizeof (intbuf); + } + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[0] = MMC_READ_12; + mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */ + mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16); + mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8); + mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF); + mmc.cmd[6] = 0; + mmc.cmd[7] = 0; + mmc.cmd[8] = 0; + mmc.cmd[9] = 0x10; + mmc.cmd[10] = 0x80; /* STREAMING bit set */ + out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */ + + return (out); +} + + +int dvd_read_streaming (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) { + mmc_command mmc; + int out; + u_int8_t intbuf[64 * 1024], *buf; + size_t bufsize; + + /* We need some buffer, be it provided externally or not */ + if (extbuf) { + buf = extbuf; + bufsize = extbufsize; + } else { + buf = intbuf; + bufsize = sizeof (intbuf); + } + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[0] = MMC_READ_12; + mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */ + mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16); + mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8); + mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF); + mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */ + mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16); + mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8); + mmc.cmd[9] = (u_int8_t) (sectors & 0x000000FF); + mmc.cmd[10] = 0x80; /* STREAMING bit set */ + out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */ + + return (out); +} + + +int dvd_flush_cache_READ12 (dvd_drive *dvd, u_int32_t sector, req_sense *sense) { + mmc_command mmc; + int out; + u_int8_t intbuf[64], *buf; + size_t bufsize; + + buf = intbuf; + bufsize = 0; + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[0] = MMC_READ_12; + mmc.cmd[1] = 0x08; + mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */ + mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16); + mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8); + mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF); + out = dvd_execute_cmd (dvd, &mmc, true); + + return (out); +} + +int dvd_stop_unit (dvd_drive *dvd, bool start, req_sense *sense) { + mmc_command mmc; + int out; + u_int8_t intbuf[64], *buf; + size_t bufsize; + + buf = intbuf; + bufsize = 0; + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[0] = 0x1B; + if (start) mmc.cmd[4] = 1; + else mmc.cmd[4] = 0; + out = dvd_execute_cmd (dvd, &mmc, true); + + return (out); +} + +int dvd_set_speed (dvd_drive *dvd, u_int32_t speed, req_sense *sense) { + mmc_command mmc; + int out; + u_int8_t intbuf[64], *buf; + size_t bufsize; + + buf = intbuf; + bufsize = 0; + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[0] = 0xBB; + mmc.cmd[2] = (u_int8_t) ((speed & 0x0000FF00) >> 8); + mmc.cmd[3] = (u_int8_t) (speed & 0x000000FF); + out = dvd_execute_cmd (dvd, &mmc, true); + + return (out); +} + +int dvd_get_size (dvd_drive *dvd, u_int32_t *size, req_sense *sense) { + mmc_command mmc; + int out; + u_int8_t intbuf[64], *buf; + size_t bufsize; + + buf = intbuf; + bufsize = 0x22; + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[0] = 0x52; + mmc.cmd[1] = 0x01; + mmc.cmd[5] = 0x01; + mmc.cmd[8] = 0x22; + out = dvd_execute_cmd (dvd, &mmc, true); + + *(size)=*(size) << 8 | intbuf[0x18]; + *(size)=*(size) << 8 | intbuf[0x19]; + *(size)=*(size) << 8 | intbuf[0x1a]; + *(size)=*(size) << 8 | intbuf[0x1b]; + + return (out); +} + +int dvd_get_layerbreak (dvd_drive *dvd, u_int32_t *layerbreak, req_sense *sense) { + mmc_command mmc; + int out; + u_int8_t intbuf[2052], *buf; + size_t bufsize; + + buf = intbuf; + bufsize = 2052; + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[0] = 0xad; + mmc.cmd[8] = 0x08; + mmc.cmd[9] = 0x04; + out = dvd_execute_cmd (dvd, &mmc, true); + + *(layerbreak)=*(layerbreak) << 8; + *(layerbreak)=*(layerbreak) << 8 | intbuf[0x11]; + *(layerbreak)=*(layerbreak) << 8 | intbuf[0x12]; + *(layerbreak)=*(layerbreak) << 8 | intbuf[0x13]; + if (*(layerbreak) > 0) *(layerbreak)=*(layerbreak) - 0x30000 + 1; + + return (out); +} + +int dvd_set_streaming (dvd_drive *dvd, u_int32_t speed, req_sense *sense) { +/* +DVD Decrypter-> +DeviceIoControl : \Device\CdRom5 +Command : IOCTL_SCSI_PASS_THROUGH_DIRECT +Length : 44 (0x002C) +ScsiStatus : 0 +PathId : 0 +TargedId : 0 +Lun : 0 +CdbLength : 12 (0x0C) +SenseInfoLength : 24 (0x18) +DataTransferLength : 28 (0x0000001C) +DataIn : 0 +TimeOutValue : 5000 + +CDB: +00000000 B6 00 00 00 00 00 00 00 00 00 1C 00 ķ........... + +Data Sent: +00000000 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF ............____ +00000010 00 00 03 E8 FF FF FF FF 00 00 03 E8 ...č____...č +*/ + mmc_command mmc; + int out; + u_int8_t inbuf[28], *buf; + size_t bufsize; + + buf = inbuf; + bufsize = 28; + + dvd_init_command (&mmc, buf, bufsize, sense); + mmc.cmd[00] = 0xB6; + mmc.cmd[10] = 28; + + *(buf+ 0)=0;//2 + *(buf+ 1)=0; + *(buf+ 2)=0; + *(buf+ 3)=0; + *(buf+ 4)=0; //MSB + *(buf+ 5)=0; // + *(buf+ 6)=0; // + *(buf+ 7)=0; //LSB + + *(buf+ 8)=0xff; //MSB + *(buf+ 9)=0xff; // + *(buf+10)=0xff; // + *(buf+11)=0xff; //LSB + + *(buf+12)=(u_int8_t) ((speed & 0xFF000000) >> 24); + *(buf+13)=(u_int8_t) ((speed & 0x00FF0000) >> 16); + *(buf+14)=(u_int8_t) ((speed & 0x0000FF00) >> 8); + *(buf+15)=(u_int8_t) (speed & 0x000000FF); + + *(buf+16)=(u_int8_t) ((1000 & 0xFF000000) >> 24); + *(buf+17)=(u_int8_t) ((1000 & 0x00FF0000) >> 16); + *(buf+18)=(u_int8_t) ((1000 & 0x0000FF00) >> 8); + *(buf+19)=(u_int8_t) (1000 & 0x000000FF); + + *(buf+20)=(u_int8_t) ((speed & 0xFF000000) >> 24); + *(buf+21)=(u_int8_t) ((speed & 0x00FF0000) >> 16); + *(buf+22)=(u_int8_t) ((speed & 0x0000FF00) >> 8); + *(buf+23)=(u_int8_t) (speed & 0x000000FF); + + *(buf+24)=(u_int8_t) ((1000 & 0xFF000000) >> 24); + *(buf+25)=(u_int8_t) ((1000 & 0x00FF0000) >> 16); + *(buf+26)=(u_int8_t) ((1000 & 0x0000FF00) >> 8); + *(buf+27)=(u_int8_t) (1000 & 0x000000FF); + + out = dvd_execute_cmd (dvd, &mmc, true); + + return (out); +} + + +char *dvd_get_vendor (dvd_drive *dvd) { + return (dvd -> vendor); +} + + +char *dvd_get_product_id (dvd_drive *dvd) { + return (dvd -> prod_id); +} + + +char *dvd_get_product_revision (dvd_drive *dvd) { + return (dvd -> prod_rev); +} + + +char *dvd_get_model_string (dvd_drive *dvd) { + return (dvd -> model_string); +} + + +bool dvd_get_support_status (dvd_drive *dvd) { + return (dvd -> supported); +} + +u_int32_t dvd_get_def_method (dvd_drive *dvd){ + return (dvd -> def_method); +} + +u_int32_t dvd_get_command (dvd_drive *dvd){ + return (dvd -> command); +} \ No newline at end of file diff --git a/libfriidump/dvd_drive.h b/libfriidump/dvd_drive.h new file mode 100644 index 0000000..e42dd89 --- /dev/null +++ b/libfriidump/dvd_drive.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef DVD_DRIVE_H_INCLUDED +#define DVD_DRIVE_H_INCLUDED + +#include "misc.h" +#include + + +typedef struct dvd_drive_s dvd_drive; + + +typedef struct { + int sense_key; + int asc; + int ascq; +} req_sense; + + +typedef struct { + u_int8_t cmd[12]; + req_sense *sense; + u_int8_t *buffer; + int buflen; +} mmc_command; + + +/* Functions */ +dvd_drive *dvd_drive_new (char *device, u_int32_t command); +void *dvd_drive_destroy (dvd_drive *d); +int dvd_read_sector_dummy (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize); +int dvd_read_sector_streaming (dvd_drive *dvd, u_int32_t sector, req_sense *sense, u_int8_t *extbuf, size_t extbufsize); +int dvd_read_streaming (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize); +int dvd_flush_cache_READ12 (dvd_drive *dvd, u_int32_t sector, req_sense *sense); +int dvd_stop_unit (dvd_drive *dvd, bool start, req_sense *sense); +int dvd_set_speed (dvd_drive *dvd, u_int32_t speed, req_sense *sense); +int dvd_get_size (dvd_drive *dvd, u_int32_t *size, req_sense *sense); +int dvd_get_layerbreak (dvd_drive *dvd, u_int32_t *layerbreak, req_sense *sense); +int dvd_set_streaming (dvd_drive *dvd, u_int32_t speed, req_sense *sense); +int dvd_memdump (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf); +char *dvd_get_vendor (dvd_drive *dvd); +char *dvd_get_product_id (dvd_drive *dvd); +char *dvd_get_product_revision (dvd_drive *dvd); +char *dvd_get_model_string (dvd_drive *dvd); +bool dvd_get_support_status (dvd_drive *dvd); +u_int32_t dvd_get_def_method (dvd_drive *dvd); +u_int32_t dvd_get_command (dvd_drive *dvd); + +/* The following are exported for use by drive-specific functions */ +typedef int (*dvd_drive_memdump_func) (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf); +void dvd_init_command (mmc_command *mmc, u_int8_t *buf, int len, req_sense *sense); +int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors); + +#endif diff --git a/libfriidump/ecma-267.c b/libfriidump/ecma-267.c new file mode 100644 index 0000000..46c3d9a --- /dev/null +++ b/libfriidump/ecma-267.c @@ -0,0 +1,109 @@ +/* +unscrambler 0.4: unscramble not standard IVs scrambled DVDs thru +bruteforce, intended for Gamecube/WII Optical Disks. + +Copyright (C) 2006 Victor Muņoz (xt5@ingenieria-inversa.cl) + +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. +*/ + +#include "ecma-267.h" + +/* EDC stuff */ +unsigned int edc_table[256] = { + 0x00000000, 0x80000011, 0x80000033, 0x00000022, 0x80000077, 0x00000066, 0x00000044, 0x80000055, + 0x800000FF, 0x000000EE, 0x000000CC, 0x800000DD, 0x00000088, 0x80000099, 0x800000BB, 0x000000AA, + 0x800001EF, 0x000001FE, 0x000001DC, 0x800001CD, 0x00000198, 0x80000189, 0x800001AB, 0x000001BA, + 0x00000110, 0x80000101, 0x80000123, 0x00000132, 0x80000167, 0x00000176, 0x00000154, 0x80000145, + 0x800003CF, 0x000003DE, 0x000003FC, 0x800003ED, 0x000003B8, 0x800003A9, 0x8000038B, 0x0000039A, + 0x00000330, 0x80000321, 0x80000303, 0x00000312, 0x80000347, 0x00000356, 0x00000374, 0x80000365, + 0x00000220, 0x80000231, 0x80000213, 0x00000202, 0x80000257, 0x00000246, 0x00000264, 0x80000275, + 0x800002DF, 0x000002CE, 0x000002EC, 0x800002FD, 0x000002A8, 0x800002B9, 0x8000029B, 0x0000028A, + 0x8000078F, 0x0000079E, 0x000007BC, 0x800007AD, 0x000007F8, 0x800007E9, 0x800007CB, 0x000007DA, + 0x00000770, 0x80000761, 0x80000743, 0x00000752, 0x80000707, 0x00000716, 0x00000734, 0x80000725, + 0x00000660, 0x80000671, 0x80000653, 0x00000642, 0x80000617, 0x00000606, 0x00000624, 0x80000635, + 0x8000069F, 0x0000068E, 0x000006AC, 0x800006BD, 0x000006E8, 0x800006F9, 0x800006DB, 0x000006CA, + 0x00000440, 0x80000451, 0x80000473, 0x00000462, 0x80000437, 0x00000426, 0x00000404, 0x80000415, + 0x800004BF, 0x000004AE, 0x0000048C, 0x8000049D, 0x000004C8, 0x800004D9, 0x800004FB, 0x000004EA, + 0x800005AF, 0x000005BE, 0x0000059C, 0x8000058D, 0x000005D8, 0x800005C9, 0x800005EB, 0x000005FA, + 0x00000550, 0x80000541, 0x80000563, 0x00000572, 0x80000527, 0x00000536, 0x00000514, 0x80000505, + 0x80000F0F, 0x00000F1E, 0x00000F3C, 0x80000F2D, 0x00000F78, 0x80000F69, 0x80000F4B, 0x00000F5A, + 0x00000FF0, 0x80000FE1, 0x80000FC3, 0x00000FD2, 0x80000F87, 0x00000F96, 0x00000FB4, 0x80000FA5, + 0x00000EE0, 0x80000EF1, 0x80000ED3, 0x00000EC2, 0x80000E97, 0x00000E86, 0x00000EA4, 0x80000EB5, + 0x80000E1F, 0x00000E0E, 0x00000E2C, 0x80000E3D, 0x00000E68, 0x80000E79, 0x80000E5B, 0x00000E4A, + 0x00000CC0, 0x80000CD1, 0x80000CF3, 0x00000CE2, 0x80000CB7, 0x00000CA6, 0x00000C84, 0x80000C95, + 0x80000C3F, 0x00000C2E, 0x00000C0C, 0x80000C1D, 0x00000C48, 0x80000C59, 0x80000C7B, 0x00000C6A, + 0x80000D2F, 0x00000D3E, 0x00000D1C, 0x80000D0D, 0x00000D58, 0x80000D49, 0x80000D6B, 0x00000D7A, + 0x00000DD0, 0x80000DC1, 0x80000DE3, 0x00000DF2, 0x80000DA7, 0x00000DB6, 0x00000D94, 0x80000D85, + 0x00000880, 0x80000891, 0x800008B3, 0x000008A2, 0x800008F7, 0x000008E6, 0x000008C4, 0x800008D5, + 0x8000087F, 0x0000086E, 0x0000084C, 0x8000085D, 0x00000808, 0x80000819, 0x8000083B, 0x0000082A, + 0x8000096F, 0x0000097E, 0x0000095C, 0x8000094D, 0x00000918, 0x80000909, 0x8000092B, 0x0000093A, + 0x00000990, 0x80000981, 0x800009A3, 0x000009B2, 0x800009E7, 0x000009F6, 0x000009D4, 0x800009C5, + 0x80000B4F, 0x00000B5E, 0x00000B7C, 0x80000B6D, 0x00000B38, 0x80000B29, 0x80000B0B, 0x00000B1A, + 0x00000BB0, 0x80000BA1, 0x80000B83, 0x00000B92, 0x80000BC7, 0x00000BD6, 0x00000BF4, 0x80000BE5, + 0x00000AA0, 0x80000AB1, 0x80000A93, 0x00000A82, 0x80000AD7, 0x00000AC6, 0x00000AE4, 0x80000AF5, + 0x80000A5F, 0x00000A4E, 0x00000A6C, 0x80000A7D, 0x00000A28, 0x80000A39, 0x80000A1B, 0x00000A0AL +}; + +u32 edc_calc(u32 edc, u8 *ptr, u32 len) { + while (len--) edc=edc_table[((edc>>24)^*ptr++)&0xFF]^(edc<<8); + return edc; +} + +/* end of EDC stuff */ + +/* LFSR stuff */ + +u16 ecma267_ivs[]= { + 0x0001, 0x5500, 0x0002, 0x2A00, + 0x0004, 0x5400, 0x0008, 0x2800, + 0x0010, 0x5000, 0x0020, 0x2001, + 0x0040, 0x4002, 0x0080, 0x0005 +}; + + +unsigned short LFSR; + +void LFSR_ecma_init(int iv) { + LFSR=ecma267_ivs[iv]; +} + +void LFSR_init(u16 seed) { + LFSR=seed; +} + +int LFSR_tick() { + int ret; + int n; + + ret=LFSR>>14; + + n=ret^((LFSR>>10)&1); + LFSR=((LFSR<<1)|n)&0x7FFF; + + return ret; +} + +unsigned char LFSR_byte() { + u8 ret; + int i; + + ret=0; + for(i=0; i<8; i++) ret=(ret<<1)|LFSR_tick(); + + return ret; +} + +/* end of LFSR stuff */ diff --git a/libfriidump/ecma-267.h b/libfriidump/ecma-267.h new file mode 100644 index 0000000..a3eefb1 --- /dev/null +++ b/libfriidump/ecma-267.h @@ -0,0 +1,47 @@ +/* +unscrambler 0.4: unscramble not standard IVs scrambled DVDs thru +bruteforce, intended for Gamecube/WII Optical Disks. + +Copyright (C) 2006 Victor Muņoz (xt5@ingenieria-inversa.cl) + +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. +*/ + +typedef unsigned int u32; +typedef int s32; + +typedef unsigned short u16; +typedef short s16; + +typedef unsigned char u8; +typedef char s8; + +/* EDC stuff */ + +u32 edc_calc(u32 edc, u8 *ptr, u32 len); + +/* end of EDC stuff */ + +/* LFSR stuff */ + +void LFSR_ecma_init(int iv); + +void LFSR_init(u16 seed); + +int LFSR_tick(); + +u8 LFSR_byte(); + +/* end of LFSR stuff */ diff --git a/libfriidump/hitachi.c b/libfriidump/hitachi.c new file mode 100644 index 0000000..b0f9dbb --- /dev/null +++ b/libfriidump/hitachi.c @@ -0,0 +1,147 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/*! \file + * \brief Memory dump functions specific to drives based on the Hitachi MN103 chip. + * + * Drives which are supported by this set of functions are, for instance, the LG GDR-8161B, GDR-8162B, GDR-8163B and GDR-8164B. All testing has been performed + * with the latter model, so I'm not really sure about the others, but they should work ;). Please report any issues and more compatible drives! + * + * This file contains code derived from the work of Kevin East (SeventhSon), kev@kev.nu, http://www.kev.nu/360/ , which, in turn, derives from work by + * a lot of other people. See his page for full details. + */ + +#include +#include +#include "misc.h" +#include "dvd_drive.h" + +/*! \brief Memory offset at which the drive cache memory is mapped. + * + * During READ, sector data is cached beginning at this address. + */ + +#define HITACHI_MEM_BASE 0x80000000 + + +/** + * Dumps a single block (with arbitrary size) of the address space of the MN103 microcontroller within the Hitachi-LG Xbox 360 DVD drive and similar drives. + * This function is derived from the work of Kevin East (SeventhSon), kev@kev.nu, http://www.kev.nu/360/. + * @param dvd The DVD drive the command should be exectued on. + * @param offset The absolute memory offset to start dumping. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int hitachi_dvd_dump_memblock (dvd_drive *dvd, u_int32_t offset, u_int32_t block_size, u_int8_t *buf) { + mmc_command mmc; + int out; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else if (!block_size || block_size > 65535) { + error ("invalid block_size (valid: 1 - 65535)"); + out = -2; + } else { + dvd_init_command (&mmc, buf, block_size, NULL); + mmc.cmd[0] = 0xE7; // vendor specific command (discovered by DaveX) + mmc.cmd[1] = 0x48; // H + mmc.cmd[2] = 0x49; // I + mmc.cmd[3] = 0x54; // T + mmc.cmd[4] = 0x01; // read MCU memory sub-command + mmc.cmd[6] = (unsigned char) ((offset & 0xFF000000) >> 24); // address MSB + mmc.cmd[7] = (unsigned char) ((offset & 0x00FF0000) >> 16); // address + mmc.cmd[8] = (unsigned char) ((offset & 0x0000FF00) >> 8); // address + mmc.cmd[9] = (unsigned char) (offset & 0x000000FF); // address LSB + mmc.cmd[10] = (unsigned char) ((block_size & 0xFF00) >> 8); // length MSB + mmc.cmd[11] = (unsigned char) (block_size & 0x00FF); // length LSB + + out = dvd_execute_cmd (dvd, &mmc, false); + } + + return (out); +} + + +/** + * Dumps a given portion of the address space of the MN103 microcontroller within the Hitachi-LG Xbox 360 DVD drive. + * WARNING: it can take a while to dump a lot of data. + * @param dvd The DVD drive the command should be exectued on. + * @param offset The absolute memory offset to start dumping. + * @param block_len The number of blocks to dump. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int hitachi_dvd_dump_mem_generic (dvd_drive *dvd, u_int32_t offset, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) { + u_int32_t i; + int r, out; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else { + r = -10; + for (i = 0; i < block_len; i++) { + if ((r = hitachi_dvd_dump_memblock (dvd, offset + i * block_size, block_size, buf + i * block_size)) < 0) { + error ("hitachi_dvd_read_block() failed with %d", r); + break; + } + } + out = r; + } + + return (out); +} + + +/** + * Dumps a given portion of the address space of the MN103 microcontroller within the Hitachi-LG Xbox 360 DVD drive, starting at the offset at which + * sector data are cached. + * WARNING: it can take a while to dump a lot of data. + * @param dvd The DVD drive the command should be exectued on. + * @param offset The memory offset to start dumping, relative to the cache start offset. + * @param block_len The number of blocks to dump. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int hitachi_dvd_dump_mem (dvd_drive *dvd, u_int32_t offset, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) { + u_int32_t i; + int r, out; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else { + r = -10; + for (i = 0; i < block_len; i++) { + if ((r = hitachi_dvd_dump_memblock (dvd, HITACHI_MEM_BASE + offset + i * block_size, block_size, buf + i * block_size)) < 0) { + error ("hitachi_dvd_read_block() failed with %d", r); + break; + } + } + out = r; + } + + return (out); +} diff --git a/libfriidump/lite-on.c b/libfriidump/lite-on.c new file mode 100644 index 0000000..6cf70fb --- /dev/null +++ b/libfriidump/lite-on.c @@ -0,0 +1,154 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "rs.h" +#include +#include +#include "misc.h" +#include "dvd_drive.h" + +/** + * Command found in Lite-On LH-18A1H; verified with LH-18A1P and LH-20A1H + * Expected to work with: DH*, DW*, LH*, SH* series + * Possibly would work with other Mediatek based drives: + * Samsung SE*, SH* series + * Some Dell drives + * Some Sony drives + * Asus DRW-1814* (DRW* series?) + */ + +//u_int8_t tmp[64*1024]; + +/** + * @param dvd The DVD drive the command should be exectued on. + * @param offset The absolute memory offset to start dumping. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int liteon_dvd_dump_memblock (dvd_drive *dvd, u_int32_t offset, u_int32_t block_size, u_int8_t *buf) { + mmc_command mmc; + int out; + u_int32_t raw_block_size; + u_int32_t raw_offset; + + u_int32_t src_offset; + u_int32_t dst_offset; + u_int32_t row_nr; + u_int32_t sec_nr; + u_int32_t sec_cnt; + u_int32_t first_sec_nr; + u_int8_t tmp[64*1024]; + //u_int8_t *tmp; + + raw_block_size = (block_size / 2064) * 0x950; + raw_offset = (offset / 2064) * 0x950; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else if (!block_size || raw_block_size >= 65535) { + error ("invalid raw_block_size (valid: 1 - 65534)"); + error ("raw_block_size = (block_size / 2064) * 2384"); + out = -2; + } else { + //tmp = malloc(64*1024); + dvd_init_command (&mmc, tmp, raw_block_size, NULL); //64*1024 + mmc.cmd[0] = 0x3C; // READ BUFFER + mmc.cmd[1] = 0x01; // Vendor specific - sole parameter supported by Lite-On + mmc.cmd[2] = 0x01; // == 0x02; 0xE2 = EEPROM; 0xF1 = KEYPARA; + mmc.cmd[3] = (unsigned char) ((raw_offset & 0x00FF0000) >> 16);// address MSB + mmc.cmd[4] = (unsigned char) ((raw_offset & 0x0000FF00) >> 8); // address + mmc.cmd[5] = (unsigned char) ( raw_offset & 0x000000FF); // address LSB + mmc.cmd[6] = (unsigned char) ((raw_block_size & 0x00FF0000) >> 16); // length MSB + mmc.cmd[7] = (unsigned char) ((raw_block_size & 0x0000FF00) >> 8); // length + mmc.cmd[8] = (unsigned char) ( raw_block_size & 0x000000FF); // length LSB + + out = dvd_execute_cmd (dvd, &mmc, false); + + src_offset = 0; + dst_offset = 0; + first_sec_nr = 0x55555555; + sec_cnt = 0; + while (src_offset < raw_block_size) { + sec_nr=(*(tmp+src_offset+1)<<16)+(*(tmp+src_offset+2)<<8)+(*(tmp+src_offset+3)); +//fprintf (stdout,"sec_nr: %x\n", sec_nr); + if (first_sec_nr==0x55555555) { + first_sec_nr=sec_nr; + rs_decode(tmp+src_offset, 0, 0); + } + else + if (sec_nr==first_sec_nr+sec_cnt) { + rs_decode(tmp+src_offset, 0, 0); //sector seq = ok + } + else { //sector seq broken -> corrupt + error ("sector sequence broken"); + out = -3; + *(tmp+src_offset+0)=0xff; + *(tmp+src_offset+1)=0xff; + *(tmp+src_offset+2)=0xff; + *(tmp+src_offset+3)=0xff; + break; + } + for (row_nr=0; row_nr<12; row_nr++) { + memcpy(buf+dst_offset, tmp+src_offset, 172); + dst_offset += 172; + src_offset += 182; + } + src_offset += 200; + sec_cnt += 1; + } + //free(tmp); + } + return (out); +} + + + +/** + * WARNING: it can take a while to dump a lot of data. + * @param dvd The DVD drive the command should be exectued on. + * @param offset The memory offset to start dumping, relative to the cache start offset. + * @param block_len The number of blocks to dump. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int liteon_dvd_dump_mem (dvd_drive *dvd, u_int32_t offset, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) { + u_int32_t i; + int r, out; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else { + r = -10; + for (i = 0; i < block_len; i++) { + if ((r = liteon_dvd_dump_memblock (dvd, offset + i * block_size, block_size, buf + i * block_size)) < 0) { + error ("liteon_dvd_dump_memblock() failed with %d", r); + break; + } + } + out = r; + } + + return (out); +} diff --git a/libfriidump/misc.c b/libfriidump/misc.c new file mode 100644 index 0000000..4a0dc2e --- /dev/null +++ b/libfriidump/misc.c @@ -0,0 +1,314 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "misc.h" +#include +#include +#include +#include +#include +#include +//#include +#include + +/*** LOGGING STUFF ***/ +/* Uses code from the printf man page */ +static int my_vasprintf (char **dst, const char *fmt, va_list ap) { + char *p; + int n, size; + bool done; + va_list ap2; + + /* Guess we need no more than 100 bytes. */ + size = 100; + if ((p = (char *) malloc (size)) != NULL) { + do { + done = false; + va_copy (ap2, ap); + n = vsnprintf (p, size, fmt, ap2); + va_end (ap2); + + /* If that worked, return the string. */ + if (n > -1 && n < size) + done = true; + else { + /* Else try again with more space. */ + if (n > -1) /* glibc 2.1 */ + size = n + 1; /* precisely what is needed */ + else /* glibc 2.0 */ + size *= 2; /* twice the old size */ + + if ((p = (char *) realloc (p, size)) == NULL) + done = true; + } + } while (!done); + } + + /* Return -1 if memory allocation failed */ + if (!(*dst = p)) + n = -1; + + return (n); +} + + +/* Appends a print-like string to the log file, with respect to the verbosity setting */ +void _logprintf (int level, char tag[], char format[], ...) { + static bool last_nocr = false; + char *buffer, t[50]; + va_list ap; + struct timeval now; + time_t nowtt; + struct tm nowtm; + + va_start (ap, format); + my_vasprintf (&buffer, format, ap); + va_end (ap); + + /* Start of line, including time and tag */ + if (!last_nocr) { + gettimeofday (&now, NULL); + nowtt = (time_t) now.tv_sec; + if (localtime_r (&nowtt, &nowtm)) + strftime (t, 50, "%H:%M:%S", &nowtm); + if (tag) + fprintf (stderr, "[%s/%s] ", t, tag); + else + fprintf (stderr, "[%s] ", t); + + /* Debug level tag */ + if (level & LOG_WARNING) + fprintf (stderr, "WARNING: "); + else if (level & LOG_ERROR) + fprintf (stderr, "ERROR: "); + #ifdef DEBUG + else if (level & LOG_DEBUG) + fprintf (stderr, "DEBUG: "); + #endif + } + + /* Actual message */ + fprintf (stderr, "%s%s", buffer, level & LOG_NOCR ? "" : "\n"); + fflush (stderr); + free (buffer); + + if (level & LOG_NOCR) + last_nocr = true; + else + last_nocr = false; + + return; +} +/******/ + + +/***************** TAKEN FROM TCPDUMP ****************/ + +#define ASCII_LINELENGTH 300 +#define HEXDUMP_BYTES_PER_LINE 16 +#define HEXDUMP_SHORTS_PER_LINE (HEXDUMP_BYTES_PER_LINE / 2) +#define HEXDUMP_HEXSTUFF_PER_SHORT 5 /* 4 hex digits and a space */ +#define HEXDUMP_HEXSTUFF_PER_LINE (HEXDUMP_HEXSTUFF_PER_SHORT * HEXDUMP_SHORTS_PER_LINE) + +void hex_and_ascii_print_with_offset (const char *ident, register const u_int8_t *cp, + register u_int16_t length, register u_int16_t oset) { + register u_int16_t i; + register int s1, s2; + register int nshorts; + char hexstuff[HEXDUMP_SHORTS_PER_LINE*HEXDUMP_HEXSTUFF_PER_SHORT+1], *hsp; + char asciistuff[ASCII_LINELENGTH+1], *asp; + + nshorts = length / sizeof(unsigned short); + i = 0; + hsp = hexstuff; + asp = asciistuff; + while (--nshorts >= 0) { + s1 = *cp++; + s2 = *cp++; + (void)snprintf(hsp, sizeof(hexstuff) - (hsp - hexstuff), + " %02x%02x", s1, s2); + hsp += HEXDUMP_HEXSTUFF_PER_SHORT; + *(asp++) = (isgraph(s1) ? s1 : '.'); + *(asp++) = (isgraph(s2) ? s2 : '.'); + i++; + if (i >= HEXDUMP_SHORTS_PER_LINE) { + *hsp = *asp = '\0'; + (void)printf("%s0x%04x: %-*s %s", + ident, oset, HEXDUMP_HEXSTUFF_PER_LINE, + hexstuff, asciistuff); + i = 0; + hsp = hexstuff; + asp = asciistuff; + oset += HEXDUMP_BYTES_PER_LINE; + } + } + if (length & 1) { + s1 = *cp++; + (void)snprintf(hsp, sizeof(hexstuff) - (hsp - hexstuff), + " %02x", s1); + hsp += 3; + *(asp++) = (isgraph(s1) ? s1 : '.'); + ++i; + } + if (i > 0) { + *hsp = *asp = '\0'; + debug ("%s0x%04x: %-*s %s", + ident, oset, HEXDUMP_HEXSTUFF_PER_LINE, + hexstuff, asciistuff); + } + + debug ("\n"); // Final spacing +} + +char *strtrimr (char *s) { + int i; + + for (i = strlen (s) - 1; i >= 0 && isspace (s[i]); i--) + s[i] = '\0'; + + return (s); +} + + +/*** The following was taken from the Python sources */ +#if !defined(HAVE_LARGEFILE_SUPPORT) +#error "Large file support must be enabled for this program" +#else + +/* A portable fseek() function + return 0 on success, non-zero on failure (with errno set) */ +int my_fseek (FILE *fp, my_off_t offset, int whence) { + #if defined(HAVE_FSEEKO) && SIZEOF_OFF_T >= 8 + return fseeko(fp, offset, whence); + #elif defined(_MSC_VER) + return _fseeki64(fp, (__int64) offset, whence); + #elif defined(HAVE_FSEEK64) + return fseek64(fp, offset, whence); + #elif defined(__BEOS__) + return _fseek(fp, offset, whence); + #elif SIZEOF_FPOS_T >= 8 + /* lacking a 64-bit capable fseek(), use a 64-bit capable fsetpos() + and fgetpos() to implement fseek()*/ + fpos_t pos; + + switch (whence) { + case SEEK_END: + #ifdef MS_WINDOWS + fflush (fp); + if (_lseeki64 (fileno(fp), 0, 2) == -1) + return -1; + #else + if (fseek (fp, 0, SEEK_END) != 0) + return -1; + #endif + // fall through + case SEEK_CUR: + if (fgetpos (fp, &pos) != 0) + return -1; + offset += pos; + break; + // case SEEK_SET: break; + } + return fsetpos(fp, &offset); + #else + #error "Large file support, but no way to fseek." + #endif +} + + +/* A portable ftell() function + Return -1 on failure with errno set appropriately, current file + position on success */ +my_off_t my_ftell (FILE* fp) { + #if defined(HAVE_FTELLO) && SIZEOF_OFF_T >= 8 + return ftello (fp); + #elif defined(_MSC_VER) + return _ftelli64 (fp); + #elif defined(HAVE_FTELL64) + return ftell64 (fp); + #elif SIZEOF_FPOS_T >= 8 + fpos_t pos; + + if (fgetpos (fp, &pos) != 0) + return -1; + + return pos; + #else + #error "Large file support, but no way to ftell." + #endif +} + +#endif + + + +/*** STUFF FOR DROPPING PRIVILEGES ***/ + +/* WARNING: I'm not sure at all that the privileges-dropping system I have implemented is secure, so don't rely too much on it. */ + +#ifndef WIN32 +#include +#endif + +/** + * Drops privileges to those of the real user (i. e. set euid to ruid). + */ +void drop_euid () { +#ifndef WIN32 + uid_t uid, euid; + + uid = getuid (); + euid = geteuid (); + if (uid != 0 && uid != euid) { +#if 1 + seteuid (uid); +#else + if (seteuid (uid) != 0) + debug ("seteuid() to uid %d failed", uid); + else + debug ("Changed euid from %d to %d", euid, uid); +#endif + } +#endif + + return; +} + + +/** + * Upgrades priviles to those of root (i. e. set euid to 0). + */ +void upgrade_euid () { +#ifndef WIN32 + if (getuid () != 0) { +#if 1 + seteuid (0); +#else + if (seteuid (0) != 0) + debug ("seteuid() to root failed"); + else + debug ("Changed euid to root"); +#endif + } +#endif + + return; +} diff --git a/libfriidump/misc.h b/libfriidump/misc.h new file mode 100644 index 0000000..8ebee80 --- /dev/null +++ b/libfriidump/misc.h @@ -0,0 +1,140 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef MISC_H_INCLUDED +#define MISC_H_INCLUDED + +/*** Include site configuration ***/ +#ifdef HAVE_CONFIG_H +#include +#endif +/******/ + +/*** Windows stuff ***/ +#include "win32compat.h" + +#define _GNU_SOURCE // For strndup() + +#include +#include +#include + + +/*** ASSERTIONS ***/ +#define MY_ASSERT(cond) \ + if (!(cond)) { \ + fprintf (stderr, "*** ASSERTION FAILED at " __FILE__ ":%d: " # cond "\n", __LINE__); \ + exit (9); \ + } + +/*** LOGGING STUFF ***/ +enum { + LOG_NORMAL = 1 << 0, + LOG_WARNING = 1 << 1, + LOG_ERROR = 1 << 2, +#ifdef DEBUG + LOG_DEBUG = 1 << 4, +#endif + LOG_NOCR = 1 << 15 /* Use in OR to avoid a trailing newline */ +}; + +/* Don't use this function explicitly. Use the macros below. */ +void _logprintf (int level, char tag[], char format[], ...); + +#ifdef DEBUG + #define logprintf(level, ...) _logprintf (level, (char *) __FUNCTION__, __VA_ARGS__) + + #define debug(...) logprintf(LOG_DEBUG, __VA_ARGS__) + #define debug_nocr(...) logprintf(LOG_DEBUG | LOG_NOCR, __VA_ARGS__) +#else + #define logprintf(level, ...) _logprintf (level, NULL, __VA_ARGS__) + + #define debug(...) do {;} while (0); + #define debug_nocr(...) do {;} while (0); +#endif + +#if defined (DEBUG) || defined (VERBOSE) +#define log(...) logprintf (LOG_NORMAL, __VA_ARGS__) +#define warning(...) logprintf (LOG_WARNING, __VA_ARGS__) +#define error(...) logprintf (LOG_ERROR, __VA_ARGS__) +#else +#define log(...) +#define warning(...) +#define error(...) +#endif +/******/ + +void hex_and_ascii_print_with_offset(const char *ident, register const u_int8_t *cp, + register u_int16_t length, register u_int16_t oset); +#define hex_and_ascii_print(ident, cp, length) hex_and_ascii_print_with_offset(ident, cp, length, 0) + + +#define my_strdup(dest, src) \ + if (!(dest = strdup (src))) { \ + fprintf (stderr, "strdup() failed\n"); \ + exit (101); \ + } + +#define my_strndup(dest, src, c) \ + if (!(dest = strndup ((const char *) src, c))) { \ + fprintf (stderr, "strndup() failed\n"); \ + exit (102); \ + } + +char *strtrimr (char *s); + +#define my_free(p) \ + if (p) { \ + free (p); \ + p = NULL; \ + } + + +/*** STUFF FOR PORTABLE LARGE-FILES FSEEK ***/ +#if defined (HAVE_OFF_T) && SIZEOF_OFF_T >= 8 +typedef off_t my_off_t; +#elif defined (HAVE_FPOS_T) && SIZEOF_FPOS_T >= 8 +typedef fpos_t my_off_t; +#else +typedef u_int64_t my_off_t; +#endif + +int my_fseek (FILE *fp, my_off_t offset, int whence); +my_off_t my_ftell (FILE* fp); +/*******/ + + +/*** STUFF FOR DROPPING PRIVILEGES ***/ +FRIIDUMPLIB_EXPORT void drop_euid (); +FRIIDUMPLIB_EXPORT void upgrade_euid (); +/******/ + + +/*** STUFF FOR BOOLEAN DATA TYPE ***/ +#ifdef HAVE_STDBOOL_H +/* If using a C99 compiler, use the builtin boolean type */ +#include +#else +typedef enum {false,true} bool; +#endif +/******/ + +#endif diff --git a/libfriidump/renesas.c b/libfriidump/renesas.c new file mode 100644 index 0000000..3f6da5c --- /dev/null +++ b/libfriidump/renesas.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "rs.h" +#include +#include +#include "misc.h" +#include "dvd_drive.h" + +/** + * Command found in Lite-On LH-18A1H + * Expected to work with: DH*, DW*, LH*, SH* series + * Possibly would work with other Mediatek based drives: + * Samsung SE*, SH* series + * Some Dell drives + * Some Sony drives + * Asus DRW-1814* (DRW* series?) + */ + + +/** + * @param dvd The DVD drive the command should be exectued on. + * @param offset The absolute memory offset to start dumping. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int renesas_dvd_dump_memblock (dvd_drive *dvd, u_int32_t offset, u_int32_t block_size, u_int8_t *buf) { + mmc_command mmc; + int out; + + u_int32_t src_offset; + u_int32_t sec_nr; + u_int32_t sec_cnt; + u_int32_t first_sec_nr; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else if (!block_size || block_size > 65535) { + error ("invalid block_size (valid: 1 - 65535)"); + out = -2; + } else { + dvd_init_command (&mmc, buf, block_size, NULL); + mmc.cmd[0] = 0x3C; + mmc.cmd[1] = 0x05; + mmc.cmd[2] = (unsigned char) ((offset & 0xFF000000) >> 24); // address MSB + mmc.cmd[3] = (unsigned char) ((offset & 0x00FF0000) >> 16); // address MSB + mmc.cmd[4] = (unsigned char) ((offset & 0x0000FF00) >> 8); // address + mmc.cmd[5] = (unsigned char) ( offset & 0x000000FF); // address LSB +// mmc.cmd[6] = (unsigned char) ((block_size & 0x00FF0000) >> 16);// length MSB + mmc.cmd[6] = 0; + mmc.cmd[7] = (unsigned char) ((block_size & 0x0000FF00) >> 8); // length + mmc.cmd[8] = (unsigned char) ( block_size & 0x000000FF); // length LSB + mmc.cmd[9] = 0x44; + + out = dvd_execute_cmd (dvd, &mmc, false); + + src_offset = 0; + first_sec_nr = 0x55555555; + sec_cnt = 0; + while (src_offset < block_size) { + sec_nr=(*(buf+src_offset+1)<<16)+(*(buf+src_offset+2)<<8)+(*(buf+src_offset+3)); + if (first_sec_nr==0x55555555) { + first_sec_nr=sec_nr; + } + else + if (sec_nr!=first_sec_nr+sec_cnt) { + error ("sector sequence broken"); + out = -3; + *(buf+src_offset+0)=0xff; + *(buf+src_offset+1)=0xff; + *(buf+src_offset+2)=0xff; + *(buf+src_offset+3)=0xff; + break; + } + src_offset += 2064; + sec_cnt += 1; + } + } + return (out); +} + + + +/** + * WARNING: it can take a while to dump a lot of data. + * @param dvd The DVD drive the command should be exectued on. + * @param offset The memory offset to start dumping, relative to the cache start offset. + * @param block_len The number of blocks to dump. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int renesas_dvd_dump_mem (dvd_drive *dvd, u_int32_t offset, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) { + u_int32_t i; + int r, out; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else { + r = -10; + for (i = 0; i < block_len; i++) { + if ((r = renesas_dvd_dump_memblock (dvd, offset + i * block_size, block_size, buf + i * block_size)) < 0) { + error ("renesas_dvd_dump_memblock() failed with %d", r); + break; + } + } + out = r; + } + + return (out); +} diff --git a/libfriidump/rs.c b/libfriidump/rs.c new file mode 100644 index 0000000..ba2c4dc --- /dev/null +++ b/libfriidump/rs.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include + +#define mm 8 /* RS code over GF(2**mm) - change to suit */ +#define n 256 /* n = size of the field */ +#define nn 182 /* nn=2**mm -1 length of codeword */ +#define kk 172 /* kk = nn-2*tt */ /* Degree of g(x) = 2*tt */ + +//#define NN n-1 +//#define FCR 0 +//#define PRIM 1 +#define _NROOTS nn-kk +//#define PAD NN-nn +//#define A0 NN +//#define IPRIM 1 + +const int NN = n-1; +const int FCR = 0; +const int PRIM = 1; +const int NROOTS = nn-kk; +const int PAD = (n-1)-nn; +const int A0 = n-1; +const int IPRIM = 1; + + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/**** Primitive polynomial ****/ +int pp [mm+1] = { 1, 0, 1, 1, 1, 0, 0, 0, 1}; /* 1+x^2+x^3+x^4+x^8 */ + +/* generator polynomial, tables for Galois field */ +int alpha_to[n], index_of[n], gg[nn-kk+1]; + +int b0 = 1; + +/* data[] is the info vector, bb[] is the parity vector, recd[] is the + noise corrupted received vector */ +int recd[nn], data[kk], bb[nn-kk]; + +int modnn(int x){ + while (x >= 0xff) { + x -= 0xff; + x = (x >> 0xff) + (x & 0xff); + } + return x; +} + + +void generate_gf() + { + register int i, mask ; + + mask = 1 ; + alpha_to[mm] = 0 ; + for (i=0; i>= 1 ; + for (i=mm+1; i<255; i++) + { if (alpha_to[i-1] >= mask) + alpha_to[i] = alpha_to[mm] ^ ((alpha_to[i-1]^mask)<<1) ; + else alpha_to[i] = alpha_to[i-1]<<1 ; + index_of[alpha_to[i]] = i ; + } + index_of[0] = A0 ;//-1 + } + + +void gen_poly() +/* Obtain the generator polynomial of the tt-error correcting, length */ + { + register int i, j, root; + + gg[0] = 1; + + for (i = 0,root=0*1; i < nn-kk; i++,root += 1) { + gg[i+1] = 1; + + for (j = i; j > 0; j--){ + if (gg[j] != 0) + gg[j] = gg[j-1] ^ alpha_to[modnn(index_of[gg[j]] + root)]; + else + gg[j] = gg[j-1]; + } + + gg[0] = alpha_to[modnn(index_of[gg[0]] + root)]; + } + for (i=0; i <= nn-kk; i++) { + gg[i] = index_of[gg[i]]; + } + } + + +void rs_encode(unsigned char *data, unsigned char *bb) + { + register int i,j ; + int feedback; + + for (i=0; i 0) { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = alpha_to[modnn(PRIM*(NN-1-eras_pos[0]))]; + for (i = 1; i < no_eras; i++) { + u = modnn(PRIM*(NN-1-eras_pos[i])); + for (j = i+1; j > 0; j--) { + tmp = index_of[lambda[j - 1]]; + if(tmp != A0) + lambda[j] ^= alpha_to[modnn(u + tmp)]; + } + } + } + for(i=0;i 0; j--){ + if (reg[j] != A0) { + reg[j] = modnn(reg[j] + j); + q ^= alpha_to[reg[j]]; + } + } + if (q != 0) + continue; /* Not a root */ + /* store root (index-form) and error location number */ + root[count] = i; + loc[count] = k; + /* If we've already found max possible roots, + * abort the search to save time + */ + if(++count == deg_lambda) + break; + } + + if (deg_lambda != count) { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + count = -1; + goto finish; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**NROOTS). in index form. Also find deg(omega). + */ + deg_omega = deg_lambda-1; + for (i = 0; i <= deg_omega;i++){ + tmp = 0; + for(j=i;j >= 0; j--){ + if ((s[i - j] != A0) && (lambda[j] != A0)) + tmp ^= alpha_to[modnn(s[i - j] + lambda[j])]; + } + omega[i] = index_of[tmp]; + } + + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count-1; j >=0; j--) { + num1 = 0; + for (i = deg_omega; i >= 0; i--) { + if (omega[i] != A0) + num1 ^= alpha_to[modnn(omega[i] + i * root[j])]; + } + num2 = alpha_to[modnn(root[j] * (FCR - 1) + NN)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + for (i = min(deg_lambda,NROOTS-1) & ~1; i >= 0; i -=2) { + if(lambda[i+1] != A0) + den ^= alpha_to[modnn(lambda[i+1] + i * root[j])]; + } + /* Apply error to data */ + if (num1 != 0 && loc[j] >= PAD) { + data[loc[j]-PAD] ^= alpha_to[modnn(index_of[num1] + index_of[num2] + NN - index_of[den])]; + } + } + + finish: + if(eras_pos != NULL){ + for(i=0;i +#include +#include +#include + +int modnn(int x); +void generate_gf(); +void gen_poly(); +void rs_encode(unsigned char *data, unsigned char *bb); +int rs_decode(unsigned char *data, int *eras_pos, int no_eras); diff --git a/libfriidump/unscrambler.c b/libfriidump/unscrambler.c new file mode 100644 index 0000000..6bfa788 --- /dev/null +++ b/libfriidump/unscrambler.c @@ -0,0 +1,370 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/*! \file + * \brief Unscrambler for Nintendo GameCube/Wii discs. + * + * As Nintendo GameCube/Wii discs use the standars DVD-ROM scrambling algorithm, but with different, unknown, seeds, the actual seeds have to be brute-forced. + * The functions in this file take care of the brute-forcing and of the actual unscrambling of the read sectors. + * + * The code in this file has been derived from unscrambler 0.4, Copyright (C) 2006 Victor Muņoz (xt5@ingenieria-inversa.cl), GPL v2+, + * http://www.ingenieria-inversa.cl/?lp_lang_pref=en . + */ + +#include "misc.h" +#include +#include +#include +#include +#include "constants.h" +#include "byteorder.h" +#include "ecma-267.h" +#include "unscrambler.h" + +// #define unscramblerdebug(...) debug (__VA_ARGS__); +#define unscramblerdebug(...) + +/*! \brief Size of the seeds cache (Do not touch) */ +#define MAX_SEEDS 4 + +/*! \brief Number of bytes of a sector on which the EDC is calculated */ +#define EDC_LENGTH (RAW_SECTOR_SIZE - 4) /* The EDC value is contained in the bottom 4 bytes of a frame */ + + +/*! \brief A structure that represents a seed + */ +typedef struct t_seed { + int seed; //!< The seed, in numeric format. + unsigned char streamcipher[SECTOR_SIZE]; //!< The stream cipher generated from the seed through the LFSR. +} t_seed; + + +/*! \brief A structure that represents an unscrambler + */ +struct unscrambler_s { + t_seed seeds[(MAX_SEEDS + 1) * 16]; //!< The seeds cache. + bool bruteforce_seeds; //!< If true, whenever a seed for a sector is not cached, it will be found via a bruteforce attack, otherwise an error will be returned. +}; + +void unscrambler_set_disctype (u_int8_t disc_type){ + disctype = disc_type; +// fprintf (stdout,"%d",disctype); +} + +/** + * Adds a seed to the cache, calculating its streamcipher. + * @param seeds The seed cache. + * @param seed The seed to add. + * @return A structure representing the added seed, or NULL if it could not be added. + */ +static t_seed *add_seed (t_seed *seeds, unsigned short seed) { + int i; + t_seed *out; + + unscramblerdebug ("Caching seed %04x\n", seed); + + if (seeds -> seed == -2) { + out = NULL; + } else { + seeds -> seed = seed; + + LFSR_init (seed); + for (i = 0; i < SECTOR_SIZE; i++) + seeds -> streamcipher[i] = LFSR_byte (); + + out = seeds; + } + + return (out); +} + + +/** + * Tests if the specified seed is the one used for the specified sector block: the check is done comparing the generated EDC with the one at the bottom of each + * sector. Sectors are processed in blocks, as the same seed is used for 16 consecutive sectors. + * @param buf The sector. + * @param j The seed. + * @return true if the seed is correct, false otherwise. + */ +static bool test_seed (u_int8_t *buf, int j) { + int i; + u_int8_t tmp[RAW_SECTOR_SIZE]; + u_int32_t edc_calculated, edc_correct; + bool out; + + memcpy (tmp, buf, RAW_SECTOR_SIZE); + + LFSR_init (j); + for (i = 12; i < EDC_LENGTH; i++) + tmp[i] ^= LFSR_byte (); + + edc_calculated = edc_calc (0x00000000, tmp, EDC_LENGTH); + edc_correct = my_ntohl (*((u_int32_t *) (&tmp[EDC_LENGTH]))); + if (edc_calculated == edc_correct) + out = true; + else + out = false; + + return (out); +} + + +/** + * Unscramble a complete block, using an already-cached seed. + * @param seed The seed to use for the unscrambling. + * @param _bin The 16-sector block to unscramble (RAW_BLOCK_SIZE). + * @param _bout The unscrambled 16-sector block (BLOCK_SIZE). + * @return True if the unscrambling was successful, false otherwise. + */ +static bool unscramble_frame (t_seed *seed, u_int8_t *_bin, u_int8_t *_bout) { + int i, j; + u_int8_t tmp[RAW_SECTOR_SIZE], *bin, *bout; + u_int32_t *_4bin, *_4cipher, edc_calculated, edc_correct; + bool out; + + out = true; + for(j = 0; j < 16; j++) { + bin = &_bin[RAW_SECTOR_SIZE * j]; + bout = &_bout[SECTOR_SIZE * j]; + + memcpy (tmp, bin, RAW_SECTOR_SIZE); + _4bin = (u_int32_t *) &tmp[12]; /* Scrambled data begin at byte 12 */ + _4cipher = (u_int32_t *) seed -> streamcipher; + for (i = 0; i < 512; i++) /* Well, the scrambling algorithm is just a bitwise XOR... */ + _4bin[i] ^= _4cipher[i]; + + //memcpy (bout, tmp + 6, SECTOR_SIZE); // copy CPR_MAI bytes + + if (disctype==3) { //Regular + memcpy (bout, tmp + 12, SECTOR_SIZE); // DVD: copy 2048 bytes (starting from CPR_MAI) + } + else { //Nintendo + memcpy (bout, tmp + 6, SECTOR_SIZE); // Nintendo: copy 2048 bytes (up to CPR_MAI) + memcpy (&_bin[(RAW_SECTOR_SIZE * j)+2054], &tmp[2054], 6); + } + + edc_calculated = edc_calc (0x00000000, tmp, EDC_LENGTH); + edc_correct = my_ntohl (*((u_int32_t *) (&tmp[EDC_LENGTH]))); + if (edc_calculated != edc_correct) { + debug ("Bad EDC (%08x), must be %08x (sector = %d)", edc_calculated, edc_correct, j); + out = false; + } + } + + return (out); +} + + +/** + * Initializes the seed cache. + * @param u The unscrambler structure. + */ +static void unscrambler_init_seeds (unscrambler *u) { + int i, j; + + for (i = 0; i < 16; i++) { + for (j = 0; j < MAX_SEEDS; j++) + u -> seeds[i * MAX_SEEDS + j].seed = -1; + + u -> seeds[i * MAX_SEEDS + j].seed = -2; // TODO Check what this does + } + + return; +} + + +/** + * Creates a new structure representing an unscrambler. + * @return The newly-created structure, to be used with the other commands. + */ +unscrambler *unscrambler_new (void) { + unscrambler *u; + + u = (unscrambler *) malloc (sizeof (unscrambler)); + unscrambler_init_seeds (u); + u -> bruteforce_seeds = true; + + return (u); +} + + +/** + * Frees resources used by an unscrambler structure and destroys it. + * @param u The unscrambler structure. + * @return NULL. + */ +void *unscrambler_destroy (unscrambler *u) { + my_free (u); + + return (NULL); +} + + +void unscrambler_set_bruteforce (unscrambler *u, bool b) { + u -> bruteforce_seeds = b; + debug ("Seed bruteforcing %s", b ? "enabled" : "disabled"); + + return; +} + + +/** + * Unscrambles a 16-sector block. + * @param u The unscrambler structure. + * @param sector_no The number of the first sector in the block. + * @param inbuf The 16-sector block to unscramble. Each block must be RAW_SECTOR_SIZE bytes long, so that the total size is RAW_BLOCK_SIZE. + * @param outbuf The unscrambled 16-sector block. Each block will be SECTOR_SIZE bytes long, so that the total size is BLOCK_SIZE. + * @return True if the unscrambling was successful, false otherwise. + */ +bool unscrambler_unscramble_16sectors (unscrambler *u, u_int32_t sector_no, u_int8_t *inbuf, u_int8_t *outbuf) { + t_seed *seeds; + t_seed *current_seed; + int j; + bool out; + + out = true; + + seeds = &(u -> seeds[((sector_no / 16) & 0x0F) * MAX_SEEDS]); + + /* Try to find the seed used for this sector */ + current_seed = NULL; + while (!current_seed && (seeds -> seed) >= 0) { + if (test_seed (inbuf, seeds -> seed)) + current_seed = seeds; + else + seeds++; + } + + if (!current_seed && u -> bruteforce_seeds) { + /* The seed is not cached, yet. Try to find it with brute force... */ + unscramblerdebug ("Brute-forcing seed for sector %d...", sector_no); + + for (j = 0; !current_seed && j < 0x7FFF; j++) { + if (test_seed (inbuf, j)) { + if (!(current_seed = add_seed (seeds, j))) { + error ("No enough cache space for caching seed"); + out = false; + } + } + } + + if (current_seed) + unscramblerdebug ("Seed found: %04x", --j); + } + + if (current_seed) { + /* OK, somehow seed was found: unscramble frame, write it and go on */ + if (!unscramble_frame (current_seed, inbuf, outbuf)) { + error ("Error unscrambling frame %u\n", sector_no); + out = false; + } else { + out = true; + } + } else { + /* Well, we only get here if there are read errors */ + error ("Cannot find seed for frame %u", sector_no); + out = false; + } + + return (out); +} + + +/** + * Unscrambles a complete file. + * @param u The unscrambler structure. + * @param infile The input file name. + * @param outfile The output file name. + * @param progress A function to be called repeatedly during the operation, useful to report progress data/statistics. + * @param progress_data Data to be passed as-is to the progress function. + * @return True if the unscrambling was successful, false otherwise. + */ +bool unscrambler_unscramble_file (unscrambler *u, char *infile, char *outfile, unscrambler_progress_func progress, void *progress_data, u_int32_t *current_sector) { + FILE *in, *outfp; + bool out; + u_int8_t b_in[RAW_BLOCK_SIZE], b_out[BLOCK_SIZE]; + size_t r; + my_off_t filesize; + int s; + u_int32_t total_sectors; + + out = false; + if(!(in = fopen (infile ? infile : "", "rb"))) { + error ("Cannot open input file \"%s\"", infile); + } else if (!(outfp = fopen (outfile ? outfile : "", "wb"))) { + error ("Cannot open output file \"%s\"", outfile); + fclose (in); + } else { + /* Find out how many sectors we need to process */ + my_fseek (in, 0, SEEK_END); + filesize = my_ftell (in); + total_sectors = (u_int32_t) (filesize / RAW_SECTOR_SIZE); + rewind (in); + + /* First call to progress function */ + if (progress) + progress (true, 0, total_sectors, progress_data); + + s = 0, out = true; + while ((r = fread (b_in, 1, RAW_BLOCK_SIZE, in)) > 0 && out) { + if (r < RAW_BLOCK_SIZE) { + warning ("Short block read (%u bytes), padding with zeroes!", r); + memset (b_in + r, 0, sizeof (b_in) - r); + } + + if (unscrambler_unscramble_16sectors (u, s, b_in, b_out)) { + clearerr (outfp); + + if (!b_out) { + error ("NULL buffer"); + out = false; + *(current_sector) = s; + } + else fwrite (b_out, SECTOR_SIZE, SECTORS_PER_BLOCK, outfp); + if (ferror (outfp)) { + error ("fwrite() to ISO output file failed"); + out = false; + *(current_sector) = s; + } + } else { + debug ("unscrambler_unscramble_16sectors() failed"); + out = false; + *(current_sector) = s; + } + + s += 16; + + if ((s % 320 == 0) || (s == total_sectors)) { //speedhack + if (progress) + progress (false, s, total_sectors, progress_data); + } + } + + if (out) { + debug ("Image successfully unscrambled"); + } + + fclose (in); + fclose (outfp); + } + + return (out); +} diff --git a/libfriidump/unscrambler.h b/libfriidump/unscrambler.h new file mode 100644 index 0000000..b7f3da6 --- /dev/null +++ b/libfriidump/unscrambler.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef UNSCRAMBLER_H_INCLUDED +#define UNSCRAMBLER_H_INCLUDED + +#include "misc.h" +#include + +typedef struct unscrambler_s unscrambler; + +/* We want this module to be independent of the library framework, so that it can be easily recycled. Hence we just define the type of + the progress function the same format we use elsewhere */ +typedef void (*unscrambler_progress_func) (bool start, u_int32_t current_sector, u_int32_t total_sectors, void *progress_data); + +u_int8_t disctype; + +FRIIDUMPLIB_EXPORT unscrambler *unscrambler_new (void); +FRIIDUMPLIB_EXPORT void *unscrambler_destroy (unscrambler *u); +FRIIDUMPLIB_EXPORT bool unscrambler_unscramble_16sectors (unscrambler *u, u_int32_t sector_no, u_int8_t *inbuf, u_int8_t *outbuf); +FRIIDUMPLIB_EXPORT bool unscrambler_unscramble_file (unscrambler *u, char *infile, char *outfile, unscrambler_progress_func progress, void *progress_data, u_int32_t *current_sector); +FRIIDUMPLIB_EXPORT void unscrambler_set_bruteforce (unscrambler *u, bool b); +FRIIDUMPLIB_EXPORT void unscrambler_set_disctype (u_int8_t disc_type); + +#endif diff --git a/libfriidump/vanilla_2064.c b/libfriidump/vanilla_2064.c new file mode 100644 index 0000000..a95b2d3 --- /dev/null +++ b/libfriidump/vanilla_2064.c @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//#include "rs.h" +#include +#include +#include "misc.h" +#include "dvd_drive.h" + +/** + * Command found in Lite-On LH-18A1H + * Expected to work with: DH*, DW*, LH*, SH* series + * Possibly would work with other Mediatek based drives: + * Samsung SE*, SH* series + * Some Dell drives + * Some Sony drives + * Asus DRW-1814* (DRW* series?) + */ + + +/** + * @param dvd The DVD drive the command should be exectued on. + * @param offset The absolute memory offset to start dumping. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int vanilla_2064_dvd_dump_memblock (dvd_drive *dvd, u_int32_t offset, u_int32_t block_size, u_int8_t *buf) { + mmc_command mmc; + int out; + + u_int32_t src_offset; + u_int32_t sec_nr; + u_int32_t sec_cnt; + u_int32_t first_sec_nr; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else if (!block_size || block_size > 65535) { + error ("invalid block_size (valid: 1 - 65535)"); + out = -2; + } else { + dvd_init_command (&mmc, buf, block_size, NULL); + mmc.cmd[0] = 0x3C; + mmc.cmd[1] = 0x02; + mmc.cmd[2] = 0x00; + mmc.cmd[3] = (unsigned char) ((offset & 0x00FF0000) >> 16); // address MSB + mmc.cmd[4] = (unsigned char) ((offset & 0x0000FF00) >> 8); // address + mmc.cmd[5] = (unsigned char) ( offset & 0x000000FF); // address LSB + mmc.cmd[6] = (unsigned char) ((block_size & 0x00FF0000) >> 16);// length MSB + mmc.cmd[7] = (unsigned char) ((block_size & 0x0000FF00) >> 8); // length + mmc.cmd[8] = (unsigned char) ( block_size & 0x000000FF); // length LSB + + out = dvd_execute_cmd (dvd, &mmc, false); + + src_offset = 0; + first_sec_nr = 0x55555555; + sec_cnt = 0; + while (src_offset < block_size) { + sec_nr=(*(buf+src_offset+1)<<16)+(*(buf+src_offset+2)<<8)+(*(buf+src_offset+3)); + if (first_sec_nr==0x55555555) { + first_sec_nr=sec_nr; + } + else + if (sec_nr!=first_sec_nr+sec_cnt) { + error ("sector sequence broken"); + out = -3; + *(buf+src_offset+0)=0xff; + *(buf+src_offset+1)=0xff; + *(buf+src_offset+2)=0xff; + *(buf+src_offset+3)=0xff; + break; + } + src_offset += 2064; + sec_cnt += 1; + } + } + + return (out); +} + + + +/** + * WARNING: it can take a while to dump a lot of data. + * @param dvd The DVD drive the command should be exectued on. + * @param offset The memory offset to start dumping, relative to the cache start offset. + * @param block_len The number of blocks to dump. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int vanilla_2064_dvd_dump_mem (dvd_drive *dvd, u_int32_t offset, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) { + u_int32_t i; + int r, out; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else { + r = -10; + for (i = 0; i < block_len; i++) { + if ((r = vanilla_2064_dvd_dump_memblock (dvd, offset + i * block_size, block_size, buf + i * block_size)) < 0) { + error ("vanilla_2064_dvd_dump_memblock() failed with %d", r); + break; + } + } + out = r; + } + + return (out); +} diff --git a/libfriidump/vanilla_2384.c b/libfriidump/vanilla_2384.c new file mode 100644 index 0000000..1190d38 --- /dev/null +++ b/libfriidump/vanilla_2384.c @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//#include "rs.h" +#include +#include +#include "misc.h" +#include "dvd_drive.h" + +/** + * Command found in Lite-On LH-18A1H + * Expected to work with: DH*, DW*, LH*, SH* series + * Possibly would work with other Mediatek based drives: + * Samsung SE*, SH* series + * Some Dell drives + * Some Sony drives + * Asus DRW-1814* (DRW* series?) + */ + +//u_int8_t tmp[64*1024]; + +/** + * @param dvd The DVD drive the command should be exectued on. + * @param offset The absolute memory offset to start dumping. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int vanilla_2384_dvd_dump_memblock (dvd_drive *dvd, u_int32_t offset, u_int32_t block_size, u_int8_t *buf) { + mmc_command mmc; + int out; + u_int32_t raw_block_size; + u_int32_t raw_offset; + + u_int32_t src_offset; + u_int32_t dst_offset; + u_int32_t row_nr; + u_int32_t sec_nr; + u_int32_t sec_cnt; + u_int32_t first_sec_nr; + u_int8_t tmp[64*1024]; + //u_int8_t *tmp; + + raw_block_size = (block_size / 2064) * 0x950; + raw_offset = (offset / 2064) * 0x950; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else if (!block_size || raw_block_size > 65535) { + error ("invalid raw_block_size (valid: 1 - 65535)"); + error ("raw_block_size = (block_size / 2064) * 2384"); + out = -2; + } else { + //tmp = malloc(64*1024); + dvd_init_command (&mmc, tmp, raw_block_size, NULL); + mmc.cmd[0] = 0x3C; + mmc.cmd[1] = 0x02; + mmc.cmd[2] = 0x00; + mmc.cmd[3] = (unsigned char) ((raw_offset & 0x00FF0000) >> 16);// address MSB + mmc.cmd[4] = (unsigned char) ((raw_offset & 0x0000FF00) >> 8); // address + mmc.cmd[5] = (unsigned char) ( raw_offset & 0x000000FF); // address LSB + mmc.cmd[6] = (unsigned char) ((raw_block_size & 0x00FF0000) >> 16); // length MSB + mmc.cmd[7] = (unsigned char) ((raw_block_size & 0x0000FF00) >> 8); // length + mmc.cmd[8] = (unsigned char) ( raw_block_size & 0x000000FF); // length LSB + + out = dvd_execute_cmd (dvd, &mmc, false); + + src_offset = 0; + dst_offset = 0; + first_sec_nr = 0x55555555; + sec_cnt = 0; + while (src_offset < raw_block_size) { + sec_nr=(*(tmp+src_offset+1)<<16)+(*(tmp+src_offset+2)<<8)+(*(tmp+src_offset+3)); + if (first_sec_nr==0x55555555) { + first_sec_nr=sec_nr; +// rs_decode(tmp+src_offset, 0, 0); + } + else + if (sec_nr==first_sec_nr+sec_cnt) { +// rs_decode(tmp+src_offset, 0, 0); //sector seq = ok + } + else { //sector seq broken -> corrupt + error ("sector sequence broken"); + out = -3; + *(tmp+src_offset+0)=0xff; + *(tmp+src_offset+1)=0xff; + *(tmp+src_offset+2)=0xff; + *(tmp+src_offset+3)=0xff; + break; + } + for (row_nr=0; row_nr<12; row_nr++) { + memcpy(buf+dst_offset, tmp+src_offset, 172); + dst_offset += 172; + src_offset += 182; + } + src_offset += 200; + sec_cnt += 1; + } + //free(tmp); + } + return (out); +} + + + +/** + * WARNING: it can take a while to dump a lot of data. + * @param dvd The DVD drive the command should be exectued on. + * @param offset The memory offset to start dumping, relative to the cache start offset. + * @param block_len The number of blocks to dump. + * @param block_size The block size for the dump. + * @param buf Where to place the dumped data. This must have been setup by the caller to store up to block_size bytes. + * @return < 0 if an error occurred, 0 otherwise. + */ +int vanilla_2384_dvd_dump_mem (dvd_drive *dvd, u_int32_t offset, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) { + u_int32_t i; + int r, out; + + if (!buf) { + error ("NULL buffer"); + out = -1; + } else { + r = -10; + for (i = 0; i < block_len; i++) { + if ((r = vanilla_2384_dvd_dump_memblock (dvd, offset + i * block_size, block_size, buf + i * block_size)) < 0) { + error ("vanilla_2384_dvd_dump_memblock() failed with %d", r); + break; + } + } + out = r; + } + + return (out); +} diff --git a/libfriidump/win32compat.c b/libfriidump/win32compat.c new file mode 100644 index 0000000..36ac72a --- /dev/null +++ b/libfriidump/win32compat.c @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://www.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifdef WIN32 + +#include "win32compat.h" +#include +#include +#include +#include + + +char *strndup (const char *src, int c) { + char *dest; + + if ((dest = (char *) malloc ((c + 1) * sizeof (char)))) { + memcpy (dest, src, c); + dest[c] = '\0'; + } + + return (dest); +} + + +int ftruncate (int fd, __int64 size) { + return (_chsize_s (fd, size)); +} + + + +/* The following gettimeofday() replacement function was taken from Snort, GPLv2 */ +/**************************************************************************** + * + * Function: gettimeofday(struct timeval *, struct timezone *) + * + * Purpose: Get current time of day. + * + * Arguments: tv => Place to store the curent time of day. + * tz => Ignored. + * + * Returns: 0 => Success. + * + ****************************************************************************/ +int gettimeofday(struct timeval *tv, struct timezone *tz) { + struct _timeb tb; + + if (tv == NULL) { + return -1; + } + _ftime(&tb); + tv->tv_sec = (long) tb.time; + tv->tv_usec = ((int) tb.millitm) * 1000; + return 0; +} + +#endif diff --git a/libfriidump/win32compat.h b/libfriidump/win32compat.h new file mode 100644 index 0000000..5a06eda --- /dev/null +++ b/libfriidump/win32compat.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://www.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +/*** WINDOWS COMPATIBILITY STUFF ***/ + +#ifdef WIN32 +#include +#include + +/* Stuff to export library symbols */ +#ifdef FRIIDUMPLIB_BUILD_DLL +#ifdef FRIIDUMPLIB_EXPORTS +#define FRIIDUMPLIB_EXPORT __declspec(dllexport) /* Building the lib */ +#else +#define FRIIDUMPLIB_EXPORT __declspec(dllimport) /* Building user code */ +#endif +#else +#define FRIIDUMPLIB_EXPORT +#endif + +/* Definition of the types we use for Windows */ +#define u_int8_t BYTE +#define u_int16_t WORD +#define int32_t INT32 +#define u_int32_t DWORD +#define int64_t INT64 +#define u_int64_t ULONGLONG +typedef long suseconds_t; + +/* Some functions have different names */ +#define snprintf _snprintf +#define strdup _strdup +#define strcasecmp lstrcmpi +#define va_copy(ap1, ap2) ((ap1) = (ap2)) // MSVC doesn't have va_copy, how crap... + +/* Some functions do not exist (export them for applications, too) */ +FRIIDUMPLIB_EXPORT char *strndup (const char *src, int c); +FRIIDUMPLIB_EXPORT int ftruncate (int fd, __int64 size); +FRIIDUMPLIB_EXPORT int gettimeofday(struct timeval *tv, struct timezone *tz); + +/* Windows does have a different localtime() */ +#define localtime_r(x, y) !localtime_s (y, x) + +#else + +#define FRIIDUMPLIB_EXPORT + +#endif +/******/ diff --git a/libmultihash/CMakeLists.txt b/libmultihash/CMakeLists.txt new file mode 100644 index 0000000..31fbd8a --- /dev/null +++ b/libmultihash/CMakeLists.txt @@ -0,0 +1,77 @@ +# Create a library called "Hello" which includes the source file "hello.cxx". +# The extension is already found. Any number of sources could be listed here. +add_library ( + multihashlib + ${libmultihash_type} + #SHARED + #STATIC + + crc32.c + edonkey.c + md4.c + md5.c + multihash.c + sha1.c + crc32.h + edonkey.h + md4.h + md5.h + multihash.h + sha1.h +) + +set_target_properties (multihashlib PROPERTIES OUTPUT_NAME "multihash") + +# Before making a release, the LTVERSION string should be modified. +# The string is of the form CURRENT:REVISION:AGE. +# +# CURRENT (C) +# The most recent interface number that this library implements. +# +# REVISION (R) +# The implementation number that this library implements. +# +# AGE (A) +# The difference between the newest and oldest interfaces that this +# library implements. In other works, the library implements all the +# interface numbers in the range from number 'CURRENT - AGE' to +# 'CURRENT'. +# +# This means that: +# +# - If interfaces have been changed or added, but binary compatibility has +# been preserved, change to C+1:0:A+1 +# +# - If binary compatibility has been broken (eg removed or changed +# interfaces) change to C+1:0:0 +# +# - If the interface is the same as the previous version, change to C:R+1:A +# +set_target_properties (multihashlib PROPERTIES SOVERSION 1.0.0) + + +#get_target_property(libhash_type multihashlib TYPE) +if (WIN32) + if (libhash_type STREQUAL "SHARED") +# MESSAGE ("Building libmultihash DLL") + ADD_DEFINITIONS (-DMULTIHASH_BUILD_DLL) + + set_target_properties (multihashlib PROPERTIES DEFINE_SYMBOL MULTIHASH_EXPORTS) + + install ( + TARGETS multihashlib + RUNTIME DESTINATION / + #ARCHIVE DESTINATION lib + ) + endif (libhash_type STREQUAL "SHARED") +else (WIN32) + # Install stuff, only if a shared library is being built + if (libhash_type STREQUAL "SHARED") + install ( + TARGETS multihashlib + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + ) + endif (libhash_type STREQUAL "SHARED") +endif (WIN32) + diff --git a/libmultihash/crc32.c b/libmultihash/crc32.c new file mode 100644 index 0000000..6f86b1f --- /dev/null +++ b/libmultihash/crc32.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 1995, Edward B. Hamrick + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that + * + * (i) the above copyright notice and the text in this "C" comment block + * appear in all copies of the software and related documentation, and + * + * (ii) any modifications to this source file must be sent, via e-mail + * to the copyright owner (currently hamrick@primenet.com) within + * 30 days of such modification. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL EDWARD B. HAMRICK BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "crc32.h" + +/* This is a pre-computed table to make crc computations efficient */ +static unsigned long crctable[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, + 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, + 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, + 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, + 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, + 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, + 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, + 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, + 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, + 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, + 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, + 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, + 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, + 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, + 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, + 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, + 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, + 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, + 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, + 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, + 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, + 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, + 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, + 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, + 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, + 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, + 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL +}; + +/* + * This CRC algorithm is the same as that used in zip. Normally it + * should be initialized with 0xffffffff, and the final CRC stored + * should be crc ^ 0xffffffff. + * + * It implements the polynomial: + * + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 + */ + +unsigned long CrcUpdate( /* returns updated crc */ + unsigned long crc, /* starting crc */ + unsigned char *buffer, /* buffer to use to update crc */ + long length /* length of buffer */ +) +{ + long i; + + for (i=0; i> 8); + } + + return crc; +} diff --git a/libmultihash/crc32.h b/libmultihash/crc32.h new file mode 100644 index 0000000..2a1acbe --- /dev/null +++ b/libmultihash/crc32.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1995, Edward B. Hamrick + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that + * + * (i) the above copyright notice and the text in this "C" comment block + * appear in all copies of the software and related documentation, and + * + * (ii) any modifications to this source file must be sent, via e-mail + * to the copyright owner (currently hamrick@primenet.com) within + * 30 days of such modification. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL EDWARD B. HAMRICK BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This CRC algorithm is the same as that used in zip. Normally it + * should be initialized with 0xffffffff, and the final CRC stored + * should be crc ^ 0xffffffff. + * + * It implements the polynomial: + * + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 + */ + +#ifndef __CRC_H +#define __CRC_H + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned long CrcUpdate( /* returns updated crc */ + unsigned long crc, /* starting crc */ + unsigned char *buffer, /* buffer to use to update crc */ + long length /* length of buffer */ +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libmultihash/edonkey.c b/libmultihash/edonkey.c new file mode 100644 index 0000000..44beba5 --- /dev/null +++ b/libmultihash/edonkey.c @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2007 by SukkoPera * + * sukkopera@sukkology.net * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* As Wikipedia (http://en.wikipedia.org/wiki/Ed2k_link) says: + * The ed2k hash function is a MD4 root hash of a MD4 hash list, and gives a + * different result than simply MD4: The file data is divided into full + * chunks of 9728000 bytes plus a remainder chunk, and a separate 128-bit + * MD4 checksum is computed for each. The ed2k hash is computed by + * concatenating the chunks' MD4 checksums in order and hashing the result + *using MD4. + */ + +#include +#include "edonkey.h" +#include "md4.h" + +void ed2khash_starts (ed2khash_context *ctx) { + md4_starts (&(ctx -> md4cur)); + md4_starts (&(ctx -> md4final)); + ctx -> bytes_processed = 0; + ctx -> chunks = 0; + + return; +} + + +void ed2khash_update (ed2khash_context *ctx, unsigned char *input, int ilen) { + unsigned long x; + + while (ilen > 0) { + if (ctx -> bytes_processed + ilen >= ED2KHASH_CHUNKSIZE) + x = ED2KHASH_CHUNKSIZE - ctx -> bytes_processed; + else + x = ilen; + + md4_update (&(ctx -> md4cur), input, x); + + if ((ctx -> bytes_processed += x) % ED2KHASH_CHUNKSIZE == 0) { + /* End of a chunk, save current MD4 and start a new one */ + md4_finish (&(ctx -> md4cur), ctx -> lastmd4); + md4_starts (&(ctx -> md4cur)); + ctx -> bytes_processed = 0; + ctx -> chunks++; + + md4_update (&(ctx -> md4final), ctx -> lastmd4, MD4_DIGESTSIZE); + } + ilen -= x; + input += x; + } + + return; +} + + +void ed2khash_finish (ed2khash_context *ctx, unsigned char *output) { + if (ctx -> chunks > 0) { + md4_finish (&(ctx -> md4cur), ctx -> lastmd4); + md4_update (&(ctx -> md4final), ctx -> lastmd4, MD4_DIGESTSIZE); + md4_finish (&(ctx -> md4final), output); + } else { + /* If we have a single chunk, use its MD4 straight away */ + md4_finish (&(ctx -> md4cur), output); + } + + return; +} diff --git a/libmultihash/edonkey.h b/libmultihash/edonkey.h new file mode 100644 index 0000000..3d9d0a1 --- /dev/null +++ b/libmultihash/edonkey.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2007 by SukkoPera * + * sukkopera@sukkology.net * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "md4.h" + +#define ED2KHASH_CHUNKSIZE 9728000 +#define MD4_DIGESTSIZE 16 + +typedef struct { + unsigned long bytes_processed; + unsigned int chunks; + md4_context md4cur; + md4_context md4final; + unsigned char lastmd4[MD4_DIGESTSIZE]; +} ed2khash_context; + +void ed2khash_starts (ed2khash_context *ctx); +void ed2khash_update (ed2khash_context *ctx, unsigned char *input, int ilen); +void ed2khash_finish (ed2khash_context *ctx, unsigned char *output); diff --git a/libmultihash/md4.c b/libmultihash/md4.c new file mode 100644 index 0000000..3b2eda0 --- /dev/null +++ b/libmultihash/md4.c @@ -0,0 +1,441 @@ +/* + * RFC 1186/1320 compliant MD4 implementation + * + * Copyright (C) 2006-2007 Christophe Devine + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +/* + * The MD4 algorithm was designed by Ron Rivest in 1990. + * + * http://www.ietf.org/rfc/rfc1186.txt + * http://www.ietf.org/rfc/rfc1320.txt + */ + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +#include +#include + +#include "md4.h" + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +/* + * MD4 context setup + */ +void md4_starts( md4_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +static void md4_process( md4_context *ctx, unsigned char data[64] ) +{ + unsigned long X[16], A, B, C, D; + + GET_UINT32_LE( X[ 0], data, 0 ); + GET_UINT32_LE( X[ 1], data, 4 ); + GET_UINT32_LE( X[ 2], data, 8 ); + GET_UINT32_LE( X[ 3], data, 12 ); + GET_UINT32_LE( X[ 4], data, 16 ); + GET_UINT32_LE( X[ 5], data, 20 ); + GET_UINT32_LE( X[ 6], data, 24 ); + GET_UINT32_LE( X[ 7], data, 28 ); + GET_UINT32_LE( X[ 8], data, 32 ); + GET_UINT32_LE( X[ 9], data, 36 ); + GET_UINT32_LE( X[10], data, 40 ); + GET_UINT32_LE( X[11], data, 44 ); + GET_UINT32_LE( X[12], data, 48 ); + GET_UINT32_LE( X[13], data, 52 ); + GET_UINT32_LE( X[14], data, 56 ); + GET_UINT32_LE( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x, y, z) ((x & y) | ((~x) & z)) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 1], 7 ); + P( C, D, A, B, X[ 2], 11 ); + P( B, C, D, A, X[ 3], 19 ); + P( A, B, C, D, X[ 4], 3 ); + P( D, A, B, C, X[ 5], 7 ); + P( C, D, A, B, X[ 6], 11 ); + P( B, C, D, A, X[ 7], 19 ); + P( A, B, C, D, X[ 8], 3 ); + P( D, A, B, C, X[ 9], 7 ); + P( C, D, A, B, X[10], 11 ); + P( B, C, D, A, X[11], 19 ); + P( A, B, C, D, X[12], 3 ); + P( D, A, B, C, X[13], 7 ); + P( C, D, A, B, X[14], 11 ); + P( B, C, D, A, X[15], 19 ); + +#undef P +#undef F + +#define F(x,y,z) ((x & y) | (x & z) | (y & z)) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x5A827999; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 4], 5 ); + P( C, D, A, B, X[ 8], 9 ); + P( B, C, D, A, X[12], 13 ); + P( A, B, C, D, X[ 1], 3 ); + P( D, A, B, C, X[ 5], 5 ); + P( C, D, A, B, X[ 9], 9 ); + P( B, C, D, A, X[13], 13 ); + P( A, B, C, D, X[ 2], 3 ); + P( D, A, B, C, X[ 6], 5 ); + P( C, D, A, B, X[10], 9 ); + P( B, C, D, A, X[14], 13 ); + P( A, B, C, D, X[ 3], 3 ); + P( D, A, B, C, X[ 7], 5 ); + P( C, D, A, B, X[11], 9 ); + P( B, C, D, A, X[15], 13 ); + +#undef P +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x6ED9EBA1; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 8], 9 ); + P( C, D, A, B, X[ 4], 11 ); + P( B, C, D, A, X[12], 15 ); + P( A, B, C, D, X[ 2], 3 ); + P( D, A, B, C, X[10], 9 ); + P( C, D, A, B, X[ 6], 11 ); + P( B, C, D, A, X[14], 15 ); + P( A, B, C, D, X[ 1], 3 ); + P( D, A, B, C, X[ 9], 9 ); + P( C, D, A, B, X[ 5], 11 ); + P( B, C, D, A, X[13], 15 ); + P( A, B, C, D, X[ 3], 3 ); + P( D, A, B, C, X[11], 9 ); + P( C, D, A, B, X[ 7], 11 ); + P( B, C, D, A, X[15], 15 ); + +#undef F +#undef P + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD4 process buffer + */ +void md4_update( md4_context *ctx, unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + md4_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + md4_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char md4_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * MD4 final digest + */ +void md4_finish( md4_context *ctx, unsigned char *output ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_LE( low, msglen, 0 ); + PUT_UINT32_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md4_update( ctx, (unsigned char *) md4_padding, padn ); + md4_update( ctx, msglen, 8 ); + + PUT_UINT32_LE( ctx->state[0], output, 0 ); + PUT_UINT32_LE( ctx->state[1], output, 4 ); + PUT_UINT32_LE( ctx->state[2], output, 8 ); + PUT_UINT32_LE( ctx->state[3], output, 12 ); +} + +/* + * Output = MD4( input buffer ) + */ +void md4( unsigned char *input, int ilen, + unsigned char *output ) +{ + md4_context ctx; + + md4_starts( &ctx ); + md4_update( &ctx, input, ilen ); + md4_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( md4_context ) ); +} + +/* + * Output = MD4( file contents ) + */ +int md4_file( char *path, unsigned char *output ) +{ + FILE *f; + size_t n; + md4_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( 1 ); + + md4_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + md4_update( &ctx, buf, (int) n ); + + md4_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( md4_context ) ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( 2 ); + } + + fclose( f ); + return( 0 ); +} + +/* + * MD4 HMAC context setup + */ +void md4_hmac_starts( md4_context *ctx, + unsigned char *key, int keylen ) +{ + int i; + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + if( i >= 64 ) break; + + ctx->ipad[i] ^= key[i]; + ctx->opad[i] ^= key[i]; + } + + md4_starts( ctx ); + md4_update( ctx, ctx->ipad, 64 ); +} + +/* + * MD4 HMAC process buffer + */ +void md4_hmac_update( md4_context *ctx, + unsigned char *input, int ilen ) +{ + md4_update( ctx, input, ilen ); +} + +/* + * MD4 HMAC final digest + */ +void md4_hmac_finish( md4_context *ctx, unsigned char *output ) +{ + unsigned char tmpbuf[16]; + + md4_finish( ctx, tmpbuf ); + md4_starts( ctx ); + md4_update( ctx, ctx->opad, 64 ); + md4_update( ctx, tmpbuf, 16 ); + md4_finish( ctx, output ); + + memset( tmpbuf, 0, sizeof( tmpbuf ) ); +} + +/* + * Output = HMAC-MD4( hmac key, input buffer ) + */ +void md4_hmac( unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char *output ) +{ + md4_context ctx; + + md4_hmac_starts( &ctx, key, keylen ); + md4_hmac_update( &ctx, input, ilen ); + md4_hmac_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( md4_context ) ); +} + +static const char _md4_src[] = "_md4_src"; + +#if defined(SELF_TEST) +/* + * RFC 1320 test vectors + */ +static const char md4_test_str[7][81] = +{ + { "" }, + { "a" }, + { "abc" }, + { "message digest" }, + { "abcdefghijklmnopqrstuvwxyz" }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, + { "12345678901234567890123456789012345678901234567890123456789012" \ + "345678901234567890" } +}; + +static const unsigned char md4_test_sum[7][16] = +{ + { 0x31, 0xD6, 0xCF, 0xE0, 0xD1, 0x6A, 0xE9, 0x31, + 0xB7, 0x3C, 0x59, 0xD7, 0xE0, 0xC0, 0x89, 0xC0 }, + { 0xBD, 0xE5, 0x2C, 0xB3, 0x1D, 0xE3, 0x3E, 0x46, + 0x24, 0x5E, 0x05, 0xFB, 0xDB, 0xD6, 0xFB, 0x24 }, + { 0xA4, 0x48, 0x01, 0x7A, 0xAF, 0x21, 0xD8, 0x52, + 0x5F, 0xC1, 0x0A, 0xE8, 0x7A, 0xA6, 0x72, 0x9D }, + { 0xD9, 0x13, 0x0A, 0x81, 0x64, 0x54, 0x9F, 0xE8, + 0x18, 0x87, 0x48, 0x06, 0xE1, 0xC7, 0x01, 0x4B }, + { 0xD7, 0x9E, 0x1C, 0x30, 0x8A, 0xA5, 0xBB, 0xCD, + 0xEE, 0xA8, 0xED, 0x63, 0xDF, 0x41, 0x2D, 0xA9 }, + { 0x04, 0x3F, 0x85, 0x82, 0xF2, 0x41, 0xDB, 0x35, + 0x1C, 0xE6, 0x27, 0xE1, 0x53, 0xE7, 0xF0, 0xE4 }, + { 0xE3, 0x3B, 0x4D, 0xDC, 0x9C, 0x38, 0xF2, 0x19, + 0x9C, 0x3E, 0x7B, 0x16, 0x4F, 0xCC, 0x05, 0x36 } +}; + +/* + * Checkup routine + */ +int md4_self_test( int verbose ) +{ + int i; + unsigned char md4sum[16]; + + for( i = 0; i < 7; i++ ) + { + if( verbose != 0 ) + printf( " MD4 test #%d: ", i + 1 ); + + md4( (unsigned char *) md4_test_str[i], + strlen( md4_test_str[i] ), md4sum ); + + if( memcmp( md4sum, md4_test_sum[i], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + return( 0 ); +} +#else +int md4_self_test( int verbose ) +{ + return( 0 ); +} +#endif diff --git a/libmultihash/md4.h b/libmultihash/md4.h new file mode 100644 index 0000000..c0cb343 --- /dev/null +++ b/libmultihash/md4.h @@ -0,0 +1,121 @@ +/** + * \file md4.h + */ +#ifndef _MD4_H +#define _MD4_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD4 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +} +md4_context; + +/** + * \brief MD4 context setup + * + * \param ctx context to be initialized + */ +void md4_starts( md4_context *ctx ); + +/** + * \brief MD4 process buffer + * + * \param ctx MD4 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md4_update( md4_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief MD4 final digest + * + * \param ctx MD4 context + * \param output MD4 checksum result + */ +void md4_finish( md4_context *ctx, unsigned char *output ); + +/** + * \brief Output = MD4( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD4 checksum result + */ +void md4( unsigned char *input, int ilen, + unsigned char *output ); + +/** + * \brief Output = MD4( file contents ) + * + * \param path input file name + * \param output MD4 checksum result + * + * \return 0 if successful, 1 if fopen failed, + * or 2 if fread failed + */ +int md4_file( char *path, unsigned char *output ); + +/** + * \brief MD4 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void md4_hmac_starts( md4_context *ctx, + unsigned char *key, int keylen ); + +/** + * \brief MD4 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md4_hmac_update( md4_context *ctx, + unsigned char *input, int ilen ); + +/** + * \brief MD4 HMAC final digest + * + * \param ctx HMAC context + * \param output MD4 HMAC checksum result + */ +void md4_hmac_finish( md4_context *ctx, unsigned char *output ); + +/** + * \brief Output = HMAC-MD4( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-MD4 result + */ +void md4_hmac( unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char *output ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int md4_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* md4.h */ diff --git a/libmultihash/md5.c b/libmultihash/md5.c new file mode 100644 index 0000000..f90f4cb --- /dev/null +++ b/libmultihash/md5.c @@ -0,0 +1,477 @@ +/* + ********************************************************************** + ** md5.c ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* -- include the following line if the md5.h header file is separate -- */ +#include "md5.h" + +/* forward declaration */ +static void Transform (); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G and H are basic MD5 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +void MD5Final (mdContext) +MD5_CTX *mdContext; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transform buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360U); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710U); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819U); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966U); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399U); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426U); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955U); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313U); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416U); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879U); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233U); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134U); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682U); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195U); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006U); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329U); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786U); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664U); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713U); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994U); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605U); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083U); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961U); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448U); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438U); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606U); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335U); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501U); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829U); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512U); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473U); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562U); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738U); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833U); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562U); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740U); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236U); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353U); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664U); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656U); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174U); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074U); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317U); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189U); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809U); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461U); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520U); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645U); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452U); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415U); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391U); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241U); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571U); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690U); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773U); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497U); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359U); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552U); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916U); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649U); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226U); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917U); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259U); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745U); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + ********************************************************************** + ** End of md5.c ** + ******************************* (cut) ******************************** + */ + +#if 0 +/* + ********************************************************************** + ** md5driver.c -- sample routines to test ** + ** RSA Data Security, Inc. MD5 message digest algorithm. ** + ** Created: 2/16/90 RLR ** + ** Updated: 1/91 SRD ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +#include +#include +#include +#include +/* -- include the following file if the file md5.h is separate -- */ +/* #include "md5.h" */ + +/* Prints message digest buffer in mdContext as 32 hexadecimal digits. + Order is from low-order byte to high-order byte of digest. + Each byte is printed with high-order hexadecimal digit first. + */ +static void MDPrint (mdContext) +MD5_CTX *mdContext; +{ + int i; + + for (i = 0; i < 16; i++) + printf ("%02x", mdContext->digest[i]); +} + +/* size of test block */ +#define TEST_BLOCK_SIZE 1000 + +/* number of blocks to process */ +#define TEST_BLOCKS 10000 + +/* number of test bytes = TEST_BLOCK_SIZE * TEST_BLOCKS */ +static long TEST_BYTES = (long)TEST_BLOCK_SIZE * (long)TEST_BLOCKS; + +/* A time trial routine, to measure the speed of MD5. + Measures wall time required to digest TEST_BLOCKS * TEST_BLOCK_SIZE + characters. + */ +static void MDTimeTrial () +{ + MD5_CTX mdContext; + time_t endTime, startTime; + unsigned char data[TEST_BLOCK_SIZE]; + unsigned int i; + + /* initialize test data */ + for (i = 0; i < TEST_BLOCK_SIZE; i++) + data[i] = (unsigned char)(i & 0xFF); + + /* start timer */ + printf ("MD5 time trial. Processing %ld characters...\n", TEST_BYTES); + time (&startTime); + + /* digest data in TEST_BLOCK_SIZE byte blocks */ + MD5Init (&mdContext); + for (i = TEST_BLOCKS; i > 0; i--) + MD5Update (&mdContext, data, TEST_BLOCK_SIZE); + MD5Final (&mdContext); + + /* stop timer, get time difference */ + time (&endTime); + MDPrint (&mdContext); + printf (" is digest of test input.\n"); + printf + ("Seconds to process test input: %ld\n", (long)(endTime-startTime)); + printf + ("Characters processed per second: %ld\n", + TEST_BYTES/(endTime-startTime)); +} + +/* Computes the message digest for string inString. + Prints out message digest, a space, the string (in quotes) and a + carriage return. + */ +static void MDString (inString) +char *inString; +{ + MD5_CTX mdContext; + unsigned int len = strlen (inString); + + MD5Init (&mdContext); + MD5Update (&mdContext, inString, len); + MD5Final (&mdContext); + MDPrint (&mdContext); + printf (" \"%s\"\n\n", inString); +} + +/* Computes the message digest for a specified file. + Prints out message digest, a space, the file name, and a carriage + return. + */ +static void MDFile (filename) +char *filename; +{ + FILE *inFile = fopen (filename, "rb"); + MD5_CTX mdContext; + int bytes; + unsigned char data[1024]; + + if (inFile == NULL) { + printf ("%s can't be opened.\n", filename); + return; + } + + MD5Init (&mdContext); + while ((bytes = fread (data, 1, 1024, inFile)) != 0) + MD5Update (&mdContext, data, bytes); + MD5Final (&mdContext); + MDPrint (&mdContext); + printf (" %s\n", filename); + fclose (inFile); +} + +/* Writes the message digest of the data from stdin onto stdout, + followed by a carriage return. + */ +static void MDFilter () +{ + MD5_CTX mdContext; + int bytes; + unsigned char data[16]; + + MD5Init (&mdContext); + while ((bytes = fread (data, 1, 16, stdin)) != 0) + MD5Update (&mdContext, data, bytes); + MD5Final (&mdContext); + MDPrint (&mdContext); + printf ("\n"); +} + +/* Runs a standard suite of test data. + */ +static void MDTestSuite () +{ + printf ("MD5 test suite results:\n\n"); + MDString (""); + MDString ("a"); + MDString ("abc"); + MDString ("message digest"); + MDString ("abcdefghijklmnopqrstuvwxyz"); + MDString + ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + MDString + ("1234567890123456789012345678901234567890\ +1234567890123456789012345678901234567890"); + /* Contents of file foo are "abc" */ + MDFile ("foo"); +} + +void main (argc, argv) +int argc; +char *argv[]; +{ + int i; + + /* For each command line argument in turn: + ** filename -- prints message digest and name of file + ** -sstring -- prints message digest and contents of string + ** -t -- prints time trial statistics for 1M characters + ** -x -- execute a standard suite of test data + ** (no args) -- writes messages digest of stdin onto stdout + */ + if (argc == 1) + MDFilter (); + else + for (i = 1; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == 's') + MDString (argv[i] + 2); + else if (strcmp (argv[i], "-t") == 0) + MDTimeTrial (); + else if (strcmp (argv[i], "-x") == 0) + MDTestSuite (); + else MDFile (argv[i]); +} + +/* + ********************************************************************** + ** End of md5driver.c ** + ******************************* (cut) ******************************** + */ +#endif diff --git a/libmultihash/md5.h b/libmultihash/md5.h new file mode 100644 index 0000000..2b61fd3 --- /dev/null +++ b/libmultihash/md5.h @@ -0,0 +1,59 @@ +/* + ********************************************************************** + ** md5.h -- Header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* typedef a 32 bit type */ +typedef unsigned long int UINT4; + +/* Data structure for MD5 (Message Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (); +void MD5Update (); +void MD5Final (); + +/* + ********************************************************************** + ** End of md5.h ** + ******************************* (cut) ******************************** + */ diff --git a/libmultihash/multihash.c b/libmultihash/multihash.c new file mode 100644 index 0000000..f5d0899 --- /dev/null +++ b/libmultihash/multihash.c @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (C) 2007 by SukkoPera * + * sukkopera@sukkology.net * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "multihash.h" +#include + +#define READBUF_SIZE 8192 + + +void multihash_init (multihash *mh) { +#ifdef USE_CRC32 + (mh -> crc32_s)[0] = '\0'; + mh -> crc32 = 0xffffffff; +#endif +#ifdef USE_MD4 + (mh -> md4_s)[0] = '\0'; + md4_starts (&(mh -> md4)); +#endif +#ifdef USE_MD5 + (mh -> md5_s)[0] = '\0'; + MD5Init (&(mh -> md5)); +#endif +#ifdef USE_ED2K + (mh -> ed2k_s)[0] = '\0'; + ed2khash_starts (&(mh -> ed2k)); +#endif +#ifdef USE_SHA1 + (mh -> sha1_s)[0] = '\0'; + SHA1Init (&(mh -> sha1)); +#endif + + return; +} + + +void multihash_update (multihash *mh, unsigned char *data, int bytes) { +#ifdef USE_CRC32 + mh -> crc32 = CrcUpdate (mh -> crc32, data, bytes); +#endif +#ifdef USE_MD4 +// md4_update (&(mh -> md4), data, bytes); +#endif +#ifdef USE_MD5 + MD5Update (&(mh -> md5), data, bytes); +#endif +#ifdef USE_ED2K +// ed2khash_update (&(mh -> ed2k), data, bytes); +#endif +#ifdef USE_SHA1 + SHA1Update (&(mh -> sha1), data, bytes); /* WARNING: SHA1Update() destroys data! */ +#endif + + return; +} + + +void multihash_finish (multihash *mh) { + unsigned char buf[MAX_DIGESTSIZE]; + int bytes; + +#ifdef USE_CRC32 + mh -> crc32 ^= 0xffffffff; + snprintf (mh -> crc32_s, LEN_CRC32 + 1, "%08x", mh -> crc32); +#endif +#ifdef USE_MD4 + md4_finish (&(mh -> md4), buf); + for (bytes = 0; bytes < LEN_MD4 / 2; bytes++) + sprintf (mh -> md4_s, "%s%02x", mh -> md4_s, buf[bytes]); + (mh -> md4_s)[LEN_MD4] = '\0'; +#endif +#ifdef USE_MD5 + MD5Final (&(mh -> md5)); + for (bytes = 0; bytes < LEN_MD5 / 2; bytes++) + sprintf (mh -> md5_s, "%s%02x", mh -> md5_s, (mh -> md5).digest[bytes]); + (mh -> md5_s)[LEN_MD5] = '\0'; +#endif +#ifdef USE_ED2K + ed2khash_finish (&(mh -> ed2k), buf); + for (bytes = 0; bytes < LEN_ED2K / 2; bytes++) + sprintf (mh -> ed2k_s, "%s%02x", mh -> ed2k_s, buf[bytes]); + (mh -> ed2k_s)[LEN_ED2K] = '\0'; +#endif +#ifdef USE_SHA1 + SHA1Final (buf, &(mh -> sha1)); + for (bytes = 0; bytes < LEN_SHA1 / 2; bytes++) + sprintf (mh -> sha1_s, "%s%02x", mh -> sha1_s, buf[bytes]); + (mh -> sha1_s)[LEN_SHA1] = '\0'; + +#endif + + return; +} + + +int multihash_file (multihash *mh, char *filename) { + FILE *fp; + int bytes, out; + unsigned char data[READBUF_SIZE]; + + multihash_init (mh); + if ((fp = fopen (filename, "r"))) { + while ((bytes = fread (data, 1, READBUF_SIZE, fp)) != 0) + multihash_update (mh, data, bytes); + multihash_finish (mh); + fclose (fp); + out = 0; + } else { + /* Cannot open file */ + out = -1; + } + + return (out); +} diff --git a/libmultihash/multihash.h b/libmultihash/multihash.h new file mode 100644 index 0000000..2fe6522 --- /dev/null +++ b/libmultihash/multihash.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2007 by SukkoPera * + * sukkopera@sukkology.net * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef MULTIHASH_H_INCLUDED +#define MULTIHASH_H_INCLUDED + +#ifdef WIN32 +#include + +/* Definition of the types we use for Windows */ +#define u_int8_t BYTE +#define u_int16_t WORD +#define u_int32_t DWORD + +/* Some functions have different names */ +#define snprintf _snprintf + +/* Stuff to export library symbols */ +#ifdef MULTIHASH_BUILD_DLL +#ifdef MULTIHASH_EXPORTS +#define MULTIHASH_EXPORT __declspec(dllexport) /* Building the lib */ +#else +#define MULTIHASH_EXPORT __declspec(dllimport) /* Building user code */ +#endif +#else +#define MULTIHASH_EXPORT +#endif + +#else /* !WIN32 */ + +#define MULTIHASH_EXPORT + +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#define USE_CRC32 +#define USE_MD4 +#define USE_MD5 +#define USE_ED2K +#define USE_SHA1 + +#ifdef USE_CRC32 +#include +#include "crc32.h" +#define LEN_CRC32 8 +#endif + +#ifdef USE_MD4 +#include "md4.h" +#define LEN_MD4 32 +#define MD4_DIGESTSIZE 16 +#endif + +#ifdef USE_MD5 +#include "md5.h" +#define LEN_MD5 32 +#endif + +#ifdef USE_ED2K +#include "edonkey.h" +#define LEN_ED2K 32 +#endif + +#ifdef USE_SHA1 +#include "sha1.h" +#define LEN_SHA1 40 +#endif + +/* This must be as long as the longest hash (in bytes) */ +#define MAX_DIGESTSIZE 20 + +typedef struct { +#ifdef USE_CRC32 + u_int32_t crc32; + char crc32_s[LEN_CRC32 + 1]; +#endif +#ifdef USE_MD4 + md4_context md4; + char md4_s[LEN_MD4 + 1]; +#endif +#ifdef USE_MD5 + MD5_CTX md5; + char md5_s[LEN_MD5 + 1]; +#endif +#ifdef USE_ED2K + ed2khash_context ed2k; + char ed2k_s[LEN_ED2K + 1]; +#endif +#ifdef USE_SHA1 + SHA1_CTX sha1; + char sha1_s[LEN_SHA1 + 1]; +#endif +} multihash; + + +/* Prototypes */ +MULTIHASH_EXPORT void multihash_init (multihash *mh); +MULTIHASH_EXPORT void multihash_update (multihash *mh, unsigned char *data, int bytes); +MULTIHASH_EXPORT void multihash_finish (multihash *mh); +MULTIHASH_EXPORT int multihash_file (multihash *mh, char *filename); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libmultihash/sha1.c b/libmultihash/sha1.c new file mode 100644 index 0000000..f44d034 --- /dev/null +++ b/libmultihash/sha1.c @@ -0,0 +1,231 @@ +/* +** sha1.c +** +** Contains all of the SHA1 functions: SHA1Transform, SHA1Init, SHA1Update, and SHA1Final. +** Make sure to define _LITTLE_ENDIAN if running on a little endian machine and NOT to +** define it otherwise. +** +** Copyright NTT MCL, 2000. +** +** Satomi Okazaki +** Security Group, NTT MCL +** November 1999 +** +************************** +** 13 December 1999. In SHA1Transform, changed "buffer" to be const. +** In SHA1Update, changed "data to be const. -- S.O. +*/ +#include "sha1.h" + +#define _LITTLE_ENDIAN /* should be defined if so */ + +/* Rotation of "value" by "bits" to the left */ +#define rotLeft(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* Basic SHA1 functions */ +#define f(u,v,w) (((u) & (v)) | ((~u) & (w))) +#define g(u,v,w) (((u) & (v)) | ((u) & (w)) | ((v) & (w))) +#define h(u,v,w) ((u) ^ (v) ^ (w)) + +/* These are the 16 4-byte words of the 64-byte block */ +#ifdef _LITTLE_ENDIAN +/* Reverse the order of the bytes in the i-th 4-byte word */ +#define x(i) (block->l[i] = (rotLeft(block->l[i], 24)& 0xff00ff00) \ +| (rotLeft(block->l[i], 8) & 0x00ff00ff)) +#else +#define x(i) (block->l[i]) +#endif + +/* Used in expanding from a 16 word block into an 80 word block */ +#define X(i) (block->l[(i)%16] = rotLeft (block->l[((i)-3)%16] ^ block->l[((i)-8)%16] \ +^ block->l[((i)-14)%16] ^ block->l[((i)-16)%16],1)) + +/* (R0+R1), R2, R3, R4 are the different round operations used in SHA1 */ +#define R0(a, b, c, d, e, i) { \ + (e) += f((b), (c), (d)) + (x(i)) + 0x5A827999 + rotLeft((a),5); \ + (b) = rotLeft((b), 30); \ +} +#define R1(a, b, c, d, e, i) { \ + (e) += f((b), (c), (d)) + (X(i)) + 0x5A827999 + rotLeft((a),5); \ + (b) = rotLeft((b), 30); \ +} +#define R2(a, b, c, d, e, i) { \ + (e) += h((b), (c), (d)) + (X(i)) + 0x6ED9EBA1 + rotLeft((a),5); \ + (b) = rotLeft((b), 30); \ +} +#define R3(a, b, c, d, e, i) { \ + (e) += g((b), (c), (d)) + (X(i)) + 0x8F1BBCDC + rotLeft((a),5); \ + (b) = rotLeft((b), 30); \ +} +#define R4(a, b, c, d, e, i) { \ + (e) += h((b), (c), (d)) + (X(i)) + 0xCA62C1D6 + rotLeft((a),5); \ + (b) = rotLeft((b), 30); \ +} + +/* Hashes a single 512-bit block. This is the compression function - the core of the algorithm. +**/ +void SHA1Transform( + unsigned long state[5], + const unsigned char buffer[SHA1_BLOCKSIZE] + ) +{ + unsigned long a, b, c, d, e; + + typedef union { + unsigned char c[64]; + unsigned long l[16]; + } CHAR64LONG16; + + /* This is for the X array */ + CHAR64LONG16* block = (CHAR64LONG16*)buffer; + + /* Initialize working variables */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. */ + /* Round 1 */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e, 10); R0(e,a,b,c,d, 11); + R0(d,e,a,b,c, 12); R0(c,d,e,a,b, 13); R0(b,c,d,e,a, 14); R0(a,b,c,d,e, 15); + R1(e,a,b,c,d, 16); R1(d,e,a,b,c, 17); R1(c,d,e,a,b, 18); R1(b,c,d,e,a, 19); + + /* Round 2 */ + R2(a,b,c,d,e, 20); R2(e,a,b,c,d, 21); R2(d,e,a,b,c, 22); R2(c,d,e,a,b, 23); + R2(b,c,d,e,a, 24); R2(a,b,c,d,e, 25); R2(e,a,b,c,d, 26); R2(d,e,a,b,c, 27); + R2(c,d,e,a,b, 28); R2(b,c,d,e,a, 29); R2(a,b,c,d,e, 30); R2(e,a,b,c,d, 31); + R2(d,e,a,b,c, 32); R2(c,d,e,a,b, 33); R2(b,c,d,e,a, 34); R2(a,b,c,d,e, 35); + R2(e,a,b,c,d, 36); R2(d,e,a,b,c, 37); R2(c,d,e,a,b, 38); R2(b,c,d,e,a, 39); + + /* Round 3 */ + R3(a,b,c,d,e, 40); R3(e,a,b,c,d, 41); R3(d,e,a,b,c, 42); R3(c,d,e,a,b, 43); + R3(b,c,d,e,a, 44); R3(a,b,c,d,e, 45); R3(e,a,b,c,d, 46); R3(d,e,a,b,c, 47); + R3(c,d,e,a,b, 48); R3(b,c,d,e,a, 49); R3(a,b,c,d,e, 50); R3(e,a,b,c,d, 51); + R3(d,e,a,b,c, 52); R3(c,d,e,a,b, 53); R3(b,c,d,e,a, 54); R3(a,b,c,d,e, 55); + R3(e,a,b,c,d, 56); R3(d,e,a,b,c, 57); R3(c,d,e,a,b, 58); R3(b,c,d,e,a, 59); + + /* Round 4 */ + R4(a,b,c,d,e, 60); R4(e,a,b,c,d, 61); R4(d,e,a,b,c, 62); R4(c,d,e,a,b, 63); + R4(b,c,d,e,a, 64); R4(a,b,c,d,e, 65); R4(e,a,b,c,d, 66); R4(d,e,a,b,c, 67); + R4(c,d,e,a,b, 68); R4(b,c,d,e,a, 69); R4(a,b,c,d,e, 70); R4(e,a,b,c,d, 71); + R4(d,e,a,b,c, 72); R4(c,d,e,a,b, 73); R4(b,c,d,e,a, 74); R4(a,b,c,d,e, 75); + R4(e,a,b,c,d, 76); R4(d,e,a,b,c, 77); R4(c,d,e,a,b, 78); R4(b,c,d,e,a, 79); + + /* Update the chaining values */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + +/* SHA1Init - Initialize new context. +**/ +void SHA1Init( + SHA1_CTX* context + ) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +/* Run your data through this. This will call the compression function SHA1Transform for each +** 64-byte block of data. +**/ +void SHA1Update( + SHA1_CTX* context, + const unsigned char* data, + unsigned long dataLen + ) +{ + unsigned long numByteDataProcessed; /* Number of bytes processed so far */ + unsigned long numByteInBuffMod64; /* Number of bytes in the buffer mod 64 */ + + numByteInBuffMod64 = (context->count[0] >> 3) % 64; + + /* Adding in the number of bits of data */ + if ((context->count[0] += dataLen << 3) < (dataLen << 3)) { + context->count[1]++; /* add in the carry bit */ + } + context->count[1] += (dataLen >> 29); + + /* If there is at least one block to be processed... */ + if ((numByteInBuffMod64 + dataLen) > 63) { + + /* Copy over 64-numByteInBuffMod64 bytes of data to the end of buffer */ + memcpy(&context->buffer[numByteInBuffMod64], data, + (numByteDataProcessed = 64 - numByteInBuffMod64)); + + /* Perform the transform on the buffer */ + SHA1Transform(context->state, context->buffer); + + /* As long as there are 64-bit blocks of data remaining, transform each one. */ + for ( ; numByteDataProcessed + 63 < dataLen; numByteDataProcessed += 64) { + SHA1Transform(context->state, &data[numByteDataProcessed]); + } + + numByteInBuffMod64 = 0; + } + /* Else there is not enough to process one block. */ + else + numByteDataProcessed = 0; + + /* Copy over the remaining data into the buffer */ + memcpy(&context->buffer[numByteInBuffMod64], &data[numByteDataProcessed], + dataLen - numByteDataProcessed); +} + +/* Add padding and return the message digest. +**/ +void SHA1Final( + unsigned char digest[SHA1_DIGESTSIZE], + SHA1_CTX* context + ) +{ + unsigned long i, j; + unsigned char numBits[8]; + + /* Record the number of bits */ + for (i = 1, j = 0; j < 8; i--, j += 4) { + numBits[j] = (unsigned char)((context->count[i] >> 24) & 0xff); + numBits[j+1] = (unsigned char)((context->count[i] >> 16) & 0xff); + numBits[j+2] = (unsigned char)((context->count[i] >> 8) & 0xff); + numBits[j+3] = (unsigned char)(context->count[i] & 0xff); + } + + /* Add padding */ + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) + SHA1Update(context, (unsigned char *)"\0", 1); + + /* Append length */ + SHA1Update(context, numBits, 8); /* Should cause a SHA1Transform() */ + + /* Store state in digest */ + for (i = 0, j = 0; j < 20; i++, j += 4) { + digest[j] = (unsigned char)((context->state[i] >> 24) & 0xff); + digest[j+1] = (unsigned char)((context->state[i] >> 16) & 0xff); + digest[j+2] = (unsigned char)((context->state[i] >> 8) & 0xff); + digest[j+3] = (unsigned char)((context->state[i]) & 0xff); + } + + /* Wipe variables */ + i = 0; + j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&numBits, 0, 8); +} diff --git a/libmultihash/sha1.h b/libmultihash/sha1.h new file mode 100644 index 0000000..c094b60 --- /dev/null +++ b/libmultihash/sha1.h @@ -0,0 +1,39 @@ +/* +** sha1.h +** +** Copyright NTT MCL, 2000. +** +** Satomi Okazaki +** Security Group, NTT MCL +** November 1999 +** +************************** +** 13 December 1999. In SHA1Transform, changed "buffer" to be const. +** In SHA1Update, changed "data to be const. -- S.O. +*/ +#ifndef __SHA1_H__ +#define __SHA1_H__ + +#include +#include + +#ifndef SHA1_DIGESTSIZE +#define SHA1_DIGESTSIZE 20 +#endif + +#ifndef SHA1_BLOCKSIZE +#define SHA1_BLOCKSIZE 64 +#endif + +typedef struct { + unsigned long state[5]; + unsigned long count[2]; /* stores the number of bits */ + unsigned char buffer[SHA1_BLOCKSIZE]; +} SHA1_CTX; + +void SHA1Transform(unsigned long state[5], const unsigned char buffer[SHA1_BLOCKSIZE]); +void SHA1Init(SHA1_CTX *context); +void SHA1Update(SHA1_CTX *context, const unsigned char *data, unsigned long len); +void SHA1Final(unsigned char digest[SHA1_DIGESTSIZE], SHA1_CTX *context); + +#endif /* __SHA1_H__ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..1ee59db --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,50 @@ +# Make sure the compiler can find include files from our Hello library. +include_directories ( + ${FriiDump_SOURCE_DIR}/libfriidump +) + +# Make sure the linker can find the Hello library once it is built. +link_directories ( + ${FriiDump_BINARY_DIR}/libfriidump +) + +# Add executable called "helloDemo" that is built from the source files +# "demo.cxx" and "demo_b.cxx". The extensions are automatically found. +if (WIN32) + set( + additional_sources + + getopt-win32.h + getopt-win32.c + getopt_long-win32.c + ) +endif (WIN32) + +add_executable ( + friidump + + friidump.c + ${additional_sources} +) + +# Link the executable to the Hello library. +target_link_libraries ( + friidump + + friidumplib +) + +if (WIN32) + # We only want a version number on Windows (Is it any use?) + set_target_properties (friidump PROPERTIES VERSION 0.2) + + install( + TARGETS friidump + RUNTIME DESTINATION / + ) +else (WIN32) + install( + TARGETS friidump + RUNTIME DESTINATION bin + ) +endif (WIN32) \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..48ecb75 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,11 @@ +mylibdir = $(top_builddir)/libfriidump +myincludedir = $(top_srcdir)/libfriidump + +bin_PROGRAMS = friidump +friidump_SOURCES = config.h.cmake friidump.c +friidump_LDADD = $(mylibdir)/libfriidump.la +friidump_LDFLAGS = $(all_libraries) + +# noinst_HEADERS = +INCLUDES = $(all_includes) -I$(myincludedir) + diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..901d5ff --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,455 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = friidump$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_friidump_OBJECTS = friidump.$(OBJEXT) +friidump_OBJECTS = $(am_friidump_OBJECTS) +friidump_DEPENDENCIES = $(mylibdir)/libfriidump.la +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(friidump_SOURCES) +DIST_SOURCES = $(friidump_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +mylibdir = $(top_builddir)/libfriidump +myincludedir = $(top_srcdir)/libfriidump +friidump_SOURCES = config.h.cmake friidump.c +friidump_LDADD = $(mylibdir)/libfriidump.la +friidump_LDFLAGS = $(all_libraries) + +# noinst_HEADERS = +INCLUDES = $(all_includes) -I$(myincludedir) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +friidump$(EXEEXT): $(friidump_OBJECTS) $(friidump_DEPENDENCIES) + @rm -f friidump$(EXEEXT) + $(LINK) $(friidump_LDFLAGS) $(friidump_OBJECTS) $(friidump_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/friidump.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-info-am + +# 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/src/friidump.c b/src/friidump.c new file mode 100644 index 0000000..8b26fe5 --- /dev/null +++ b/src/friidump.c @@ -0,0 +1,760 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://wii.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "misc.h" +#include +#include +#include +#include +#include "disc.h" +#include "dumper.h" +#include "unscrambler.h" + +#define USECS_PER_SEC 1000000 + + +/* Name of package */ +#define PACKAGE "friidump" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "arep@no.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "FriiDump" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.5.3.1" + + +#ifdef WIN32 + +#include "getopt-win32.h" + + +#else +#include +#include +#endif + + +/* Struct for program options */ +struct { + char *device; + bool autodump; + bool gui; + char *raw_in; + char *raw_out; + char *iso_out; + bool resume; + int dump_method; + u_int32_t command; + u_int32_t start_sector; + u_int32_t sectors_no; + u_int32_t speed; + u_int32_t disctype; + u_int32_t sec_disc; + u_int32_t sec_mem; + bool no_hashing; + bool no_unscrambling; + bool no_flushing; + bool stop_unit; + bool allmethods; +} options; + + +/* Struct for progress data */ +typedef struct { + struct timeval start_time; + struct timeval end_time; + double mb_total; + double mb_total_real; + u_int32_t sectors_skipped; +} progstats; + + +void progress_for_guis (bool start, u_int32_t sectors_done, u_int32_t total_sectors, progstats *stats) { + int perc; + double elapsed, mb_done, mb_done_real, mb_hour, seconds_left; + struct timeval now; + time_t eta; + struct tm etatm; + char buf[50]; + + if (start) { + gettimeofday (&(stats -> start_time), NULL); + stats -> mb_total = (double) total_sectors * 2064 / 1024 / 1024; + stats -> mb_total_real = (double) (total_sectors - sectors_done) * 2064 / 1024 / 1024; + stats -> sectors_skipped = sectors_done; + } else { + perc = (int) (100.0 * sectors_done / total_sectors); + gettimeofday (&now, NULL); + elapsed = difftime (now.tv_sec, (stats -> start_time).tv_sec); + mb_done = (double) sectors_done * 2064 / 1024 / 1024; + mb_done_real = (double) (sectors_done - stats -> sectors_skipped) * 2064 / 1024 / 1024; + mb_hour = mb_done_real / elapsed * 60 * 60; + seconds_left = stats -> mb_total_real / mb_hour * 60 * 60; + eta = (time_t) ((stats -> start_time).tv_sec + seconds_left); + if (localtime_r (&eta, &etatm)) + strftime (buf, 50, "%d/%m/%Y %H:%M:%S", &etatm); + else + sprintf (buf, "N/A"); + + /* This is the only thing we print to stdout, so that other programs can easily capture and parse our output */ + fprintf (stdout, "%d%%|%u/%u sectors|%.2lf/%.0lf MB|%.0lf/%.0lf seconds|%.2lf MB/h|%s\n", + perc, sectors_done, total_sectors, mb_done, stats -> mb_total, elapsed, seconds_left, mb_hour, buf); + fflush (stdout); + } + + /* Save return time, in case this will be the last call */ + gettimeofday (&(stats -> end_time), NULL); + + return; +} + + +void progress (bool start, u_int32_t sectors_done, u_int32_t total_sectors, progstats *stats) { + int perc, i; + double elapsed, mb_done, mb_done_real, mb_hour, seconds_left; + struct timeval now; + time_t eta; + struct tm etatm; + char buf[50]; + + if (start) { + gettimeofday (&(stats -> start_time), NULL); + stats -> mb_total = (double) total_sectors * 2064 / 1024 / 1024; + stats -> mb_total_real = (double) (total_sectors - sectors_done) * 2064 / 1024 / 1024; + stats -> sectors_skipped = sectors_done; + } else { + perc = (int) (100.0 * sectors_done / total_sectors); + gettimeofday (&now, NULL); + elapsed = difftime (now.tv_sec, (stats -> start_time).tv_sec); + mb_done = (double) sectors_done * 2064 / 1024 / 1024; + mb_done_real = (double) (sectors_done - stats -> sectors_skipped) * 2064 / 1024 / 1024; + mb_hour = mb_done_real / elapsed * 60 * 60; + seconds_left = stats -> mb_total_real / mb_hour * 60 * 60; + eta = (time_t) ((stats -> start_time).tv_sec + seconds_left); + if (localtime_r (&eta, &etatm)) + strftime (buf, 50, "%d/%m/%Y %H:%M:%S", &etatm); + else + sprintf (buf, "N/A"); + + fprintf (stdout, "\r%3d%% ", perc); + fprintf (stdout, "|"); + for (i = 0; i < 100 / 3; i++) { + if (i == perc / 3) + fprintf (stdout, "*"); + else + fprintf (stdout, "-"); + } + fprintf (stdout, "| "); + fprintf (stdout, "%.2lf MB/h, ETA: %s", mb_hour, buf); + fflush (stdout); + } + + if (sectors_done == total_sectors) + printf ("\n"); + + /* Save return time, in case this will be the last call */ + gettimeofday (&(stats -> end_time), NULL); + + return; +} + + + +void welcome (void) { + /* Welcome text */ + fprintf (stderr, + "FriiDump " PACKAGE_VERSION " - Copyright (C) 2007 Arep\n" + "This software comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, and you are welcome to redistribute it\n" + "under certain conditions; see COPYING for details.\n" + "\n" + "Official support forum: http://wii.console-tribe.com\n" + "\n" + "Forum for this UNOFFICIAL VERSION: http://forum.redump.org\n" + "\n" + ); + fflush (stderr); + + return; +} + + +void help (void) { + /* 80 cols guide: + * |-------------------------------------------------------------------------------| + */ + fprintf (stderr, "\n" + "Available command line options:\n" + "\n" + " -h, --help Show this help\n" + " -a, --autodump Dump the disc to an ISO file with an\n" + " automatically-generated name, resuming the dump\n" + " if possible\n" + " -g, --gui Use more verbose output that can be easily\n" + " parsed by a GUI frontend\n" + " -d, --device Dump disc from device \n" + " -p, --stop Instruct device to stop disc rotation\n" + " -c, --command Force memory dump command:\n" + " 0 - vanilla 2064\n" + " 1 - vanilla 2384\n" + " 2 - Hitachi\n" + " 3 - Lite-On\n" + " 4 - Renesas\n" + " -x, --speed Set streaming speed (1, 24, 32, 64, etc.,\n" + " where 1 = 150 KiB/s and so on)\n" + " -T, --type Force disc type:\n" + " 0 - GameCube\n" + " 1 - Wii\n" + " 2 - Wii_DL\n" + " 3 - DVD\n" + " -S, --size Force disc size\n" + " -r, --raw Output to file in raw format (2064-byte\n" + " sectors)\n" + " -i, --iso Output to file in ISO format (2048-byte\n" + " sectors)\n" + " -u, --unscramble Convert (unscramble) raw image contained in\n" + " to ISO format\n" + " -H, --nohash Do not compute CRC32/MD5/SHA-1 hashes\n" + " for generated files\n" + " -s, --resume Resume partial dump\n" + " - General -----------------------------------\n" + " -0, --method0[=,] Use dumping method 0 (Optional argument\n" + " specifies how many sectors to request from disc\n" + " and read from cache at a time. Values should be\n" + " separated with a comma. Default 16,16)\n" + " - Non-Streaming -----------------------------\n" + " -1, --method1[=,] Use dumping method 1 (Default 16,16)\n" + " -2, --method2[=,] Use dumping method 2 (Default 16,16)\n" + " -3, --method3[=,] Use dumping method 3 (Default 16,16)\n" + " - Streaming ---------------------------------\n" + " -4, --method4[=,] Use dumping method 4 (Default 27,27)\n" + " -5, --method5[=,] Use dumping method 5 (Default 27,27)\n" + " -6, --method6[=,] Use dumping method 6 (Default 27,27)\n" + " - Hitachi -----------------------------------\n" + " -7, --method7 Use dumping method 7 (Read and dump 5 blocks\n" + " at a time, using streaming read)\n" + " -8, --method8 Use dumping method 8 (Read and dump 5 blocks\n" + " at a time, using streaming read, using DMA)\n" + " -9, --method9 Use dumping method 9 (Read and dump 5 blocks\n" + " at a time, using streaming read, using DMA and\n" + " some speed tricks)\n" + " -A, --allmethods Try all known methods and commands until\n" + " one works.\n" +#ifdef DEBUG + " -n, --donottunscramble Do not try unscrambling to check EDC. Only\n" + " useful for testing the raw performance of the\n" + " different methods\n" + " -f, --donottflush Do not call fflush() after every fwrite()\n" +#endif + ); + + return; +} + + +bool optparse (int argc, char **argv) { + bool out; + char *result = NULL; + int c; + int option_index = 0; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, //0 - no_argument + {"autodump", 0, 0, 'a'}, + {"gui", 0, 0, 'g'}, + {"device", 1, 0, 'd'}, //1 - required_argument + {"raw", 1, 0, 'r'}, + {"iso", 1, 0, 'i'}, + {"unscramble", 1, 0, 'u'}, + {"nohash", 0, 0, 'H'}, + {"resume", 0, 0, 's'}, + {"method0", 2, 0, '0'}, //2 - optional_argument + {"method1", 2, 0, '1'}, + {"method2", 2, 0, '2'}, + {"method3", 2, 0, '3'}, + {"method4", 2, 0, '4'}, + {"method5", 2, 0, '5'}, + {"method6", 2, 0, '6'}, + {"method7", 0, 0, '7'}, + {"method8", 0, 0, '8'}, + {"method9", 0, 0, '9'}, + {"stop", 0, 0, 'p'}, + {"command", 1, 0, 'c'}, + {"startsector", 1, 0, 't'}, + {"size", 1, 0, 'S'}, + {"speed", 1, 0, 'x'}, + {"type", 1, 0, 'T'}, + {"allmethods", 0, 0, 'A'}, +#ifdef DEBUG + /* We don't want newbies to generate and put into circulation bad dumps, so this options are disabled for releases */ + {"donottunscramble", 0, 0, 'n'}, + {"donottflush", 0, 0, 'f'}, +#endif + {0, 0, 0, 0} + }; + + if (argc == 1) { + help (); + exit (1); + } + + /* Init options to default values */ + options.device = NULL; + options.autodump = false; + options.gui = false; + options.raw_in = NULL; + options.raw_out = NULL; + options.iso_out = NULL; + options.no_hashing = false; + options.resume = false; + options.dump_method = -1; + options.command = -1; + options.start_sector = -1; + options.sectors_no = -1; + options.speed = -1; + options.disctype = -1; + options.sec_disc = -1; + options.sec_mem = -1; + options.no_unscrambling = false; + options.no_flushing = false; + options.stop_unit = false; + options.allmethods = false; + + do { +#ifdef DEBUG + c = getopt_long (argc, argv, "hpagd:r:i:u:Hs0::1::2::3::4::5::6::789c:t:S:x:T:Anf", long_options, &option_index); +#else + c = getopt_long (argc, argv, "hpagd:r:i:u:Hs0::1::2::3::4::5::6::789c:t:S:x:T:A", long_options, &option_index); +#endif + + switch (c) { + case 'h': + help (); + exit (1); + break; + case 'p': + options.stop_unit = true; + break; + case 'a': + options.autodump = true; + options.resume = true; + break; + case 'g': + options.gui = true; + break; + case 'd': + my_strdup (options.device, optarg); + break; + case 'r': + my_strdup (options.raw_out, optarg); + break; + case 'i': + my_strdup (options.iso_out, optarg); + break; + case 'u': + my_strdup (options.raw_in, optarg); + break; + case 'H': + options.no_hashing = true; + break; + case 's': + options.resume = true; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + options.dump_method = c - '0'; + if (optarg) { + result = strtok(optarg, ","); + result = strtok(NULL, ","); + options.sec_disc = atol(strpbrk(optarg,"1234567890")); + if (result) options.sec_mem = atol(result); + else { + help (); + exit (1); + } + } + break; + case '7': + case '8': + case '9': + options.dump_method = c - '0'; + break; + case 'c': + options.command = atol (optarg); + if (options.command > 4) { + help (); + exit (1); + }; + break; + case 't': + options.start_sector = atol (optarg); + break; + case 'S': + options.sectors_no = atol (optarg); + break; + case 'x': + options.speed = atol (optarg); + break; + case 'T': + options.disctype = atol (optarg); + if (options.disctype > 3) { + help (); + exit (1); + }; + unscrambler_set_disctype (options.disctype); + break; + case 'A': + options.allmethods = true; + options.resume = true; + break; +#ifdef DEBUG + case 'n': + options.no_unscrambling = true; + break; + case 'f': + options.no_flushing = true; + break; +#endif + case -1: + break; + default: +// fprintf (stderr, "?? getopt returned character code 0%o ??\n", c); + exit (7); + break; + } + } while (c != -1); + + if (optind < argc) { + /* Command-line arguments remaining. Ignore them, warning the user. */ + fprintf (stderr, "WARNING: Extra parameters ignored\n"); + } + + /* Sanity checks... */ + out = false; + if (!options.device && !options.raw_in) { + fprintf (stderr, "No operation specified. Please use the -d or -u options.\n"); + } else if (options.raw_in && options.raw_out) { + fprintf (stderr, + "Are you sure you want to convert a raw image to another raw image? ;)\n" + "Take a look at the -i and -a options!\n" + ); + } else if (options.autodump && (options.raw_out || options.iso_out)) { + fprintf (stderr, "The -r and -i options cannot be used together with -a.\n"); + } else { + /* Specified options seem to make sense */ + out = true; + } + + return (out); +} + +int dologic (disc *d, progstats stats) { + disc_type type_id; + char *type, *game_id, *region, *maker_id, *maker, *version, *title, tmp[0x03E0 + 4 + 1]; + bool drive_supported; + int out; + dumper *dmp; + u_int32_t current_sector; + + + + if (options.stop_unit) { //stop rotation, if requested + fprintf (stderr, "Issuing STOP command... %s\n", (disc_stop_unit (d, false)) ? "OK" : "Failed"); + exit (1); + } + else disc_stop_unit(d, true); //else start rotation + + drive_supported = disc_get_drive_support_status (d); + fprintf (stderr, + "\n" + "Drive information:\n" + "----------------------------------------------------------------------\n" + "Drive model........: %s\n" + "Supported..........: %s\n", disc_get_drive_model_string (d), drive_supported ? "Yes" : "No" + ); + + init_range(d, options.sec_disc, options.sec_mem); + + if (!(disc_set_read_method (d, options.dump_method))) + exit (2); + + if (options.command!=-1) fprintf (stderr, + "Command............: %d (forced)\n", disc_get_command(d)); + else fprintf (stderr, + "Command............: %d\n", disc_get_command(d)); + options.dump_method=disc_get_method(d); + if (disc_get_def_method(d)!=options.dump_method) fprintf (stderr, + "Method.............: %d (forced)\n", options.dump_method); + else fprintf (stderr, + "Method.............: %d\n", options.dump_method); + if ((options.dump_method==0) + || (options.dump_method==1) || (options.dump_method==2) || (options.dump_method==3) + || (options.dump_method==4) || (options.dump_method==5) || (options.dump_method==6) + ){ + fprintf (stderr, + "Requested sectors..: %d\n", disc_get_sec_disc(d)); + fprintf (stderr, + "Expected sectors...: %d\n", disc_get_sec_mem(d)); + } + + fprintf (stderr, "\nPress Ctrl+C at any time to terminate\n"); + fprintf (stderr, "\nRetrieving disc seeds, this might take a while... "); + + //set speed for 1st time + if (options.speed != -1) disc_set_speed(d, options.speed * 177); + if (options.speed != -1) disc_set_streaming_speed(d, options.speed * 177); +// disc_set_speed(d, 0xffff); + + if (!disc_init (d, options.disctype, options.sectors_no)) { + fprintf (stderr, "Failed\n"); + out = false; + } else { + fprintf (stderr, "OK\n"); + disc_get_type (d, &type_id, &type); + disc_get_gameid (d, &game_id); + disc_get_region (d, NULL, ®ion); + disc_get_maker (d, &maker_id, &maker); + disc_get_version (d, NULL, &version); + disc_get_title (d, &title); + fprintf (stderr, + "\n" + "Disc information:\n" + "----------------------------------------------------------------------\n"); + + if (options.disctype!=-1) fprintf (stderr, + "Disc type..........: %s (forced)\n", type); + else fprintf (stderr, + "Disc type..........: %s\n", type); + if (options.sectors_no!=-1) fprintf (stderr, + "Disc size..........: %d (forced)\n", disc_get_sectors_no(d)); + else fprintf (stderr, + "Disc size..........: %d\n", disc_get_sectors_no(d)); + + if (disc_get_layerbreak(d)>0 && type_id==DISC_TYPE_DVD) fprintf (stderr, + "Layer break........: %d\n", disc_get_layerbreak(d)); + + if ((type_id==DISC_TYPE_GAMECUBE) || (type_id==DISC_TYPE_WII) || (type_id==DISC_TYPE_WII_DL)) fprintf (stderr, + "Game ID............: %s\n" + "Region.............: %s\n" + "Maker..............: %s - %s\n" + "Version............: %s\n" + "Game title.........: %s\n", game_id, region, maker_id, maker, version, title + ); + + if (type_id == DISC_TYPE_WII || type_id == DISC_TYPE_WII_DL) + fprintf (stderr, "Contains update....: %s\n" , disc_get_update (d) ? "Yes" : "No"); + fprintf (stderr, "\n"); + + disc_set_unscrambling (d, !options.no_unscrambling); + + unscrambler_set_disctype (type_id); + + if (options.autodump) { + snprintf (tmp, sizeof (tmp), "%s.iso", title); + my_strdup (options.iso_out, tmp); + } + + //set speed 2nd time after rotation is started and some sectors read + if (options.speed != -1) disc_set_streaming_speed(d, options.speed * 177); + if (options.speed != -1) disc_set_speed(d, options.speed * 177); + + /* If at least an output file was specified, proceed dumping, otherwise stop here */ + if (options.raw_out || options.iso_out) { + if (options.raw_out) + fprintf (stderr, "Writing to file \"%s\" in raw format\n", options.raw_out); + if (options.iso_out) + fprintf (stderr, "Writing to file \"%s\" in ISO format\n", options.iso_out); + fprintf (stderr, "\n"); + + dmp = dumper_new (d); + + dumper_set_hashing (dmp, !options.no_hashing); + dumper_set_flushing (dmp, !options.no_flushing); + + if (!dumper_set_raw_output_file (dmp, options.raw_out, options.resume)) { + fprintf (stderr, "Cannot setup raw output file\n"); + } else if (!dumper_set_iso_output_file (dmp, options.iso_out, options.resume)) { + fprintf (stderr, "Cannot setup ISO output file\n"); + } else if (!dumper_prepare (dmp)) { + fprintf (stderr, "Cannot prepare dumper"); + } else { +// fprintf (stderr, "Starting dump process from sector %u...\n", dmp -> start_sector); +// opdd.start_sector = options.start_sector; + + if (options.gui) + dumper_set_progress_callback (dmp, (progress_func) progress_for_guis, &stats); + else + dumper_set_progress_callback (dmp, (progress_func) progress, &stats); + + if (dumper_dump (dmp, ¤t_sector)) { + fprintf (stderr, "Dump completed successfully!\n"); + if (!options.no_hashing && options.raw_out) + fprintf (stderr, + "Raw image hashes:\n" + "CRC32...: %s\n" + //"MD4.....: %s\n" + "MD5.....: %s\n" + "SHA-1...: %s\n" + /*"ED2K....: %s\n"*/, + dumper_get_raw_crc32 (dmp), /*dumper_get_raw_md4 (dmp),*/ dumper_get_raw_md5 (dmp), + dumper_get_raw_sha1 (dmp)/*, dumper_get_raw_ed2k (dmp)*/ + ); + if (!options.no_hashing && options.iso_out) + fprintf (stderr, + "ISO image hashes:\n" + "CRC32...: %s\n" + //"MD4.....: %s\n" + "MD5.....: %s\n" + "SHA-1...: %s\n" + /*"ED2K....: %s\n"*/, + dumper_get_iso_crc32 (dmp), /*dumper_get_iso_md4 (dmp),*/ dumper_get_iso_md5 (dmp), + dumper_get_iso_sha1 (dmp)/*, dumper_get_iso_ed2k (dmp)*/ + ); + + out = true; + disc_stop_unit (d, 0); + } else { + fprintf (stderr, "\nDump failed at sectors: %u..%u\n", current_sector, current_sector+15); + out = false; + //disc_stop_unit (d, 0); + } + } + + dmp = dumper_destroy (dmp); + } else { + fprintf (stderr, "No output file for dumping specified, please take a look at the -i, -r and -a options\n"); + } + } + return out; +} + +int main (int argc, char *argv[]) { + disc *d; + progstats stats; + double duration; + suseconds_t us; + int out, ret; + unscrambler *u; + unscrambler_progress_func pfunc; + u_int32_t current_sector; + + /* First of all... */ + drop_euid (); + + welcome (); + + d = NULL; + out = false; + if (optparse (argc, argv)) { + if (options.device) { + /* Dump DVD to file */ + fprintf (stderr, "Initializing DVD drive... "); + + if (!(d = disc_new (options.device, options.command))) { + fprintf (stderr, "Failed\n"); +#ifndef WIN32 + fprintf (stderr, + "Probably you do not have access to the DVD device. Ask the administrator\n" + "to add you to the proper group, or use 'sudo'.\n" + ); +#endif + } else { + fprintf (stderr, "OK\n"); + + if(options.allmethods) + { + fprintf (stderr, "Trying all methods... This will take a LOOOONG time and generate an insanely long console output :p\n"); + for(options.command=0;options.command<=4;options.command++) + { + for(options.dump_method=0;options.dump_method<=9;options.dump_method++) + { + fprintf (stderr, "Trying with command %d, method %d\n", options.command, options.dump_method); + out = dologic(d, stats); + if(out==true) + { + break; + } + } + if(out==true) + { + fprintf (stderr, "Command %d and method %d combination worked!\n", options.command, options.dump_method); + break; + } + } + } else { + out = dologic(d, stats); + } + + d = disc_destroy (d); + } + } else if (options.raw_in) { + /* Convert raw image to ISO format */ + u = unscrambler_new (); + + if (options.gui) + pfunc = (unscrambler_progress_func) progress_for_guis; + else + pfunc = (unscrambler_progress_func) progress; + + if ((out = unscrambler_unscramble_file (u, options.raw_in, options.iso_out, pfunc, &stats, ¤t_sector))) + fprintf (stderr, "Unscrambling completed successfully!\n"); + else + fprintf (stderr, "\nUnscrambling failed at sectors: %u..%u\n", current_sector, current_sector+15); + + u = unscrambler_destroy (u); + } else { + MY_ASSERT (0); + } + + if (out) { + duration = stats.end_time.tv_sec - stats.start_time.tv_sec; + if (stats.end_time.tv_usec >= stats.start_time.tv_usec) { + us = stats.end_time.tv_usec - stats.start_time.tv_usec; + } else { + if (duration > 0) + duration--; + us = USECS_PER_SEC + stats.end_time.tv_usec - stats.start_time.tv_usec; + } + duration += ((double) us / (double) USECS_PER_SEC); + if (duration < 0) + duration = 0; + fprintf (stderr, "Operation took %.2f seconds\n", duration); + + ret = EXIT_SUCCESS; + } else { + ret = EXIT_FAILURE; + } + + my_free (options.device); + my_free (options.iso_out); + my_free (options.raw_out); + my_free (options.raw_in); + } + + return (out); +} diff --git a/src/getopt-win32.c b/src/getopt-win32.c new file mode 100644 index 0000000..1380eaa --- /dev/null +++ b/src/getopt-win32.c @@ -0,0 +1,130 @@ +/* $Id$ */ +/* +** Copyright (C) 2002 Martin Roesch +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License Version 2 as +** published by the Free Software Foundation. You may not use, modify or +** distribute this program under any other version of the GNU General +** Public License. +** +** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef SNORT_GETOPT + +#include /* for EOF */ +#include /* for strchr() */ + +#include "getopt.h" + +/* static (global) variables that are specified as exported by getopt() */ +char *optarg = NULL; /* pointer to the start of the option argument */ +int optind = 1; /* number of the next argv[] to be evaluated */ +int opterr = 1; /* non-zero if a question mark should be returned + when a non-valid option character is detected */ +int optopt; + +int getopt(int argc, char *argv[], char *opstring) +{ + static char *pIndexPosition = NULL; /* place inside current argv string */ + char *pArgString = NULL; /* where to start from next */ + char *pOptString; /* the string in our program */ + + + if (pIndexPosition != NULL) { + /* we last left off inside an argv string */ + if (*(++pIndexPosition)) { + /* there is more to come in the most recent argv */ + pArgString = pIndexPosition; + } + } + + if (pArgString == NULL) { + /* we didn't leave off in the middle of an argv string */ + if (optind >= argc) { + /* more command-line arguments than the argument count */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all command-line arguments */ + } + + /*--------------------------------------------------------------------- + * If the next argv[] is not an option, there can be no more options. + *-------------------------------------------------------------------*/ + pArgString = argv[optind++]; /* set this to the next argument ptr */ + + if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */ + ('-' != *pArgString)) { + --optind; /* point to current arg once we're done */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all the command-line flags */ + } + + /* check for special end-of-flags markers */ + if ((strcmp(pArgString, "-") == 0) || + (strcmp(pArgString, "--") == 0)) { + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* encountered the special flag */ + } + + pArgString++; /* look past the / or - */ + } + + if (':' == *pArgString) { /* is it a colon? */ + /*--------------------------------------------------------------------- + * Rare case: if opterr is non-zero, return a question mark; + * otherwise, just return the colon we're on. + *-------------------------------------------------------------------*/ + return (opterr ? (int)'?' : (int)':'); + } + else if ((pOptString = strchr(opstring, *pArgString)) == 0) { + /*--------------------------------------------------------------------- + * The letter on the command-line wasn't any good. + *-------------------------------------------------------------------*/ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return (opterr ? (int)'?' : (int)*pArgString); + } + else { + /*--------------------------------------------------------------------- + * The letter on the command-line matches one we expect to see + *-------------------------------------------------------------------*/ + if (':' == _next_char(pOptString)) { /* is the next letter a colon? */ + /* It is a colon. Look for an argument string. */ + if ('\0' != _next_char(pArgString)) { /* argument in this argv? */ + optarg = &pArgString[1]; /* Yes, it is */ + } + else { + /*------------------------------------------------------------- + * The argument string must be in the next argv. + * But, what if there is none (bad input from the user)? + * In that case, return the letter, and optarg as NULL. + *-----------------------------------------------------------*/ + if (optind < argc) + optarg = argv[optind++]; + else { + optarg = NULL; + return (opterr ? (int)'?' : (int)*pArgString); + } + } + + pIndexPosition = NULL; /* not in the middle of anything */ + } + else { + /* it's not a colon, so just return the letter */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = pArgString; /* point to the letter we're on */ + } + return (int)*pArgString; /* return the letter that matched */ + } +} + +#endif diff --git a/src/getopt-win32.h b/src/getopt-win32.h new file mode 100644 index 0000000..e08af95 --- /dev/null +++ b/src/getopt-win32.h @@ -0,0 +1,36 @@ +/* $Id$ */ +/* +** Copyright (C) 2002 Martin Roesch +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License Version 2 as +** published by the Free Software Foundation. You may not use, modify or +** distribute this program under any other version of the GNU General +** Public License. +** +** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _SNORT_GETOPT_H_ +#define _SNORT_GETOPT_H_ + +#ifdef SNORT_GETOPT +#define _next_char(string) (char)(*(string+1)) + +extern char * optarg; +extern int optind; + +int getopt(int, char**, char*); + +#else +#include "getopt1.h" +#endif + +#endif /* _SNORT_GETOPT_H_ */ diff --git a/src/getopt1.h b/src/getopt1.h new file mode 100644 index 0000000..7c2552a --- /dev/null +++ b/src/getopt1.h @@ -0,0 +1,130 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + it under the terms of the GNU General Public License Version 2 as + published by the Free Software Foundation. You may not use, modify or + distribute this program under any other version of the GNU General + Public License. + + 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. */ + +#ifndef _GETOPT1_H +#define _GETOPT1_H 1 + +#ifndef HAVE_GETOPT_LONG + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if __STDC__ + +#ifndef HAVE_GETOPT +extern int getopt (int argc, char *const *argv, const char *optstring); +#endif + +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ + +#ifndef HAVE_GETOPT +extern int getopt (); +#endif +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_GETOPT_LONG */ + +#endif /* _GETOPT_H */ diff --git a/src/getopt_long-win32.c b/src/getopt_long-win32.c new file mode 100644 index 0000000..991363e --- /dev/null +++ b/src/getopt_long-win32.c @@ -0,0 +1,727 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + it under the terms of the GNU General Public License Version 2 as + published by the Free Software Foundation. You may not use, modify or + distribute this program under any other version of the GNU General + Public License. + + 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. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#if defined (emacs) || defined (CONFIG_BROKETS) +/* We use instead of "config.h" so that a compilation + using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h + (which it would do because it found this file in $srcdir). */ +#include +#else +#include "config.h" +#endif +#endif + + +#ifndef __STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + + +#ifdef HAVE_STRING_H +#include +#endif + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt1.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#if !defined(_WIN32) +char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#ifndef __STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (optstring) + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0) + optstring = _getopt_initialize (optstring); + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0')) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if (nameend - nextchar == (int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); + else + fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + it under the terms of the GNU General Public License Version 2 as + published by the Free Software Foundation. You may not use, modify or + distribute this program under any other version of the GNU General + Public License. + + 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. */ + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/templates/c b/templates/c new file mode 100644 index 0000000..c5c13d3 --- /dev/null +++ b/templates/c @@ -0,0 +1,20 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://www.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ diff --git a/templates/h b/templates/h new file mode 100644 index 0000000..c5c13d3 --- /dev/null +++ b/templates/h @@ -0,0 +1,20 @@ +/*************************************************************************** + * Copyright (C) 2007 by Arep * + * Support is provided through the forums at * + * http://www.console-tribe.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/