├── .gitignore ├── released_version_is_2.7.1 ├── CMakeLists.txt ├── win32 ├── asprintf.c ├── dirent.c └── getopt.c ├── README.md ├── LICENSE.TXT ├── .github └── workflows │ └── CI.yml └── extract-xiso.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /released_version_is_2.7.1: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(extract-xiso) 3 | 4 | string(TOUPPER __${CMAKE_SYSTEM_NAME}__ TARGET_OS) 5 | 6 | set(SOURCE_FILES 7 | extract-xiso.c 8 | ) 9 | 10 | add_executable(extract-xiso ${SOURCE_FILES}) 11 | target_compile_definitions(extract-xiso PRIVATE ${TARGET_OS}) 12 | install(TARGETS extract-xiso RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 13 | -------------------------------------------------------------------------------- /win32/asprintf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2001 Federico Di Gregorio 3 | * Copyright (C) 1991, 1994-1999, 2000, 2001 Free Software Foundation, Inc. 4 | * 5 | * This code has been derived from an example in the glibc2 documentation. 6 | * This file is part of the psycopg module. 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License 10 | * as published by the Free Software Foundation; either version 2, 11 | * or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | /* 24 | * asprintf.c -- asprintf() implementation for braindamaged operating systems 25 | */ 26 | 27 | #ifndef _WIN32 28 | #include 29 | #endif 30 | #include 31 | #include 32 | #ifndef _WIN32 33 | #include 34 | #endif 35 | #include 36 | 37 | #ifdef _WIN32 38 | #define vsnprintf _vsnprintf 39 | #endif 40 | 41 | int asprintf(char **buffer, char *fmt, ...); 42 | int asprintf(char **buffer, char *fmt, ...) 43 | { 44 | /* Guess we need no more than 200 chars of space. */ 45 | int size = 200; 46 | int nchars; 47 | va_list ap; 48 | 49 | *buffer = (char*)malloc(size); 50 | if (*buffer == NULL) return -1; 51 | 52 | /* Try to print in the allocated space. */ 53 | va_start(ap, fmt); 54 | nchars = vsnprintf(*buffer, size, fmt, ap); 55 | va_end(ap); 56 | 57 | if (nchars >= size) 58 | { 59 | char *tmpbuff; 60 | /* Reallocate buffer now that we know how much space is needed. */ 61 | size = nchars+1; 62 | tmpbuff = (char*)realloc(*buffer, size); 63 | 64 | 65 | if (tmpbuff == NULL) { /* we need to free it*/ 66 | free(*buffer); 67 | return -1; 68 | } 69 | 70 | *buffer=tmpbuff; 71 | /* Try again. */ 72 | va_start(ap, fmt); 73 | nchars = vsnprintf(*buffer, size, fmt, ap); 74 | va_end(ap); 75 | } 76 | 77 | if (nchars < 0) return nchars; 78 | return size; 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # extract-xiso 2 | 3 | A command line utility created by [*in*](mailto:in@fishtank.com) to allow the creation, modification, and extraction of XISOs. Currently being maintained and modernized by the [*XboxDev organization*](https://github.com/XboxDev/XboxDev). 4 | 5 | ## Features 6 | 7 | - Create XISOs from a directory. 8 | 9 | - Extract XISO content to a directory. 10 | 11 | - Multi-Platform and Open-Source. 12 | 13 | ## Usage 14 | 15 | The `extract-xiso` utility can run in multiple modes: *create*, *list*, *rewrite*, and *extract*. 16 | 17 | ### Create `-c` 18 | 19 | Create an XISO from a directiory. 20 | ``` 21 | # Create halo-2.iso in the current directory containing the files within ./halo-2.iso 22 | ./extract-xiso -c ./halo-2 23 | 24 | # Create halo-ce.iso in the /home/me/games directory containing files in the ./halo-ce directory 25 | ./extract-xiso -c ./halo-ce /home/me/games/halo-ce.iso 26 | ``` 27 | 28 | ### List `-l` 29 | 30 | List the file contents within an XISO file. 31 | ``` 32 | # Get file contents of a XISO 33 | ./extract-xiso -l ./halo-ce.iso 34 | 35 | # List file contents of multiple XISOs 36 | ./extract-xiso -l ./halo-2.iso ./halo-ce.iso 37 | ``` 38 | 39 | ### Rewrite `-r` 40 | 41 | Rewrites filesystem structure of an XISO. 42 | ``` 43 | # Rewrites XISO 44 | ./extract-xiso -r ./halo-ce.iso 45 | # Can be batched 46 | ./extract-xiso -r ./halo-ce.iso ./halo-2.iso 47 | ``` 48 | 49 | ### Extract `-x` 50 | 51 | Extract XISO contents to a directory. 52 | ``` 53 | # Default mode when no arguments given, extracts to ./halo-ce/ 54 | ./extract-xiso ./halo-ce.iso 55 | 56 | # Can be given a target directory 57 | ./extract-xiso ./halo-2.iso -d /home/games/halo-2/ 58 | ``` 59 | 60 | ### Options 61 | 62 | `extract-xiso` has a few optional arguments that can be provided in different modes: 63 | ``` 64 | -d In extract mode, expand xiso in . 65 | In rewrite mode, rewrite xiso in . 66 | -D In rewrite mode, delete old xiso after processing. 67 | -h Print this help text and exit. 68 | -m In create or rewrite mode, disable automatic .xbe 69 | media enable patching (not recommended). 70 | -q Run quiet (suppress all non-error output). 71 | -Q Run silent (suppress all output). 72 | -s Skip $SystemUpdate folder. 73 | -v Print version information and exit. 74 | ``` 75 | 76 | ## Building 77 | 78 | ### Requirements 79 | 80 | - cmake 81 | - make 82 | - gcc 83 | 84 | ### Windows / macOS / Linux 85 | 86 | After requirements are installed with your distribution's package manager (or homebrew for macOS), open terminal and change directory to the project root. Then run the following build commands: 87 | 88 | ``` 89 | # Clone Repo 90 | git clone https://github.com/XboxDev/extract-xiso.git 91 | 92 | # cd into directory 93 | cd extract-xiso 94 | 95 | # Create working directory 96 | mkdir build 97 | cd build 98 | 99 | # Build project 100 | cmake .. 101 | make 102 | ``` 103 | 104 | The compiled binary should now be in the `extract-xiso/build` directory as `extract-xiso`. 105 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | This is an xdvdfs (xbox iso) file creation/extraction utility for linux/darwin/ 2 | freebsd. 3 | 4 | If you want to port it to your OS it shouldn't be too hard, just make sure your 5 | fseek/ftell takes 64-bit file offsets. 6 | 7 | I wrote this code completely from scratch today because xbiso (the one on 8 | sourceforge) keeps seg-faulting and the code is buggy. I got useful 9 | documentation on the xdvdfs file system from http://xbox-linux.sourceforge.net. 10 | 11 | I will say thanks though to the author of xbiso, it was a good start and got me 12 | thinking! 13 | 14 | 15 | Regarding licensing: 16 | 17 | I think the GPL sucks! (it stands for Generosity Poor License) 18 | 19 | My open-source code is really *FREE* so you can do whatever you want with it, as 20 | long as 1) you don't claim that you wrote my code and 2) you retain a notice 21 | that some parts of the code are copyright in@fishtank.com and 3) you understand 22 | there are no warranties. I only guarantee that it will take up disk space! 23 | 24 | If you want to help out with this project it would be welcome, just email me at 25 | in@fishtank.com. 26 | 27 | This code is copyright in@fishtank.com and is licensed under a slightly 28 | modifified version of the Berkeley Software License, which follows: 29 | 30 | /* 31 | * Copyright (c) 2003 in 32 | * All rights reserved. 33 | * 34 | * Redistribution and use in source and binary forms, with or without 35 | * modification, are permitted provided that the following conditions 36 | * are met: 37 | * 38 | * 1. Redistributions of source code must retain the above copyright 39 | * notice, this list of conditions and the following disclaimer. 40 | * 41 | * 2. Redistributions in binary form must reproduce the above copyright 42 | * notice, this list of conditions and the following disclaimer in the 43 | * documentation and/or other materials provided with the distribution. 44 | * 45 | * 3. All advertising materials mentioning features or use of this software 46 | * must display the following acknowledgement: 47 | * 48 | * This product includes software developed by in . 49 | * 50 | * 4. Neither the name of "in" nor the email address "in@fishtank.com" 51 | * may be used to endorse or promote products derived from this software 52 | * without specific prior written permission. 53 | * 54 | * THIS SOFTWARE IS PROVIDED `AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES 55 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 56 | * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 57 | * AUTHOR OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 58 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 59 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 60 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 61 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 62 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 63 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 64 | */ 65 | 66 | Please send bug reports to me at in@fishtank.com 67 | 68 | To compile, run 'make' in the source directory. 69 | 70 | NOTE: You need to be running GNU make and /bin/bash must be executable 71 | 72 | Enjoy! 73 | 74 | in version 1.0 75 | March 10, 2003 76 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - '*.md' 9 | - '*.yml' 10 | pull_request: 11 | paths-ignore: 12 | - '*.md' 13 | - '*.yml' 14 | 15 | jobs: 16 | Init: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Create build tag 20 | run: | 21 | export BUILD_TAG=build-$(date -u +'%Y%m%d%H%M') 22 | echo "BUILD_TAG=$BUILD_TAG" >> $GITHUB_ENV 23 | echo -n $BUILD_TAG > tag 24 | - name: Upload artifacts 25 | uses: actions/upload-artifact@v4 26 | with: 27 | name: tag 28 | path: tag 29 | 30 | build-windows: 31 | runs-on: windows-latest 32 | needs: Init 33 | env: 34 | POWERSHELL_TELEMETRY_OPTOUT: 1 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | configuration: [Release, Debug] 39 | arch: [x86, x64] 40 | include: 41 | - arch: x86 42 | platform: Win32 43 | artifact_os: Win32 44 | - arch: x64 45 | platform: x64 46 | artifact_os: Win64 47 | steps: 48 | - uses: actions/checkout@v2 49 | - name: CMake generate 50 | run: | 51 | mkdir build && cd build 52 | cmake .. -A ${{ matrix.platform }} 53 | - name: Build 54 | working-directory: build 55 | run: cmake --build . --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS 56 | - name: Prepare artifacts 57 | run: | 58 | mkdir artifacts 59 | mv -vb build\${{ matrix.configuration }}\extract-xiso.exe, LICENSE.TXT artifacts 60 | cd artifacts 61 | powershell Compress-Archive . ../extract-xiso-${{ matrix.artifact_os }}_${{ matrix.configuration }}.zip 62 | - uses: actions/upload-artifact@v4 63 | with: 64 | name: extract-xiso_${{ matrix.artifact_os }}_${{ matrix.configuration }} 65 | path: extract-xiso-${{ matrix.artifact_os }}_${{ matrix.configuration }}.zip 66 | 67 | build-linux: 68 | runs-on: ubuntu-latest 69 | needs: Init 70 | steps: 71 | - uses: actions/checkout@v2 72 | - name: CMake generate 73 | run: | 74 | mkdir build && cd build 75 | cmake .. 76 | - name: Build 77 | working-directory: build 78 | run: cmake --build . -j $(nproc --all) 79 | - name: Prepare artifacts 80 | run: | 81 | mkdir artifacts 82 | mv -v build/extract-xiso LICENSE.TXT artifacts 83 | cd artifacts 84 | zip -r ../extract-xiso_${{ runner.os }}.zip * 85 | - uses: actions/upload-artifact@v4 86 | with: 87 | name: extract-xiso_${{ runner.os }} 88 | path: extract-xiso_${{ runner.os }}.zip 89 | 90 | build-macos: 91 | runs-on: macos-latest 92 | needs: Init 93 | steps: 94 | - uses: actions/checkout@v2 95 | - name: CMake generate 96 | run: | 97 | mkdir build && cd build 98 | cmake .. 99 | - name: Build 100 | working-directory: build 101 | run: cmake --build . -j $(sysctl -n hw.ncpu) 102 | - name: Prepare artifacts 103 | run: | 104 | mkdir artifacts 105 | mv -v build/extract-xiso LICENSE.TXT artifacts 106 | cd artifacts 107 | zip -r ../extract-xiso_${{ runner.os }}.zip * 108 | - uses: actions/upload-artifact@v4 109 | with: 110 | name: extract-xiso_${{ runner.os }} 111 | path: extract-xiso_${{ runner.os }}.zip 112 | 113 | Release: 114 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 115 | runs-on: ubuntu-latest 116 | needs: [build-windows, build-linux, build-macos] 117 | steps: 118 | - name: Download artifacts 119 | uses: actions/download-artifact@v4 120 | with: 121 | path: dist 122 | - name: Get package info 123 | run: | 124 | echo "BUILD_TAG=$(cat dist/tag/tag)" >> $GITHUB_ENV 125 | - name: Create release 126 | uses: softprops/action-gh-release@v2 127 | with: 128 | tag_name: ${{ env.BUILD_TAG }} 129 | name: ${{ env.BUILD_TAG }} 130 | prerelease: false 131 | draft: false 132 | files: dist/*/*.zip 133 | -------------------------------------------------------------------------------- /win32/dirent.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Implementation of POSIX directory browsing functions and types for Win32. 4 | 5 | Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) 6 | History: Created March 1997. Updated June 2003. 7 | Rights: See end of file. 8 | 9 | */ 10 | 11 | #ifndef DIRENT_INCLUDED 12 | #define DIRENT_INCLUDED 13 | 14 | /* 15 | 16 | Declaration of POSIX directory browsing functions and types for Win32. 17 | 18 | Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) 19 | History: Created March 1997. Updated June 2003. 20 | Rights: See end of file. 21 | 22 | */ 23 | 24 | #ifdef __cplusplus 25 | extern "C" 26 | { 27 | #endif 28 | 29 | typedef struct DIR DIR; 30 | 31 | struct dirent 32 | { 33 | char *d_name; 34 | }; 35 | 36 | DIR *opendir(const char *); 37 | int closedir(DIR *); 38 | struct dirent *readdir(DIR *); 39 | void rewinddir(DIR *); 40 | 41 | /* 42 | 43 | Copyright Kevlin Henney, 1997, 2003. All rights reserved. 44 | 45 | Permission to use, copy, modify, and distribute this software and its 46 | documentation for any purpose is hereby granted without fee, provided 47 | that this copyright and permissions notice appear in all copies and 48 | derivatives. 49 | 50 | This software is supplied "as is" without express or implied warranty. 51 | 52 | But that said, if there are any problems please get in touch. 53 | 54 | */ 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | 60 | #endif 61 | 62 | #include 63 | #include /* _findfirst and _findnext set errno iff they return -1 */ 64 | #include 65 | #include 66 | 67 | #ifdef __cplusplus 68 | extern "C" 69 | { 70 | #endif 71 | 72 | struct DIR 73 | { 74 | intptr_t handle; /* -1 for failed rewind */ 75 | struct _finddata_t info; 76 | struct dirent result; /* d_name null iff first time */ 77 | char *name; /* null-terminated char string */ 78 | }; 79 | 80 | DIR *opendir(const char *name) 81 | { 82 | DIR *dir = 0; 83 | 84 | if(name && name[0]) 85 | { 86 | size_t base_length = strlen(name); 87 | const char *all = /* search pattern must end with suitable wildcard */ 88 | strchr("/\\", name[base_length - 1]) ? "*" : "/*"; 89 | 90 | if((dir = (DIR *) malloc(sizeof *dir)) != 0 && 91 | (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) 92 | { 93 | strcat(strcpy(dir->name, name), all); 94 | 95 | if((dir->handle = _findfirst(dir->name, &dir->info)) != -1) 96 | { 97 | dir->result.d_name = 0; 98 | } 99 | else /* rollback */ 100 | { 101 | free(dir->name); 102 | free(dir); 103 | dir = 0; 104 | } 105 | } 106 | else /* rollback */ 107 | { 108 | free(dir); 109 | dir = 0; 110 | errno = ENOMEM; 111 | } 112 | } 113 | else 114 | { 115 | errno = EINVAL; 116 | } 117 | 118 | return dir; 119 | } 120 | 121 | int closedir(DIR *dir) 122 | { 123 | int result = -1; 124 | 125 | if(dir) 126 | { 127 | if(dir->handle != -1) 128 | { 129 | result = _findclose(dir->handle); 130 | } 131 | 132 | free(dir->name); 133 | free(dir); 134 | } 135 | 136 | if(result == -1) /* map all errors to EBADF */ 137 | { 138 | errno = EBADF; 139 | } 140 | 141 | return result; 142 | } 143 | 144 | struct dirent *readdir(DIR *dir) 145 | { 146 | struct dirent *result = 0; 147 | 148 | if(dir && dir->handle != -1) 149 | { 150 | if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) 151 | { 152 | result = &dir->result; 153 | result->d_name = dir->info.name; 154 | } 155 | } 156 | else 157 | { 158 | errno = EBADF; 159 | } 160 | 161 | return result; 162 | } 163 | 164 | void rewinddir(DIR *dir) 165 | { 166 | if(dir && dir->handle != -1) 167 | { 168 | _findclose(dir->handle); 169 | dir->handle = _findfirst(dir->name, &dir->info); 170 | dir->result.d_name = 0; 171 | } 172 | else 173 | { 174 | errno = EBADF; 175 | } 176 | } 177 | 178 | #ifdef __cplusplus 179 | } 180 | #endif 181 | 182 | /* 183 | 184 | Copyright Kevlin Henney, 1997, 2003. All rights reserved. 185 | 186 | Permission to use, copy, modify, and distribute this software and its 187 | documentation for any purpose is hereby granted without fee, provided 188 | that this copyright and permissions notice appear in all copies and 189 | derivatives. 190 | 191 | This software is supplied "as is" without express or implied warranty. 192 | 193 | But that said, if there are any problems please get in touch. 194 | 195 | */ 196 | -------------------------------------------------------------------------------- /win32/getopt.c: -------------------------------------------------------------------------------- 1 | /* ==================================================================== 2 | * The Apache Software License, Version 1.1 3 | * 4 | * Copyright (c) 2000-2003 The Apache Software Foundation. All rights 5 | * reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. The end-user documentation included with the redistribution, 20 | * if any, must include the following acknowledgment: 21 | * "This product includes software developed by the 22 | * Apache Software Foundation (http://www.apache.org/)." 23 | * Alternately, this acknowledgment may appear in the software itself, 24 | * if and wherever such third-party acknowledgments normally appear. 25 | * 26 | * 4. The names "Apache" and "Apache Software Foundation" must 27 | * not be used to endorse or promote products derived from this 28 | * software without prior written permission. For written 29 | * permission, please contact apache@apache.org. 30 | * 31 | * 5. Products derived from this software may not be called "Apache", 32 | * nor may "Apache" appear in their name, without prior written 33 | * permission of the Apache Software Foundation. 34 | * 35 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 36 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 37 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 38 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 39 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 42 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 43 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 44 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 45 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 46 | * SUCH DAMAGE. 47 | * ==================================================================== 48 | * 49 | * This software consists of voluntary contributions made by many 50 | * individuals on behalf of the Apache Software Foundation. For more 51 | * information on the Apache Software Foundation, please see 52 | * . 53 | * 54 | * Portions of this software are based upon public domain software 55 | * originally written at the National Center for Supercomputing Applications, 56 | * University of Illinois, Urbana-Champaign. 57 | */ 58 | 59 | #ifdef WIN32 60 | 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #define OPTERRCOLON (1) 67 | #define OPTERRNF (2) 68 | #define OPTERRARG (3) 69 | 70 | char *optarg; 71 | int optreset = 0; 72 | int optind = 1; 73 | int opterr = 1; 74 | int optopt; 75 | 76 | static int 77 | optiserr(int argc, char * const *argv, int oint, const char *optstr, 78 | int optchr, int err) 79 | { 80 | argc; 81 | optstr; 82 | 83 | if(opterr) 84 | { 85 | fprintf(stderr, "Error in argument %d, char %d: ", oint, optchr+1); 86 | switch(err) 87 | { 88 | case OPTERRCOLON: 89 | fprintf(stderr, ": in flags\n"); 90 | break; 91 | case OPTERRNF: 92 | fprintf(stderr, "option not found %c\n", argv[oint][optchr]); 93 | break; 94 | case OPTERRARG: 95 | fprintf(stderr, "no argument for option %c\n", argv[oint][optchr]); 96 | break; 97 | default: 98 | fprintf(stderr, "unknown\n"); 99 | break; 100 | } 101 | } 102 | optopt = argv[oint][optchr]; 103 | return('?'); 104 | } 105 | 106 | 107 | int getopt(int argc, char* const *argv, const char *optstr); 108 | int getopt(int argc, char* const *argv, const char *optstr) 109 | { 110 | static int optchr = 0; 111 | static int dash = 0; /* have already seen the - */ 112 | 113 | char *cp; 114 | 115 | if (optreset) 116 | optreset = optchr = dash = 0; 117 | if(optind >= argc) 118 | return(EOF); 119 | if(!dash && (argv[optind][0] != '-')) 120 | return(EOF); 121 | if(!dash && (argv[optind][0] == '-') && !argv[optind][1]) 122 | { 123 | /* 124 | * use to specify stdin. Need to let pgm process this and 125 | * the following args 126 | */ 127 | return(EOF); 128 | } 129 | if((argv[optind][0] == '-') && (argv[optind][1] == '-')) 130 | { 131 | /* -- indicates end of args */ 132 | optind++; 133 | return(EOF); 134 | } 135 | if(!dash) 136 | { 137 | assert((argv[optind][0] == '-') && argv[optind][1]); 138 | dash = 1; 139 | optchr = 1; 140 | } 141 | 142 | /* Check if the guy tries to do a -: kind of flag */ 143 | assert(dash); 144 | if(argv[optind][optchr] == ':') 145 | { 146 | dash = 0; 147 | optind++; 148 | return(optiserr(argc, argv, optind-1, optstr, optchr, OPTERRCOLON)); 149 | } 150 | if(!(cp = strchr(optstr, argv[optind][optchr]))) 151 | { 152 | int errind = optind; 153 | int errchr = optchr; 154 | 155 | if(!argv[optind][optchr+1]) 156 | { 157 | dash = 0; 158 | optind++; 159 | } 160 | else 161 | optchr++; 162 | return(optiserr(argc, argv, errind, optstr, errchr, OPTERRNF)); 163 | } 164 | if(cp[1] == ':') 165 | { 166 | dash = 0; 167 | optind++; 168 | if(optind == argc) 169 | return(optiserr(argc, argv, optind-1, optstr, optchr, OPTERRARG)); 170 | optarg = argv[optind++]; 171 | return(*cp); 172 | } 173 | else 174 | { 175 | if(!argv[optind][optchr+1]) 176 | { 177 | dash = 0; 178 | optind++; 179 | } 180 | else 181 | optchr++; 182 | return(*cp); 183 | } 184 | assert(0); 185 | return(0); 186 | } 187 | 188 | #ifdef TESTGETOPT 189 | int 190 | main (int argc, char **argv) 191 | { 192 | int c; 193 | extern char *optarg; 194 | extern int optind; 195 | int aflg = 0; 196 | int bflg = 0; 197 | int errflg = 0; 198 | char *ofile = NULL; 199 | 200 | while ((c = getopt(argc, argv, "abo:")) != EOF) 201 | switch (c) { 202 | case 'a': 203 | if (bflg) 204 | errflg++; 205 | else 206 | aflg++; 207 | break; 208 | case 'b': 209 | if (aflg) 210 | errflg++; 211 | else 212 | bflg++; 213 | break; 214 | case 'o': 215 | ofile = optarg; 216 | (void)printf("ofile = %s\n", ofile); 217 | break; 218 | case '?': 219 | errflg++; 220 | } 221 | if (errflg) { 222 | (void)fprintf(stderr, 223 | "usage: cmd [-a|-b] [-o ] files...\n"); 224 | exit (2); 225 | } 226 | for ( ; optind < argc; optind++) 227 | (void)printf("%s\n", argv[optind]); 228 | return 0; 229 | } 230 | 231 | #endif /* TESTGETOPT */ 232 | 233 | #endif /* WIN32 */ 234 | -------------------------------------------------------------------------------- /extract-xiso.c: -------------------------------------------------------------------------------- 1 | /* 2 | extract-xiso.c 3 | 4 | An xdvdfs .iso (xbox iso) file extraction and creation tool by in 5 | written March 10, 2003 6 | 7 | 8 | View this file with your tab stops set to 4 spaces or it it will look wacky. 9 | 10 | 11 | Regarding licensing: 12 | 13 | I think the GPL sucks! (it stands for Generosity Poor License) 14 | 15 | My open-source code is really *FREE* so you can do whatever you want with it, 16 | as long as 1) you don't claim that you wrote my code and 2) you retain a notice 17 | that some parts of the code are copyright in@fishtank.com and 3) you understand 18 | there are no warranties. I only guarantee that it will take up disk space! 19 | 20 | If you want to help out with this project it would be welcome, just email me at 21 | in@fishtank.com. 22 | 23 | This code is copyright in@fishtank.com and is licensed under a slightly modified 24 | version of the Berkeley Software License, which follows: 25 | 26 | /* 27 | * Copyright (c) 2003 in 28 | * All rights reserved. 29 | * 30 | * Redistribution and use in source and binary forms, with or without 31 | * modification, are permitted provided that the following conditions 32 | * are met: 33 | * 34 | * 1. Redistributions of source code must retain the above copyright 35 | * notice, this list of conditions and the following disclaimer. 36 | * 37 | * 2. Redistributions in binary form must reproduce the above copyright 38 | * notice, this list of conditions and the following disclaimer in the 39 | * documentation and/or other materials provided with the distribution. 40 | * 41 | * 3. All advertising materials mentioning features or use of this software 42 | * must display the following acknowledgement: 43 | * 44 | * This product includes software developed by in . 45 | * 46 | * 4. Neither the name "in" nor the email address "in@fishtank.com" 47 | * may be used to endorse or promote products derived from this software 48 | * without specific prior written permission. 49 | * 50 | * THIS SOFTWARE IS PROVIDED `AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES 51 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 52 | * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 53 | * AUTHOR OR ANY CONTRIBUTOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 54 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 55 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 56 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 57 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 58 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 59 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60 | *\ 61 | 62 | 63 | Last modified: 64 | 65 | 03.29.03 in: Fixed a path display bug, changed the tree descent algorithm 66 | and added ftp to xbox support (rev to v1.2) 67 | 68 | 04.04.03 in: Added a counter for total number of files in xiso (rev to 69 | v1.2.1) THIS VERSION NOT FOR RELEASE! 70 | 71 | 04.18.03 in: Added xoff_t typecasts for __u32 * __u32 manipulations. 72 | This fixed a bug with very large iso's where the directory 73 | table was at the end of the iso--duh! (rev to v1.3) 74 | 75 | 04.19.03 in: A user pointed out that the program is increasing its 76 | memory usage over large iso's. I've tracked this to the buffer 77 | allocation in extract_file() during traverse_xiso() 78 | recursions. As a fix I've moved the copy buffer to a static 79 | variable. Not as encapsulated as I'd like but hey, this is 80 | C after all. 81 | 82 | Also added support for FreeBSD (on Intel x86) (rev to v1.4) 83 | 84 | 04.21.03 in: It looks like whomever is making xiso creation tools out there 85 | has never heard of a binary tree and is sticking *every* 86 | directory entry off the right subnode (at least on all the iso's 87 | I've found so far). This causes extremely deep recursion for 88 | iso's with lots of files (and also causes these iso's, when 89 | burned to DVD, to behave as a linked list for file lookups, thus 90 | providing *worst case* lookup performance at all times). 91 | 92 | Not only do I find this annoying and extremely bad programming, 93 | I've noticed that it is causing sporadic stack overflows with 94 | my (formerly) otherwise good tree traversal code. 95 | 96 | In order to combat such bad implementations, I've re-implemented 97 | the traverse_xiso() routine to get rid of any non-directory 98 | recursion. Additionally, I've made a few extra tweaks to 99 | conserve even more memory. I can see now that I'm going to need 100 | to write xiso creation as well and do it right. (rev to v1.5 beta) 101 | NOT FOR RELEASE 102 | 103 | 04.22.03 in: Making some major changes... 104 | 105 | Working on the optimization algorithm, still needs some tweaks 106 | apparently. DO NOT RELEASE THIS SOURCE BUILD! 107 | 108 | NOTE: I'm building this as 1.5 beta and sending the source to 109 | Emil only, this source is not yet for release. 110 | 111 | 04.28.03 in: I've finally decided that optimizing in-place just *isn't* going 112 | to happen. The xbox is *really* picky about how its b-trees 113 | are laid out. I've noticed that it will read the directory if 114 | I lay the entries out in prefix order. Seems kind of weird to 115 | me that it would *have* to be that way but whatever. So, I guess 116 | I'll write xiso creation and then piggyback a rewrite type op 117 | on top of it. Not quite as nice since it means you need extra 118 | disk space but such is life. 119 | 120 | 05.01.03 in: Well it looks like I got the creation code working tonight, what 121 | a pain in the ass *that* was. I've been working on it in my free 122 | time (which is almost non-existent) for a week now, bleh. Also 123 | decided to implement rewriting xisos and I think I'll add build 124 | xiso from ftp-server, just to be *really* lazy. I guess this 125 | means I'll have to read the stat code in the ftp tree. Hmmm, 126 | probably need to dig around in there anyway... A user reported 127 | that newer builds of evox are barfing with ftp upload so I guess 128 | I'll go debug that. 129 | 130 | Also cleaned up the code quite a bit tonight just for posterity. 131 | I'd just like to point out that I *know* I'm being really lazy with 132 | all these big-ass functions and no header files and such. The fact 133 | is I just can't seem to bring myself to care woohaahaa! 134 | 135 | (rev to 2.0 beta) NOT FOR RELEASE until I get the other goodies 136 | written ;) 137 | 138 | 05.03.03 in: Added support for create xiso from ftp server. Still need to debug 139 | evox to see what the problem is-- looks like something to do for 140 | tomorrow! 141 | 142 | 05.06.03 in: Finally got back to this little project ;0 -- the ftp bug was that 143 | FtpWriteBlock() in the libftp kit was timing out on occasion and returning 144 | less than a complete buffer. So I fixed that and some other little 145 | bugs here and there, plus I changed the handling of the create mode 146 | so that you can now specify an iso name. Hopefully that's a bit more 147 | intuitive. 148 | 149 | 05.10.03 in: Fixed a lot of stuff by now, I think it's solid for 2.0 release. 150 | (rev to 2.0, release) 151 | 152 | 05.13.03 in: Oops, fixed a bug in main() passing an (essentially) nil pointer to 153 | create_xiso that was causing a core dump and cleaned up the avl_fetch() 154 | and avl_insert() routines. (rev to 2.1, release) 155 | 156 | 05.14.03 in: Added media check fix, fixed a bug in the ftp library where FtpStat was 157 | failing on filenames with spaces in them. 158 | 159 | 06.16.03 in: Based on code from zeek, I added support for win32, minus ftp 160 | functionality. Neither he nor I have the time to port the ftp library 161 | to windows right now, but at least the creation code will work. Big thanks 162 | to zeek for taking the time to wade through the source and figure out 163 | what needed to be tweaked for the windows build. 164 | 165 | 06.20.03 in: Well I just couldn't release the windows build without ftp support (can 166 | you say OCD ;-), anyway I sat down today and ported the ftp library 167 | to win32. That was a major pain let me tell you as I don't have a decent 168 | PC to run windows on (all my decent PC's run linux) and I've never really 169 | programmed anything on Windows. Who'd have known that I couldn't just use 170 | fdopen() to convert a socket descriptor to a FILE *! Anyway, whining aside 171 | I think it all works the way it's supposed to. I'm sure once I throw it on 172 | the PC community I'll have plenty of bug reports, but such is life. I also 173 | fixed a few other minor glitches here and there that gcc never complained 174 | about but that vc++ didn't like. 175 | 176 | 07.15.03 in: Fixed a bug first reported by Metal Maniac (thanks) where the path string was 177 | being generated improperly during xiso creation on windows. Special thanks to 178 | Hydra for submitting code that mostly fixed the problem, I needed to make a few 179 | more tweaks but nothing much. Hopefully this will solve the problem. Also, 180 | thanks to Huge for doing a Win32 GUI around extract-xiso 2.2! Rev to 2.3, release. 181 | 182 | 07.16.03 in: Changed some of the help text, looks like I introduced a copy-paste 183 | bug somewhere along the line. Oops. 184 | 185 | 07.28.03 in: Added support for progress updating to create_xiso, now just pass in 186 | a pointer to a progress_callback routine (see typedef below). Also added 187 | support on darwin for burning the iso to cd/dvd. For some reason right now 188 | everything works fine if I try to burn an image to a DVD, but if I try to 189 | insert a cd it dies. I have no idea as of yet what's wrong. I am strongly 190 | considering *not* opensourcing my cd-burning stuff once I get it working 191 | as I can think of a few commercial uses for it. Have to mull that one 192 | over a bit more. This version only for release to UI developers. 193 | 194 | 12.02.03 in: Fixed a few bugs in the ftp subsystem and increased the read-write buffer size 195 | to 2Mb. That should help ftp performance quite a bit. 196 | 197 | 10.29.04 in: Well, it's been a looooong time since I've worked on this little program... 198 | I've always been irritated by the fact that extract-xiso would never create an 199 | iso that could be auto-detected by CD/DVD burning software. To burn iso's I've 200 | always had to go in and select a manual sector size of 2048 bytes, etc. What 201 | a pain! As a result, I've been trying to get my hands on the Yellow Book for 202 | ages. I never did manage that as I didn't want to pay for it but I did some 203 | research the other day and came across the ECMA-119 specification. It lays 204 | out the exact volume format that I needed to use. Hooray! Now xiso's are 205 | autodetected and burned properly by burning software... 206 | 207 | If you try to follow what I've done and find the write_volume_descriptors() 208 | method cryptic, just go download the ecma-119 specification from the ecma 209 | website. Read about primary volume descriptors and it'll make sense. 210 | 211 | Bleh! This code is ugly ;-) 212 | 213 | 10.25.05 in: Added in patch from Nordman. Thanks. 214 | Added in security patch from Chris Bainbridge. Thanks. 215 | Fixed a few minor bugs. 216 | 217 | 01.18.10 aiyyo: XBox 360 iso extraction supported. 218 | 219 | 10.04.10 aiyyo: Added new command line switch (-s skip $SystemUpdate folder). 220 | Display file progress in percent. 221 | Try to create destination directory. 222 | 223 | 10.11.10 aiyyo: Fix -l bug (empty list). 224 | 225 | 05.02.11 aiyyo: Remove security patch. 226 | 227 | 09.30.11 somski: Added XGD3 support 228 | 229 | 01.11.14 twillecomme: Intégration of aiyyo's and somski's work. 230 | Minor warn fixes. 231 | 232 | enjoy! 233 | 234 | in 235 | */ 236 | 237 | #if defined( __LINUX__ ) 238 | #define _LARGEFILE64_SOURCE 239 | #endif 240 | #if defined( __GNUC__ ) 241 | #define _GNU_SOURCE 242 | #endif 243 | 244 | 245 | #include 246 | #include 247 | #include 248 | #include 249 | #include 250 | #include 251 | #include 252 | #include 253 | #include 254 | #include 255 | 256 | #if defined( __FREEBSD__ ) || defined( __OPENBSD__ ) 257 | #include 258 | #endif 259 | 260 | #if defined( _WIN32 ) 261 | #include 262 | #include "win32/dirent.c" 263 | #else 264 | #include 265 | #include 266 | #include 267 | #endif 268 | 269 | #if defined(_MSC_VER) 270 | #define strcasecmp _stricmp 271 | #define strncasecmp _strnicmp 272 | #else 273 | #include 274 | #endif 275 | 276 | 277 | #if defined( __DARWIN__ ) 278 | #define exiso_target "macos-x" 279 | 280 | #define PATH_CHAR '/' 281 | #define PATH_CHAR_STR "/" 282 | 283 | #define FORCE_ASCII 1 284 | #define READFLAGS O_RDONLY 285 | #define WRITEFLAGS O_WRONLY | O_CREAT | O_TRUNC 286 | #define READWRITEFLAGS O_RDWR 287 | 288 | typedef off_t xoff_t; 289 | #elif defined( __FREEBSD__ ) 290 | #define exiso_target "freebsd" 291 | 292 | #define PATH_CHAR '/' 293 | #define PATH_CHAR_STR "/" 294 | 295 | #define FORCE_ASCII 1 296 | #define READFLAGS O_RDONLY 297 | #define WRITEFLAGS O_WRONLY | O_CREAT | O_TRUNC 298 | #define READWRITEFLAGS O_RDWR 299 | 300 | typedef off_t xoff_t; 301 | #elif defined( __LINUX__ ) 302 | #define exiso_target "linux" 303 | 304 | #define PATH_CHAR '/' 305 | #define PATH_CHAR_STR "/" 306 | 307 | #define FORCE_ASCII 0 308 | #define READFLAGS O_RDONLY | O_LARGEFILE 309 | #define WRITEFLAGS O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE 310 | #define READWRITEFLAGS O_RDWR | O_LARGEFILE 311 | 312 | #define lseek lseek64 313 | #define stat stat64 314 | 315 | typedef off64_t xoff_t; 316 | #elif defined( __OPENBSD__ ) 317 | #define exiso_target "openbsd" 318 | #elif defined( _WIN32 ) 319 | #define exiso_target "win32" 320 | 321 | #define PATH_CHAR '\\' 322 | #define PATH_CHAR_STR "\\" 323 | 324 | #define FORCE_ASCII 0 325 | #define READFLAGS O_RDONLY | O_BINARY 326 | #define WRITEFLAGS O_WRONLY | O_CREAT | O_TRUNC | O_BINARY 327 | #define READWRITEFLAGS O_RDWR | O_BINARY 328 | 329 | #define S_ISDIR( x ) ( ( x ) & _S_IFDIR ) 330 | #define S_ISREG( x ) ( ( x ) & _S_IFREG ) 331 | 332 | #include "win32/getopt.c" 333 | #if defined(_MSC_VER) 334 | #include "win32/asprintf.c" 335 | #endif 336 | #define lseek _lseeki64 337 | #define mkdir( a, b ) mkdir( a ) 338 | 339 | typedef __int32 int32_t; 340 | typedef __int64 xoff_t; 341 | #else 342 | #error unknown target, cannot compile! 343 | #endif 344 | 345 | 346 | #define swap16( n ) ( ( n ) = ( n ) << 8 | ( n ) >> 8 ) 347 | #define swap32( n ) ( ( n ) = ( n ) << 24 | ( n ) << 8 & 0xff0000 | ( n ) >> 8 & 0xff00 | ( n ) >> 24 ) 348 | 349 | #ifdef USE_BIG_ENDIAN 350 | #define big16( n ) 351 | #define big32( n ) 352 | #define little16( n ) swap16( n ) 353 | #define little32( n ) swap32( n ) 354 | #else 355 | #define big16( n ) swap16( n ) 356 | #define big32( n ) swap32( n ) 357 | #define little16( n ) 358 | #define little32( n ) 359 | #endif 360 | 361 | 362 | #define DEBUG_VERIFY_XISO 0 363 | #define DEBUG_OPTIMIZE_XISO 0 364 | #define DEBUG_TRAVERSE_XISO_DIR 0 365 | 366 | 367 | #ifndef DEBUG 368 | #define DEBUG 0 369 | #endif 370 | 371 | 372 | #include 373 | 374 | #ifndef nil 375 | #define nil 0 376 | #endif 377 | 378 | 379 | #define exiso_version "2.7.1 (01.11.14)" 380 | #define VERSION_LENGTH 16 381 | 382 | #define banner "extract-xiso v" exiso_version " for " exiso_target " - written by in \n" 383 | 384 | #define usage() fprintf( stderr, \ 385 | "%s\n\ 386 | Usage:\n\ 387 | \n\ 388 | %s [options] [-[lrx]] [file2.xiso] ...\n\ 389 | %s [options] -c [name] [-c [name]] ...\n\ 390 | \n\ 391 | Mutually exclusive modes:\n\ 392 | \n\ 393 | -c [name] Create xiso from file(s) starting in . If the\n\ 394 | [name] parameter is specified, the xiso will be\n\ 395 | created with the (path and) name given, otherwise\n\ 396 | the xiso will be created in the current directory\n\ 397 | with the name .iso. The -c option may be\n\ 398 | specified multiple times to create multiple xiso\n\ 399 | images.\n\ 400 | -l List files in xiso(s).\n\ 401 | -r Rewrite xiso(s) as optimized xiso(s).\n\ 402 | -x Extract xiso(s) (the default mode if none is given).\n\ 403 | If no directory is specified with -d, a directory\n\ 404 | with the name of the xiso (minus the .iso portion)\n\ 405 | will be created in the current directory and the\n\ 406 | xiso will be expanded there.\n\ 407 | \n\ 408 | Options:\n\ 409 | \n\ 410 | -d In extract mode, expand xiso in .\n\ 411 | In rewrite mode, rewrite xiso in .\n\ 412 | -D In rewrite mode, delete old xiso after processing.\n\ 413 | -h Print this help text and exit.\n\ 414 | -m In create or rewrite mode, disable automatic .xbe\n\ 415 | media enable patching (not recommended).\n\ 416 | -q Run quiet (suppress all non-error output).\n\ 417 | -Q Run silent (suppress all output).\n\ 418 | -s Skip $SystemUpdate folder.\n\ 419 | -v Print version information and exit.\n\ 420 | ", banner, argv[ 0 ], argv[ 0 ] ); 421 | 422 | #define exiso_log if ( ! s_quiet ) printf 423 | #define flush() if ( ! s_quiet ) fflush( stdout ) 424 | 425 | #define mem_err() { log_err( __FILE__, __LINE__, "out of memory error\n" ); err = 1; } 426 | #define read_err() { log_err( __FILE__, __LINE__, "read error: %s\n", strerror( errno ) ); err = 1; } 427 | #define seek_err() { log_err( __FILE__, __LINE__, "seek error: %s\n", strerror( errno ) ); err = 1; } 428 | #define write_err() { log_err( __FILE__, __LINE__, "write error: %s\n", strerror( errno ) ); err = 1; } 429 | #define rread_err() { log_err( __FILE__, __LINE__, "unable to read remote file\n" ); err = 1; } 430 | #define rwrite_err() { log_err( __FILE__, __LINE__, "unable to write to remote file\n" ); err = 1; } 431 | #define unknown_err() { log_err( __FILE__, __LINE__, "an unrecoverable error has occurred\n" ); err = 1; } 432 | #define open_err( in_file ) { log_err( __FILE__, __LINE__, "open error: %s %s\n", ( in_file ), strerror( errno ) ); err = 1; } 433 | #define chdir_err( in_dir ) { log_err( __FILE__, __LINE__, "unable to change to directory %s: %s\n", ( in_dir ), strerror( errno ) ); err = 1; } 434 | #define mkdir_err( in_dir ) { log_err( __FILE__, __LINE__, "unable to create directory %s: %s\n", ( in_dir ), strerror( errno ) ); err = 1; } 435 | #define ropen_err( in_file ) { log_err( __FILE__, __LINE__, "unable to open remote file %s\n", ( in_file ) ); err = 1; } 436 | #define rchdir_err( in_dir ) { log_err( __FILE__, __LINE__, "unable to change to remote directory %s\n", ( in_dir ) ); err = 1; } 437 | #define rmkdir_err( in_dir ) { log_err( __FILE__, __LINE__, "unable to create remote directory %s\n", ( in_dir ) ); err = 1; } 438 | #define misc_err( in_format, a, b, c ) { log_err( __FILE__, __LINE__, ( in_format ), ( a ), ( b ), ( c ) ); err = 1; } 439 | 440 | 441 | #ifndef min 442 | #define min( a , b ) ( ( a ) < ( b ) ? ( a ) : ( b ) ) 443 | #define max( a , b ) ( ( a ) > ( b ) ? ( a ) : ( b ) ) 444 | #endif 445 | 446 | 447 | #define GLOBAL_LSEEK_OFFSET 0x0FD90000ul 448 | #define XGD3_LSEEK_OFFSET 0x02080000ul 449 | #define XGD1_LSEEK_OFFSET 0x18300000ul 450 | 451 | #define n_sectors( size ) ( ( size ) / XISO_SECTOR_SIZE + ( ( size ) % XISO_SECTOR_SIZE ? 1 : 0 ) ) 452 | 453 | #define XISO_HEADER_DATA "MICROSOFT*XBOX*MEDIA" 454 | #define XISO_HEADER_DATA_LENGTH 20 455 | #define XISO_HEADER_OFFSET 0x10000 456 | 457 | #define XISO_FILE_MODULUS 0x10000 458 | 459 | #define XISO_ROOT_DIRECTORY_SECTOR 0x108 460 | 461 | #define XISO_OPTIMIZED_TAG_OFFSET 31337 462 | #define XISO_OPTIMIZED_TAG "in!xiso!" exiso_version 463 | #define XISO_OPTIMIZED_TAG_LENGTH ( 8 + VERSION_LENGTH ) 464 | #define XISO_OPTIMIZED_TAG_LENGTH_MIN 7 465 | 466 | #define XISO_ATTRIBUTES_SIZE 1 467 | #define XISO_FILENAME_LENGTH_SIZE 1 468 | #define XISO_TABLE_OFFSET_SIZE 2 469 | #define XISO_SECTOR_OFFSET_SIZE 4 470 | #define XISO_DIRTABLE_SIZE 4 471 | #define XISO_FILESIZE_SIZE 4 472 | #define XISO_DWORD_SIZE 4 473 | #define XISO_FILETIME_SIZE 8 474 | 475 | #define XISO_SECTOR_SIZE 2048 476 | #define XISO_UNUSED_SIZE 0x7c8 477 | 478 | #define XISO_FILENAME_OFFSET 14 479 | #define XISO_FILENAME_LENGTH_OFFSET ( XISO_FILENAME_OFFSET - 1 ) 480 | #define XISO_FILENAME_MAX_CHARS 255 481 | 482 | #define XISO_ATTRIBUTE_RO 0x01 483 | #define XISO_ATTRIBUTE_HID 0x02 484 | #define XISO_ATTRIBUTE_SYS 0x04 485 | #define XISO_ATTRIBUTE_DIR 0x10 486 | #define XISO_ATTRIBUTE_ARC 0x20 487 | #define XISO_ATTRIBUTE_NOR 0x80 488 | 489 | #define XISO_PAD_BYTE 0xff 490 | #define XISO_PAD_SHORT 0xffff 491 | 492 | #define XISO_MEDIA_ENABLE "\xe8\xca\xfd\xff\xff\x85\xc0\x7d" 493 | #define XISO_MEDIA_ENABLE_BYTE '\xeb' 494 | #define XISO_MEDIA_ENABLE_LENGTH 8 495 | #define XISO_MEDIA_ENABLE_BYTE_POS 7 496 | 497 | #define EMPTY_SUBDIRECTORY ( (dir_node_avl *) 1 ) 498 | 499 | #define READWRITE_BUFFER_SIZE 0x00200000 500 | 501 | #define DEBUG_DUMP_DIRECTORY "/Volumes/c/xbox/iso/exiso" 502 | 503 | #define GETOPT_STRING "c:d:Dhlmp:qQrsvx" 504 | 505 | 506 | typedef enum avl_skew { k_no_skew , k_left_skew , k_right_skew } avl_skew; 507 | typedef enum avl_result { no_err, k_avl_error, k_avl_balanced } avl_result; 508 | typedef enum avl_traversal_method { k_prefix, k_infix, k_postfix } avl_traversal_method; 509 | 510 | typedef enum bm_constants { k_default_alphabet_size = 256 } bm_constants; 511 | 512 | typedef enum modes { k_generate_avl, k_extract, k_list, k_rewrite } modes; 513 | typedef enum errors { err_end_of_sector = -5001, err_iso_rewritten = -5002, err_iso_no_files = -5003 } errors; 514 | 515 | typedef void (*progress_callback)( xoff_t in_current_value, xoff_t in_final_value ); 516 | typedef int (*traversal_callback)( void *in_node, void *in_context, long in_depth ); 517 | 518 | typedef struct dir_node dir_node; 519 | typedef struct create_list create_list; 520 | typedef struct dir_node_avl dir_node_avl; 521 | 522 | struct dir_node { 523 | dir_node *left; 524 | dir_node *parent; 525 | dir_node_avl *avl_node; 526 | 527 | char *filename; 528 | 529 | uint16_t r_offset; 530 | uint8_t attributes; 531 | uint8_t filename_length; 532 | 533 | uint32_t file_size; 534 | uint32_t start_sector; 535 | }; 536 | 537 | struct dir_node_avl { 538 | uint32_t offset; 539 | xoff_t dir_start; 540 | 541 | char *filename; 542 | uint32_t file_size; 543 | uint32_t start_sector; 544 | dir_node_avl *subdirectory; 545 | 546 | uint32_t old_start_sector; 547 | 548 | avl_skew skew; 549 | dir_node_avl *left; 550 | dir_node_avl *right; 551 | }; 552 | 553 | struct create_list { 554 | char *path; 555 | char *name; 556 | create_list *next; 557 | }; 558 | 559 | typedef struct FILE_TIME { 560 | uint32_t l; 561 | uint32_t h; 562 | } FILE_TIME; 563 | 564 | typedef struct wdsafp_context { 565 | xoff_t dir_start; 566 | uint32_t *current_sector; 567 | } wdsafp_context; 568 | 569 | typedef struct write_tree_context { 570 | int xiso; 571 | char *path; 572 | int from; 573 | progress_callback progress; 574 | xoff_t final_bytes; 575 | } write_tree_context; 576 | 577 | 578 | int log_err( const char *in_file, int in_line, const char *in_format, ... ); 579 | void avl_rotate_left( dir_node_avl **in_root ); 580 | void avl_rotate_right( dir_node_avl **in_root ); 581 | int avl_compare_key( char *in_lhs, char *in_rhs ); 582 | avl_result avl_left_grown( dir_node_avl **in_root ); 583 | avl_result avl_right_grown( dir_node_avl **in_root ); 584 | dir_node_avl *avl_fetch( dir_node_avl *in_root, char *in_filename ); 585 | avl_result avl_insert( dir_node_avl **in_root, dir_node_avl *in_node ); 586 | int avl_traverse_depth_first( dir_node_avl *in_root, traversal_callback in_callback, void *in_context, avl_traversal_method in_method, long in_depth ); 587 | 588 | void boyer_moore_done(); 589 | char *boyer_moore_search( char *in_text, long in_text_len ); 590 | int boyer_moore_init( char *in_pattern, long in_pat_len, long in_alphabet_size ); 591 | 592 | int free_dir_node_avl( void *in_dir_node_avl, void *, long ); 593 | int extract_file( int in_xiso, dir_node *in_file, modes in_mode, char *path ); 594 | int decode_xiso( char *in_xiso, char *in_path, modes in_mode, char **out_iso_path, bool in_ll_compat ); 595 | int verify_xiso( int in_xiso, int32_t *out_root_dir_sector, int32_t *out_root_dir_size, char *in_iso_name ); 596 | int traverse_xiso( int in_xiso, dir_node *in_dir_node, xoff_t in_dir_start, char *in_path, modes in_mode, dir_node_avl **in_root, bool in_ll_compat ); 597 | int create_xiso( char *in_root_directory, char *in_output_directory, dir_node_avl *in_root, int in_xiso, char **out_iso_path, char *in_name, progress_callback in_progress_callback ); 598 | 599 | FILE_TIME *alloc_filetime_now( void ); 600 | int generate_avl_tree_local( dir_node_avl **out_root, int *io_n ); 601 | int generate_avl_tree_remote( dir_node_avl **out_root, int *io_n ); 602 | int write_directory( dir_node_avl *in_avl, int in_xiso, int in_depth ); 603 | int write_file( dir_node_avl *in_avl, write_tree_context *in_context, int in_depth ); 604 | int write_tree( dir_node_avl *in_avl, write_tree_context *in_context, int in_depth ); 605 | int calculate_total_files_and_bytes( dir_node_avl *in_avl, void *in_context, int in_depth ); 606 | int calculate_directory_size( dir_node_avl *in_avl, uint32_t *out_size, long in_depth ); 607 | int calculate_directory_requirements( dir_node_avl *in_avl, void *in_context, int in_depth ); 608 | int calculate_directory_offsets( dir_node_avl *in_avl, uint32_t *io_context, int in_depth ); 609 | int write_dir_start_and_file_positions( dir_node_avl *in_avl, wdsafp_context *io_context, int in_depth ); 610 | int write_volume_descriptors( int in_xiso, uint32_t in_total_sectors ); 611 | 612 | #if DEBUG 613 | void write_sector( int in_xiso, xoff_t in_start, char *in_name, char *in_extension ); 614 | #endif 615 | 616 | 617 | static long s_pat_len; 618 | static bool s_quiet = false; 619 | static char *s_pattern = nil; 620 | static long *s_gs_table = nil; 621 | static long *s_bc_table = nil; 622 | static xoff_t s_total_bytes = 0; 623 | static int s_total_files = 0; 624 | static char *s_copy_buffer = nil; 625 | static bool s_real_quiet = false; 626 | static bool s_media_enable = true; 627 | static xoff_t s_total_bytes_all_isos = 0; 628 | static int s_total_files_all_isos = 0; 629 | static bool s_warned = 0; 630 | 631 | static bool s_remove_systemupdate = false; 632 | static char *s_systemupdate = "$SystemUpdate"; 633 | 634 | static xoff_t s_xbox_disc_lseek = 0; 635 | 636 | 637 | #if 0 // #pragma mark - inserts a spacer in the function popup of certain text editors (i.e. mine ;-) 638 | #pragma mark - 639 | #endif 640 | 641 | 642 | int main( int argc, char **argv ) { 643 | struct stat sb; 644 | create_list *create = nil, *p, *q, **r; 645 | int i, fd, opt_char, err = 0, isos = 0; 646 | bool extract = true, rewrite = false, free_user = false, free_pass = false, x_seen = false, delete = false, optimized; 647 | char *cwd = nil, *path = nil, *buf = nil, *new_iso_path = nil, tag[ XISO_OPTIMIZED_TAG_LENGTH * sizeof(long) ]; 648 | 649 | if ( argc < 2 ) { usage(); exit( 1 ); } 650 | 651 | while ( ! err && ( opt_char = getopt( argc, argv, GETOPT_STRING ) ) != -1 ) { 652 | switch ( opt_char ) { 653 | case 'c': { 654 | if ( x_seen || rewrite || ! extract ) { 655 | usage(); 656 | exit( 1 ); 657 | } 658 | 659 | for ( r = &create; *r != nil; r = &(*r)->next ) ; 660 | 661 | if ( ( *r = (create_list *) malloc( sizeof(create_list) ) ) == nil ) mem_err(); 662 | if ( ! err ) { 663 | (*r)->name = nil; 664 | (*r)->next = nil; 665 | 666 | if ( ( (*r)->path = strdup( optarg ) ) == nil ) mem_err(); 667 | } 668 | if ( ! err && argv[ optind ] && *argv[ optind ] != '-' && *argv[ optind ] && ( (*r)->name = strdup( argv[ optind++ ] ) ) == nil ) mem_err(); 669 | } break; 670 | 671 | case 'd': { 672 | if ( path ) free( path ); 673 | if ( ( path = strdup( optarg ) ) == nil ) mem_err(); 674 | } break; 675 | 676 | case 'D': { 677 | delete = true; 678 | } break; 679 | 680 | case 'h': { 681 | usage(); 682 | exit( 0 ); 683 | } break; 684 | 685 | case 'l': { 686 | if ( x_seen || rewrite || create ) { 687 | usage(); 688 | exit( 1 ); 689 | } 690 | extract = false; 691 | } break; 692 | 693 | case 'm': { 694 | if ( x_seen || ! extract ) { 695 | usage(); 696 | exit( 1 ); 697 | } 698 | s_media_enable = false; 699 | } break; 700 | 701 | case 'q': { 702 | s_quiet = true; 703 | } break; 704 | 705 | case 'Q': { 706 | s_quiet = s_real_quiet = true; 707 | } break; 708 | 709 | case 'r': { 710 | if ( x_seen || ! extract || create ) { 711 | usage(); 712 | exit( 1 ); 713 | } 714 | rewrite = true; 715 | } break; 716 | 717 | case 's': { 718 | s_remove_systemupdate = true; 719 | } break; 720 | 721 | case 'v': { 722 | printf( "%s", banner ); 723 | exit( 0 ); 724 | } break; 725 | 726 | case 'x': { 727 | if ( ! extract || rewrite || create ) { 728 | usage(); 729 | exit( 1 ); 730 | } 731 | x_seen = true; 732 | } break; 733 | 734 | default: { 735 | usage(); 736 | exit( 1 ); 737 | } break; 738 | } 739 | } 740 | 741 | if ( ! err ) { 742 | 743 | if ( create ) { if ( optind < argc ) { usage(); exit( 1 ); } } 744 | else if ( optind >= argc ) { usage(); exit( 1 ); } 745 | 746 | exiso_log( "%s", banner ); 747 | 748 | if ( ( extract ) && ( s_copy_buffer = (char *) malloc( READWRITE_BUFFER_SIZE ) ) == nil ) mem_err(); 749 | } 750 | 751 | if ( ! err && ( create || rewrite ) ) err = boyer_moore_init( XISO_MEDIA_ENABLE, XISO_MEDIA_ENABLE_LENGTH, k_default_alphabet_size ); 752 | 753 | if ( ! err && create ) { 754 | for ( p = create; ! err && p != nil; ) { 755 | char *tmp = nil; 756 | 757 | if ( p->name ) { 758 | for ( i = (int) strlen( p->name ); i >= 0 && p->name[ i ] != PATH_CHAR; --i ) ; ++i; 759 | 760 | if ( i ) { 761 | if ( ( tmp = (char *) malloc( i + 1 ) ) == nil ) mem_err(); 762 | if ( ! err ) { 763 | strncpy( tmp, p->name, i ); 764 | tmp[ i ] = 0; 765 | } 766 | } 767 | } 768 | 769 | if ( ! err ) err = create_xiso( p->path, tmp, nil, -1, nil, p->name ? p->name + i : nil, nil ); 770 | 771 | if ( tmp ) free( tmp ); 772 | 773 | q = p->next; 774 | 775 | if ( p->name ) free( p->name ); 776 | free( p->path ); 777 | free( p ); 778 | 779 | p = q; 780 | } 781 | } else for ( i = optind; ! err && i < argc; ++i ) { 782 | ++isos; 783 | exiso_log( "\n" ); 784 | s_total_bytes = s_total_files = 0; 785 | 786 | 787 | if ( ! err ) { 788 | optimized = false; 789 | 790 | if ( ( fd = open( argv[ i ], READFLAGS, 0 ) ) == -1 ) open_err( argv[ i ] ); 791 | if ( ! err && lseek( fd, (xoff_t) XISO_OPTIMIZED_TAG_OFFSET, SEEK_SET ) == -1 ) seek_err(); 792 | if ( ! err && read( fd, tag, XISO_OPTIMIZED_TAG_LENGTH ) != XISO_OPTIMIZED_TAG_LENGTH ) read_err(); 793 | 794 | if ( fd != -1 ) close( fd ); 795 | 796 | if ( ! err ) { 797 | tag[ XISO_OPTIMIZED_TAG_LENGTH ] = 0; 798 | 799 | if ( ! strncmp( tag, XISO_OPTIMIZED_TAG, XISO_OPTIMIZED_TAG_LENGTH_MIN ) ) optimized = true; 800 | 801 | if ( rewrite ) { 802 | if ( optimized ) { 803 | exiso_log( "%s is already optimized, skipping...\n", argv[ i ] ); 804 | continue; 805 | } 806 | 807 | if ( ! err && ( buf = (char *) malloc( strlen( argv[ i ] ) + 5 ) ) == nil ) mem_err(); // + 5 magic number is for ".old\0" 808 | if ( ! err ) { 809 | sprintf( buf, "%s.old", argv[ i ] ); 810 | if ( stat( buf, &sb ) != -1 ) misc_err( "%s already exists, cannot rewrite %s\n", buf, argv[ i ], 0 ); 811 | if ( ! err && rename( argv[ i ], buf ) == -1 ) misc_err( "cannot rename %s to %s\n", argv[ i ], buf, 0 ); 812 | 813 | if ( err ) { err = 0; free( buf ); continue; } 814 | } 815 | if ( ! err ) err = decode_xiso( buf, path, k_rewrite, &new_iso_path, true ); 816 | if ( ! err && delete && unlink( buf ) == -1 ) log_err( __FILE__, __LINE__, "unable to delete %s\n", buf ); 817 | 818 | if ( buf ) free( buf ); 819 | } else { 820 | // the order of the mutually exclusive options here is important, the extract ? k_extract : k_list test *must* be the final comparison 821 | if ( ! err ) err = decode_xiso( argv[ i ], path, extract ? k_extract : k_list, nil, ! optimized ); 822 | } 823 | } 824 | } 825 | 826 | if ( ! err ) exiso_log( "\n%u files in %s total %lld bytes\n", s_total_files, rewrite ? new_iso_path : argv[ i ], (long long int) s_total_bytes ); 827 | 828 | if ( new_iso_path ) { 829 | if ( ! err ) exiso_log( "\n%s successfully rewritten%s%s\n", argv[ i ], path ? " as " : ".", path ? new_iso_path : "" ); 830 | 831 | free( new_iso_path ); 832 | new_iso_path = nil; 833 | } 834 | 835 | if ( err == err_iso_no_files ) err = 0; 836 | } 837 | 838 | if ( ! err && isos > 1 ) exiso_log( "\n%u files in %u xiso's total %lld bytes\n", s_total_files_all_isos, isos, (long long int) s_total_bytes_all_isos ); 839 | if ( s_warned ) exiso_log( "\nWARNING: Warning(s) were issued during execution--review stderr!\n" ); 840 | 841 | boyer_moore_done(); 842 | 843 | if ( s_copy_buffer ) free( s_copy_buffer ); 844 | if ( path ) free( path ); 845 | 846 | return err; 847 | } 848 | 849 | 850 | int log_err( const char *in_file, int in_line, const char *in_format, ... ) { 851 | va_list ap; 852 | char *format; 853 | int ret; 854 | 855 | #if DEBUG 856 | asprintf( &format, "%s:%u %s", in_file, in_line, in_format ); 857 | #else 858 | format = (char *) in_format; 859 | #endif 860 | 861 | if ( s_real_quiet ) ret = 0; 862 | else { 863 | va_start( ap, in_format ); 864 | ret = vfprintf( stderr, format, ap ); 865 | va_end( ap ); 866 | } 867 | 868 | #if DEBUG 869 | free( format ); 870 | #endif 871 | 872 | return ret; 873 | } 874 | 875 | 876 | #if 0 877 | #pragma mark - 878 | #endif 879 | 880 | 881 | int verify_xiso( int in_xiso, int32_t *out_root_dir_sector, int32_t *out_root_dir_size, char *in_iso_name ) { 882 | int err = 0; 883 | char buffer[ XISO_HEADER_DATA_LENGTH ]; 884 | 885 | if ( lseek( in_xiso, (xoff_t) XISO_HEADER_OFFSET, SEEK_SET ) == -1 ) seek_err(); 886 | if ( ! err && read( in_xiso, buffer, XISO_HEADER_DATA_LENGTH ) != XISO_HEADER_DATA_LENGTH ) read_err(); 887 | if ( ! err && memcmp( buffer, XISO_HEADER_DATA, XISO_HEADER_DATA_LENGTH ) ) 888 | { 889 | if ( lseek( in_xiso, (xoff_t) XISO_HEADER_OFFSET + GLOBAL_LSEEK_OFFSET, SEEK_SET ) == -1 ) seek_err(); 890 | if ( ! err && read( in_xiso, buffer, XISO_HEADER_DATA_LENGTH ) != XISO_HEADER_DATA_LENGTH ) read_err(); 891 | if ( ! err && memcmp( buffer, XISO_HEADER_DATA, XISO_HEADER_DATA_LENGTH ) ) 892 | { 893 | if ( lseek( in_xiso, (xoff_t) XISO_HEADER_OFFSET + XGD3_LSEEK_OFFSET, SEEK_SET ) == -1 ) seek_err(); 894 | if ( ! err && read( in_xiso, buffer, XISO_HEADER_DATA_LENGTH ) != XISO_HEADER_DATA_LENGTH ) read_err(); 895 | if (!err && memcmp(buffer, XISO_HEADER_DATA, XISO_HEADER_DATA_LENGTH)) 896 | { 897 | if (lseek(in_xiso, (xoff_t)XISO_HEADER_OFFSET + XGD1_LSEEK_OFFSET, SEEK_SET) == -1) seek_err(); 898 | if (!err && read(in_xiso, buffer, XISO_HEADER_DATA_LENGTH) != XISO_HEADER_DATA_LENGTH) read_err(); 899 | if (!err && memcmp(buffer, XISO_HEADER_DATA, XISO_HEADER_DATA_LENGTH)) misc_err("%s does not appear to be a valid xbox iso image\n", in_iso_name, 0, 0) 900 | else s_xbox_disc_lseek = XGD1_LSEEK_OFFSET; 901 | } 902 | else s_xbox_disc_lseek = XGD3_LSEEK_OFFSET; 903 | } 904 | else s_xbox_disc_lseek = GLOBAL_LSEEK_OFFSET; 905 | } 906 | else s_xbox_disc_lseek = 0; 907 | 908 | // read root directory information 909 | if ( ! err && read( in_xiso, out_root_dir_sector, XISO_SECTOR_OFFSET_SIZE ) != XISO_SECTOR_OFFSET_SIZE ) read_err(); 910 | if ( ! err && read( in_xiso, out_root_dir_size, XISO_DIRTABLE_SIZE ) != XISO_DIRTABLE_SIZE ) read_err(); 911 | 912 | little32( *out_root_dir_sector ); 913 | little32( *out_root_dir_size ); 914 | 915 | // seek to header tail and verify media tag 916 | if ( ! err && lseek( in_xiso, (xoff_t) XISO_FILETIME_SIZE + XISO_UNUSED_SIZE, SEEK_CUR ) == -1 ) seek_err(); 917 | if ( ! err && read( in_xiso, buffer, XISO_HEADER_DATA_LENGTH ) != XISO_HEADER_DATA_LENGTH ) read_err(); 918 | if ( ! err && memcmp( buffer, XISO_HEADER_DATA, XISO_HEADER_DATA_LENGTH ) ) misc_err( "%s appears to be corrupt\n", in_iso_name, 0, 0 ); 919 | 920 | // seek to root directory sector 921 | if ( ! err ) { 922 | if ( ! *out_root_dir_sector && ! *out_root_dir_size ) { 923 | exiso_log( "xbox image %s contains no files.\n", in_iso_name ); 924 | err = err_iso_no_files; 925 | } else { 926 | if ( lseek( in_xiso, (xoff_t) *out_root_dir_sector * XISO_SECTOR_SIZE, SEEK_SET ) == -1 ) seek_err(); 927 | } 928 | } 929 | 930 | return err; 931 | } 932 | 933 | 934 | 935 | 936 | int create_xiso( char *in_root_directory, char *in_output_directory, dir_node_avl *in_root, int in_xiso, char **out_iso_path, char *in_name, progress_callback in_progress_callback ) { 937 | xoff_t pos; 938 | dir_node_avl root; 939 | FILE_TIME *ft = nil; 940 | write_tree_context wt_context; 941 | uint32_t start_sector; 942 | int i, n, xiso = -1, err = 0; 943 | char *cwd = nil, *buf = nil, *iso_name, *xiso_path, *iso_dir; 944 | 945 | s_total_bytes = s_total_files = 0; 946 | 947 | memset( &root, 0, sizeof(dir_node_avl) ); 948 | 949 | if ( ( cwd = getcwd( nil, 0 ) ) == nil ) mem_err(); 950 | if ( ! err ) { 951 | if ( ! in_root ) { 952 | if ( chdir( in_root_directory ) == -1 ) chdir_err( in_root_directory ); 953 | if ( ! err ) { 954 | if ( in_root_directory[ i = (int) strlen( in_root_directory ) - 1 ] == '/' || in_root_directory[ i ] == '\\' ) in_root_directory[ i-- ] = 0; 955 | for ( iso_dir = &in_root_directory[ i ]; iso_dir >= in_root_directory && *iso_dir != PATH_CHAR; --iso_dir ) ; ++iso_dir; 956 | 957 | iso_name = in_name ? in_name : iso_dir; 958 | } 959 | } else { 960 | iso_dir = iso_name = in_root_directory; 961 | } 962 | } 963 | if ( ! err ) { 964 | if ( ! *iso_dir ) iso_dir = PATH_CHAR_STR; 965 | if ( ! in_output_directory ) in_output_directory = cwd; 966 | if ( in_output_directory[ i = (int) strlen( in_output_directory ) - 1 ] == PATH_CHAR ) in_output_directory[ i-- ] = 0; 967 | if ( ! iso_name || ! *iso_name ) iso_name = "root"; 968 | else if ( iso_name[ 1 ] == ':' ) { iso_name[ 1 ] = iso_name[ 0 ]; ++iso_name; } 969 | #if defined( _WIN32 ) 970 | if ( ( asprintf( &xiso_path, "%s%c%s%s", *in_output_directory ? in_output_directory : cwd, PATH_CHAR, iso_name, in_name ? "" : ".iso" ) ) == -1 ) mem_err(); 971 | #else 972 | if ( ( asprintf( &xiso_path, "%s%s%s%c%s%s", *in_output_directory == PATH_CHAR ? "" : cwd, *in_output_directory == PATH_CHAR ? "" : PATH_CHAR_STR, in_output_directory, PATH_CHAR, iso_name, in_name ? "" : ".iso" ) ) == -1 ) mem_err(); 973 | #endif 974 | } 975 | if ( ! err ) { 976 | exiso_log( "%s %s%s:\n\n", in_root ? "rewriting" : "\ncreating", iso_name, in_name ? "" : ".iso" ); 977 | 978 | root.start_sector = XISO_ROOT_DIRECTORY_SECTOR; 979 | 980 | s_total_bytes = s_total_files = 0; 981 | 982 | if ( in_root ) { 983 | root.subdirectory = in_root; 984 | avl_traverse_depth_first( in_root, (traversal_callback) calculate_total_files_and_bytes, nil, k_prefix, 0 ); 985 | } else { 986 | int i, n = 0; 987 | 988 | exiso_log( "generating avl tree from %sfilesystem: ", "" ); flush(); 989 | 990 | err = generate_avl_tree_local( &root.subdirectory, &n ); 991 | 992 | for ( i = 0; i < n; ++i ) exiso_log( "\b" ); 993 | for ( i = 0; i < n; ++i ) exiso_log( " " ); 994 | for ( i = 0; i < n; ++i ) exiso_log( "\b" ); 995 | 996 | exiso_log( "%s\n\n", err ? "failed!" : "[OK]" ); 997 | } 998 | } 999 | if ( ! err && in_progress_callback ) (*in_progress_callback)( 0, s_total_bytes ); 1000 | if ( ! err ) { 1001 | wt_context.final_bytes = s_total_bytes; 1002 | 1003 | s_total_bytes = s_total_files = 0; 1004 | 1005 | start_sector = root.start_sector; 1006 | 1007 | avl_traverse_depth_first( &root, (traversal_callback) calculate_directory_requirements, nil, k_prefix, 0 ); 1008 | avl_traverse_depth_first( &root, (traversal_callback) calculate_directory_offsets, &start_sector, k_prefix, 0 ); 1009 | } 1010 | if ( ! err && ( buf = (char *) malloc( n = max( READWRITE_BUFFER_SIZE, XISO_HEADER_OFFSET ) ) ) == nil ) mem_err(); 1011 | if ( ! err ) { 1012 | if ( ( xiso = open( xiso_path, WRITEFLAGS, 0644 ) ) == -1 ) open_err( xiso_path ); 1013 | if ( out_iso_path ) *out_iso_path = xiso_path; 1014 | else free( xiso_path ); 1015 | } 1016 | if ( ! err ) { 1017 | memset( buf, 0, n ); 1018 | if ( write( xiso, buf, XISO_HEADER_OFFSET ) != XISO_HEADER_OFFSET ) write_err(); 1019 | } 1020 | if ( ! err && write( xiso, XISO_HEADER_DATA, XISO_HEADER_DATA_LENGTH ) != XISO_HEADER_DATA_LENGTH ) write_err(); 1021 | if ( ! err ) { 1022 | little32( root.start_sector ); 1023 | if ( write( xiso, &root.start_sector, XISO_SECTOR_OFFSET_SIZE ) != XISO_SECTOR_OFFSET_SIZE ) write_err(); 1024 | little32( root.start_sector ); 1025 | } 1026 | if ( ! err ) { 1027 | little32( root.file_size ); 1028 | if ( write( xiso, &root.file_size, XISO_DIRTABLE_SIZE ) != XISO_DIRTABLE_SIZE ) write_err(); 1029 | little32( root.file_size ); 1030 | } 1031 | if ( ! err ) { 1032 | if ( in_root ) { 1033 | if ( lseek( in_xiso, (xoff_t) XISO_HEADER_OFFSET + XISO_HEADER_DATA_LENGTH + XISO_SECTOR_OFFSET_SIZE + XISO_DIRTABLE_SIZE + s_xbox_disc_lseek, SEEK_SET ) == -1 ) seek_err(); 1034 | if ( ! err && read( in_xiso, buf, XISO_FILETIME_SIZE ) != XISO_FILETIME_SIZE ) read_err(); 1035 | if ( ! err && write( xiso, buf, XISO_FILETIME_SIZE ) != XISO_FILETIME_SIZE ) write_err(); 1036 | 1037 | memset( buf, 0, XISO_FILETIME_SIZE ); 1038 | } else { 1039 | if ( ( ft = alloc_filetime_now() ) == nil ) mem_err(); 1040 | if ( ! err && write( xiso, ft, XISO_FILETIME_SIZE ) != XISO_FILETIME_SIZE ) write_err(); 1041 | } 1042 | } 1043 | if ( ! err && write( xiso, buf, XISO_UNUSED_SIZE ) != XISO_UNUSED_SIZE ) write_err(); 1044 | if ( ! err && write( xiso, XISO_HEADER_DATA, XISO_HEADER_DATA_LENGTH ) != XISO_HEADER_DATA_LENGTH ) write_err(); 1045 | 1046 | if ( ! err && ! in_root ) { 1047 | if ( chdir( ".." ) == -1 ) chdir_err( ".." ); 1048 | } 1049 | if ( ! err && ( root.filename = strdup( iso_dir ) ) == nil ) mem_err(); 1050 | 1051 | if ( ! err && lseek( xiso, (xoff_t) root.start_sector * XISO_SECTOR_SIZE, SEEK_SET ) == -1 ) seek_err(); 1052 | if ( ! err ) { 1053 | wt_context.path = nil; 1054 | wt_context.xiso = xiso; 1055 | wt_context.from = in_root ? in_xiso : -1; 1056 | wt_context.progress = in_progress_callback; 1057 | 1058 | err = avl_traverse_depth_first( &root, (traversal_callback) write_tree, &wt_context, k_prefix, 0 ); 1059 | } 1060 | 1061 | if ( ! err && ( pos = lseek( xiso, (xoff_t) 0, SEEK_END ) ) == -1 ) seek_err(); 1062 | if ( ! err && write( xiso, buf, i = (int) (( XISO_FILE_MODULUS - pos % XISO_FILE_MODULUS ) % XISO_FILE_MODULUS) ) != i ) write_err(); 1063 | 1064 | if ( ! err ) err = write_volume_descriptors( xiso, ( pos + (xoff_t) i ) / XISO_SECTOR_SIZE ); 1065 | 1066 | if ( ! err && lseek( xiso, (xoff_t) XISO_OPTIMIZED_TAG_OFFSET, SEEK_SET ) == -1 ) seek_err(); 1067 | if ( ! err && write( xiso, XISO_OPTIMIZED_TAG, XISO_OPTIMIZED_TAG_LENGTH ) != XISO_OPTIMIZED_TAG_LENGTH ) write_err(); 1068 | 1069 | if ( ! in_root ) { 1070 | if ( err ) { exiso_log( "\ncould not create %s%s\n", iso_name ? iso_name : "xiso", iso_name && ! in_name ? ".iso" : "" ); } 1071 | else exiso_log( "\nsucessfully created %s%s (%u files totalling %lld bytes added)\n", iso_name ? iso_name : "xiso", iso_name && ! in_name ? ".iso" : "", s_total_files, (long long int) s_total_bytes ); 1072 | } 1073 | 1074 | if ( root.subdirectory != EMPTY_SUBDIRECTORY ) avl_traverse_depth_first( root.subdirectory, free_dir_node_avl, nil, k_postfix, 0 ); 1075 | 1076 | if ( xiso != -1 ) { 1077 | close( xiso ); 1078 | if ( err ) unlink( xiso_path ); 1079 | } 1080 | 1081 | if ( root.filename ) free( root.filename ); 1082 | if ( buf ) free( buf ); 1083 | if ( ft ) free( ft ); 1084 | 1085 | if ( cwd ) { 1086 | if ( chdir( cwd ) == -1 ) chdir_err( cwd ); 1087 | free( cwd ); 1088 | } 1089 | 1090 | return err; 1091 | } 1092 | 1093 | 1094 | int decode_xiso( char *in_xiso, char *in_path, modes in_mode, char **out_iso_path, bool in_ll_compat ) { 1095 | dir_node_avl *root = nil; 1096 | bool repair = false; 1097 | int32_t root_dir_sect, root_dir_size; 1098 | int xiso, err = 0, len, path_len = 0, add_slash = 0; 1099 | char *buf, *cwd = nil, *name = nil, *short_name = nil, *iso_name, *folder = nil; 1100 | 1101 | if ( ( xiso = open( in_xiso, READFLAGS, 0 ) ) == -1 ) open_err( in_xiso ); 1102 | 1103 | if ( ! err ) { 1104 | len = (int) strlen( in_xiso ); 1105 | 1106 | if ( in_mode == k_rewrite ) { 1107 | in_xiso[ len -= 4 ] = 0; 1108 | repair = true; 1109 | } 1110 | 1111 | for ( name = &in_xiso[ len ]; name >= in_xiso && *name != PATH_CHAR; --name ) ; ++name; 1112 | 1113 | len = (int) strlen( name ); 1114 | 1115 | // create a directory of the same name as the file we are working on, minus the ".iso" portion 1116 | if (len > 4 && strcasecmp(&name[len - 4], ".iso") == 0) { 1117 | name[ len -= 4 ] = 0; 1118 | if ( ( short_name = strdup( name ) ) == nil ) mem_err(); 1119 | name[ len ] = '.'; 1120 | } 1121 | } 1122 | 1123 | if ( ! err && ! len ) misc_err( "invalid xiso image name: %s\n", in_xiso, 0, 0 ); 1124 | 1125 | if ( ! err && in_mode == k_extract && in_path ) { 1126 | if ( ( cwd = getcwd( nil, 0 ) ) == nil ) mem_err(); 1127 | if ( ! err && mkdir( in_path, 0755 ) ); 1128 | if ( ! err && chdir( in_path ) == -1 ) chdir_err( in_path ); 1129 | } 1130 | 1131 | if ( ! err ) err = verify_xiso( xiso, &root_dir_sect, &root_dir_size, name ); 1132 | 1133 | iso_name = short_name ? short_name : name; 1134 | 1135 | if ( ! err && in_mode != k_rewrite ) { 1136 | exiso_log( "%s %s:\n\n", in_mode == k_extract ? "extracting" : "listing", name ); 1137 | 1138 | if ( in_mode == k_extract ) { 1139 | if ( ! in_path ) { 1140 | if ( ( err = mkdir( iso_name, 0755 ) ) ) mkdir_err( iso_name ); 1141 | if ( ! err && ( err = chdir( iso_name ) ) ) chdir_err( iso_name ); 1142 | } 1143 | } 1144 | } 1145 | 1146 | if ( ! err && root_dir_sect && root_dir_size ) { 1147 | if ( in_path ) { 1148 | path_len = (int) strlen( in_path ); 1149 | if ( in_path[ path_len - 1 ] != PATH_CHAR ) ++add_slash; 1150 | } 1151 | 1152 | if ( ( buf = (char *) malloc( path_len + add_slash + strlen( iso_name ) + 2 ) ) == nil ) mem_err(); 1153 | 1154 | if ( ! err ) { 1155 | sprintf( buf, "%s%s%s%c", in_path ? in_path : "", add_slash && ( ! in_path ) ? PATH_CHAR_STR : "", in_mode != k_list && ( ! in_path ) ? iso_name : "", PATH_CHAR ); 1156 | 1157 | if ( in_mode == k_rewrite ) { 1158 | 1159 | if ( ! err && lseek( xiso, (xoff_t) root_dir_sect * XISO_SECTOR_SIZE + s_xbox_disc_lseek, SEEK_SET ) == -1 ) seek_err(); 1160 | if ( ! err ) err = traverse_xiso( xiso, nil, (xoff_t) root_dir_sect * XISO_SECTOR_SIZE + s_xbox_disc_lseek, buf, k_generate_avl, &root, in_ll_compat ); 1161 | if ( ! err ) err = create_xiso( iso_name, in_path, root, xiso, out_iso_path, nil, nil ); 1162 | 1163 | } else { 1164 | if ( ! err && lseek( xiso, (xoff_t) root_dir_sect * XISO_SECTOR_SIZE + s_xbox_disc_lseek, SEEK_SET ) == -1) seek_err(); 1165 | if ( ! err ) err = traverse_xiso( xiso, nil, (xoff_t) root_dir_sect * XISO_SECTOR_SIZE + s_xbox_disc_lseek, buf, in_mode, nil, in_ll_compat ); 1166 | } 1167 | 1168 | free( buf ); 1169 | } 1170 | } 1171 | 1172 | if ( err == err_iso_rewritten ) err = 0; 1173 | if ( err ) misc_err( "failed to %s xbox iso image %s\n", in_mode == k_rewrite ? "rewrite" : in_mode == k_extract ? "extract" : "list", name, 0 ); 1174 | 1175 | if ( xiso != -1 ) close( xiso ); 1176 | 1177 | if ( short_name ) free( short_name ); 1178 | if ( cwd ) { 1179 | chdir( cwd ); 1180 | free( cwd ); 1181 | } 1182 | 1183 | if ( repair ) in_xiso[ strlen( in_xiso ) ] = '.'; 1184 | 1185 | return err; 1186 | } 1187 | 1188 | 1189 | int traverse_xiso( int in_xiso, dir_node *in_dir_node, xoff_t in_dir_start, char *in_path, modes in_mode, dir_node_avl **in_root, bool in_ll_compat ) { 1190 | dir_node_avl *avl; 1191 | char *path; 1192 | xoff_t curpos; 1193 | dir_node subdir; 1194 | dir_node *dir, node; 1195 | int err = 0, sector; 1196 | uint16_t l_offset = 0, tmp; 1197 | 1198 | if ( in_dir_node == nil ) in_dir_node = &node; 1199 | 1200 | memset( dir = in_dir_node, 0, sizeof(dir_node) ); 1201 | 1202 | read_entry: 1203 | 1204 | if ( ! err && read( in_xiso, &tmp, XISO_TABLE_OFFSET_SIZE ) != XISO_TABLE_OFFSET_SIZE ) read_err(); 1205 | 1206 | if ( ! err ) { 1207 | if ( tmp == XISO_PAD_SHORT ) { 1208 | if ( l_offset == 0 ) { // Directory is empty 1209 | if (in_mode == k_generate_avl) { 1210 | avl_insert(in_root, EMPTY_SUBDIRECTORY); 1211 | } 1212 | goto end_traverse; 1213 | } 1214 | 1215 | l_offset = l_offset * XISO_DWORD_SIZE + ( XISO_SECTOR_SIZE - ( l_offset * XISO_DWORD_SIZE ) % XISO_SECTOR_SIZE ); 1216 | err = lseek( in_xiso, in_dir_start + (xoff_t) l_offset, SEEK_SET ) == -1 ? 1 : 0; 1217 | 1218 | if ( ! err ) goto read_entry; // me and my silly comments 1219 | } else { 1220 | l_offset = tmp; 1221 | } 1222 | } 1223 | 1224 | if ( ! err && read( in_xiso, &dir->r_offset, XISO_TABLE_OFFSET_SIZE ) != XISO_TABLE_OFFSET_SIZE ) read_err(); 1225 | if ( ! err && read( in_xiso, &dir->start_sector, XISO_SECTOR_OFFSET_SIZE ) != XISO_SECTOR_OFFSET_SIZE ) read_err(); 1226 | if ( ! err && read( in_xiso, &dir->file_size, XISO_FILESIZE_SIZE ) != XISO_FILESIZE_SIZE ) read_err(); 1227 | if ( ! err && read( in_xiso, &dir->attributes, XISO_ATTRIBUTES_SIZE ) != XISO_ATTRIBUTES_SIZE ) read_err(); 1228 | if ( ! err && read( in_xiso, &dir->filename_length, XISO_FILENAME_LENGTH_SIZE ) != XISO_FILENAME_LENGTH_SIZE ) read_err(); 1229 | 1230 | if ( ! err ) { 1231 | little16( l_offset ); 1232 | little16( dir->r_offset ); 1233 | little32( dir->file_size ); 1234 | little32( dir->start_sector ); 1235 | 1236 | if ( ( dir->filename = (char *) malloc( dir->filename_length + 1 ) ) == nil ) mem_err(); 1237 | } 1238 | 1239 | if ( ! err ) { 1240 | if ( read( in_xiso, dir->filename, dir->filename_length ) != dir->filename_length ) read_err(); 1241 | if ( ! err ) { 1242 | dir->filename[ dir->filename_length ] = 0; 1243 | 1244 | // security patch (Chris Bainbridge), modified by in to support "...", etc. 02.14.06 (in) 1245 | if ( ! strcmp( dir->filename, "." ) || ! strcmp( dir->filename, ".." ) || strchr( dir->filename, '/' ) || strchr( dir->filename, '\\' ) ) { 1246 | log_err( __FILE__, __LINE__, "filename '%s' contains invalid character(s), aborting.", dir->filename ); 1247 | exit( 1 ); 1248 | } 1249 | } 1250 | } 1251 | 1252 | if ( ! err && in_mode == k_generate_avl ) { 1253 | if ( ( avl = (dir_node_avl *) malloc( sizeof(dir_node_avl) ) ) == nil ) mem_err(); 1254 | if ( ! err ) { 1255 | memset( avl, 0, sizeof(dir_node_avl) ); 1256 | 1257 | if ( ( avl->filename = strdup( dir->filename ) ) == nil ) mem_err(); 1258 | } 1259 | if ( ! err ) { 1260 | dir->avl_node = avl; 1261 | 1262 | avl->file_size = dir->file_size; 1263 | avl->old_start_sector = dir->start_sector; 1264 | 1265 | if ( avl_insert( in_root, avl ) == k_avl_error ) misc_err( "this iso appears to be corrupt\n", 0, 0, 0 ); 1266 | } 1267 | } 1268 | 1269 | if ( ! err && l_offset ) { 1270 | in_ll_compat = false; 1271 | 1272 | if ( ( dir->left = (dir_node *) malloc( sizeof(dir_node) ) ) == nil ) mem_err(); 1273 | if ( ! err ) { 1274 | memset( dir->left, 0, sizeof(dir_node) ); 1275 | if ( lseek( in_xiso, in_dir_start + (xoff_t) l_offset * XISO_DWORD_SIZE, SEEK_SET ) == -1 ) seek_err(); 1276 | } 1277 | if ( ! err ) { 1278 | dir->left->parent = dir; 1279 | dir = dir->left; 1280 | 1281 | goto read_entry; 1282 | } 1283 | } 1284 | 1285 | left_processed: 1286 | 1287 | if ( dir->left ) { free( dir->left ); dir->left = nil; } 1288 | 1289 | if ( ! err && ( curpos = lseek( in_xiso, 0, SEEK_CUR ) ) == -1 ) seek_err(); 1290 | 1291 | if ( ! err ) { 1292 | if ( dir->attributes & XISO_ATTRIBUTE_DIR ) { 1293 | if ( in_path ) { 1294 | if ( ( path = (char *) malloc( strlen( in_path ) + dir->filename_length + 2 ) ) == nil ) mem_err(); 1295 | 1296 | if ( ! err ) { 1297 | sprintf( path, "%s%s%c", in_path, dir->filename, PATH_CHAR ); 1298 | if ( lseek( in_xiso, (xoff_t) dir->start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek, SEEK_SET ) == -1 ) seek_err(); 1299 | } 1300 | } else path = nil; 1301 | 1302 | if ( ! err ) { 1303 | if ( !s_remove_systemupdate || !strstr( dir->filename, s_systemupdate ) ) 1304 | { 1305 | if ( in_mode == k_extract ) { 1306 | if ( ( err = mkdir( dir->filename, 0755 ) ) ) mkdir_err( dir->filename ); 1307 | if ( ! err && ( err = chdir( dir->filename ) ) ) chdir_err( dir->filename ); 1308 | } 1309 | if( ! err && in_mode != k_generate_avl ) { 1310 | exiso_log("%s%s%s%s (0 bytes)%s", in_mode == k_extract ? "creating " : "", in_path, dir->filename, PATH_CHAR_STR, in_mode == k_extract ? " [OK]" : ""); flush(); 1311 | exiso_log("\n"); 1312 | } 1313 | } 1314 | } 1315 | 1316 | if ( ! err ) { 1317 | memcpy( &subdir, dir, sizeof(dir_node) ); 1318 | 1319 | subdir.parent = nil; 1320 | if ( ! err && dir->file_size > 0 ) err = traverse_xiso( in_xiso, &subdir, (xoff_t) dir->start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek, path, in_mode, in_mode == k_generate_avl ? &dir->avl_node->subdirectory : nil, in_ll_compat ); 1321 | 1322 | if ( !s_remove_systemupdate || !strstr( dir->filename, s_systemupdate ) ) 1323 | { 1324 | 1325 | if ( ! err && in_mode == k_extract && ( err = chdir( ".." ) ) ) chdir_err( ".." ); 1326 | } 1327 | } 1328 | 1329 | if ( path ) free( path ); 1330 | } else if ( in_mode != k_generate_avl ) { 1331 | if ( ! err ) { 1332 | if ( !s_remove_systemupdate || !strstr( in_path, s_systemupdate ) ) 1333 | { 1334 | 1335 | if ( in_mode == k_extract ) { 1336 | err = extract_file( in_xiso, dir, in_mode, in_path ); 1337 | } else { 1338 | exiso_log( "%s%s%s (%u bytes)%s", in_mode == k_extract ? "extracting " : "", in_path, dir->filename, dir->file_size , "" ); flush(); 1339 | exiso_log( "\n" ); 1340 | } 1341 | 1342 | ++s_total_files; 1343 | ++s_total_files_all_isos; 1344 | s_total_bytes += dir->file_size; 1345 | s_total_bytes_all_isos += dir->file_size; 1346 | } 1347 | } 1348 | } 1349 | } 1350 | 1351 | if ( ! err && dir->r_offset ) { 1352 | // compatibility for iso's built as linked lists (bleh!) 1353 | if ( in_ll_compat && (xoff_t) dir->r_offset * XISO_DWORD_SIZE / XISO_SECTOR_SIZE > ( sector = (int) (( curpos - in_dir_start ) / XISO_SECTOR_SIZE )) ) dir->r_offset = sector * ( XISO_SECTOR_SIZE / XISO_DWORD_SIZE ) + ( XISO_SECTOR_SIZE / XISO_DWORD_SIZE ); 1354 | 1355 | if ( ! err && lseek( in_xiso, in_dir_start + (xoff_t) dir->r_offset * XISO_DWORD_SIZE, SEEK_SET ) == -1 ) seek_err(); 1356 | if ( ! err ) { 1357 | if ( dir->filename ) { free( dir->filename ); dir->filename = nil; } 1358 | 1359 | l_offset = dir->r_offset; 1360 | 1361 | goto read_entry; 1362 | } 1363 | } 1364 | 1365 | end_traverse: 1366 | 1367 | if ( dir->filename ) free( dir->filename ); 1368 | 1369 | if ( ( dir = dir->parent ) ) goto left_processed; 1370 | 1371 | return err; 1372 | } 1373 | 1374 | 1375 | #if 0 1376 | #pragma mark - 1377 | #endif 1378 | 1379 | 1380 | dir_node_avl *avl_fetch( dir_node_avl *in_root, char *in_filename ) { 1381 | int result; 1382 | 1383 | for ( ;; ) { 1384 | if ( in_root == nil ) return nil; 1385 | 1386 | result = avl_compare_key( in_filename, in_root->filename ); 1387 | 1388 | if ( result < 0 ) in_root = in_root->left; 1389 | else if ( result > 0 ) in_root = in_root->right; 1390 | else return in_root; 1391 | } 1392 | } 1393 | 1394 | 1395 | avl_result avl_insert( dir_node_avl **in_root, dir_node_avl *in_node ) { 1396 | avl_result tmp; 1397 | int result; 1398 | 1399 | if ( *in_root == nil ) { *in_root = in_node; return k_avl_balanced; } 1400 | 1401 | result = avl_compare_key( in_node->filename, (*in_root)->filename ); 1402 | 1403 | if ( result < 0 ) return ( tmp = avl_insert( &(*in_root)->left, in_node ) ) == k_avl_balanced ? avl_left_grown( in_root ) : tmp; 1404 | if ( result > 0 ) return ( tmp = avl_insert( &(*in_root)->right, in_node ) ) == k_avl_balanced ? avl_right_grown( in_root ) : tmp; 1405 | 1406 | return k_avl_error; 1407 | } 1408 | 1409 | 1410 | avl_result avl_left_grown( dir_node_avl **in_root ) { 1411 | switch ( (*in_root)->skew ) { 1412 | case k_left_skew: { 1413 | if ( (*in_root)->left->skew == k_left_skew ) { 1414 | (*in_root)->skew = (*in_root)->left->skew = k_no_skew; 1415 | avl_rotate_right( in_root ); 1416 | } else { 1417 | switch ( (*in_root)->left->right->skew ) { 1418 | case k_left_skew: { 1419 | (*in_root)->skew = k_right_skew; 1420 | (*in_root)->left->skew = k_no_skew; 1421 | } break; 1422 | 1423 | case k_right_skew: { 1424 | (*in_root)->skew = k_no_skew; 1425 | (*in_root)->left->skew = k_left_skew; 1426 | } break; 1427 | 1428 | default: { 1429 | (*in_root)->skew = k_no_skew; 1430 | (*in_root)->left->skew = k_no_skew; 1431 | } break; 1432 | } 1433 | (*in_root)->left->right->skew = k_no_skew; 1434 | avl_rotate_left( &(*in_root)->left ); 1435 | avl_rotate_right( in_root ); 1436 | } 1437 | } return no_err; 1438 | 1439 | case k_right_skew: { 1440 | (*in_root)->skew = k_no_skew; 1441 | } return no_err; 1442 | 1443 | default: { 1444 | (*in_root)->skew = k_left_skew; 1445 | } return k_avl_balanced; 1446 | } 1447 | } 1448 | 1449 | 1450 | avl_result avl_right_grown( dir_node_avl **in_root ) { 1451 | switch ( (*in_root)->skew ) { 1452 | case k_left_skew: { 1453 | (*in_root)->skew = k_no_skew; 1454 | } return no_err; 1455 | 1456 | case k_right_skew: { 1457 | if ( (*in_root)->right->skew == k_right_skew ) { 1458 | (*in_root)->skew = (*in_root)->right->skew = k_no_skew; 1459 | avl_rotate_left( in_root ); 1460 | } else { 1461 | switch ( (*in_root)->right->left->skew ) { 1462 | case k_left_skew: { 1463 | (*in_root)->skew = k_no_skew; 1464 | (*in_root)->right->skew = k_right_skew; 1465 | } break; 1466 | 1467 | case k_right_skew: { 1468 | (*in_root)->skew = k_left_skew; 1469 | (*in_root)->right->skew = k_no_skew; 1470 | } break; 1471 | 1472 | default: { 1473 | (*in_root)->skew = k_no_skew; 1474 | (*in_root)->right->skew = k_no_skew; 1475 | } break; 1476 | } 1477 | (*in_root)->right->left->skew = k_no_skew; 1478 | avl_rotate_right( &(*in_root)->right ); 1479 | avl_rotate_left( in_root ); 1480 | } 1481 | } return no_err; 1482 | 1483 | default: { 1484 | (*in_root)->skew = k_right_skew; 1485 | } return k_avl_balanced; 1486 | } 1487 | } 1488 | 1489 | 1490 | void avl_rotate_left( dir_node_avl **in_root ) { 1491 | dir_node_avl *tmp = *in_root; 1492 | 1493 | *in_root = (*in_root)->right; 1494 | tmp->right = (*in_root)->left; 1495 | (*in_root)->left = tmp; 1496 | } 1497 | 1498 | 1499 | void avl_rotate_right( dir_node_avl **in_root ) { 1500 | dir_node_avl *tmp = *in_root; 1501 | 1502 | *in_root = (*in_root)->left; 1503 | tmp->left = (*in_root)->right; 1504 | (*in_root)->right = tmp; 1505 | } 1506 | 1507 | 1508 | int avl_compare_key( char *in_lhs, char *in_rhs ) { 1509 | char a, b; 1510 | 1511 | for ( ;; ) { 1512 | a = *in_lhs++; 1513 | b = *in_rhs++; 1514 | 1515 | if ( a >= 'a' && a <= 'z' ) a -= 32; // uppercase(a); 1516 | if ( b >= 'a' && b <= 'z' ) b -= 32; // uppercase(b); 1517 | 1518 | if ( a ) { 1519 | if ( b ) { 1520 | if ( a < b ) return -1; 1521 | if ( a > b ) return 1; 1522 | } else return 1; 1523 | } else return b ? -1 : 0; 1524 | } 1525 | } 1526 | 1527 | 1528 | int avl_traverse_depth_first( dir_node_avl *in_root, traversal_callback in_callback, void *in_context, avl_traversal_method in_method, long in_depth ) { 1529 | int err; 1530 | 1531 | if ( in_root == nil ) return 0; 1532 | 1533 | switch ( in_method ) { 1534 | case k_prefix: { 1535 | err = (*in_callback)( in_root, in_context, in_depth ); 1536 | if ( ! err ) err = avl_traverse_depth_first( in_root->left, in_callback, in_context, in_method, in_depth + 1 ); 1537 | if ( ! err ) err = avl_traverse_depth_first( in_root->right, in_callback, in_context, in_method, in_depth + 1 ); 1538 | } break; 1539 | 1540 | case k_infix: { 1541 | err = avl_traverse_depth_first( in_root->left, in_callback, in_context, in_method, in_depth + 1 ); 1542 | if ( ! err ) err = (*in_callback)( in_root, in_context, in_depth ); 1543 | if ( ! err ) err = avl_traverse_depth_first( in_root->right, in_callback, in_context, in_method, in_depth + 1 ); 1544 | } break; 1545 | 1546 | case k_postfix: { 1547 | err = avl_traverse_depth_first( in_root->left, in_callback, in_context, in_method, in_depth + 1 ); 1548 | if ( ! err ) err = avl_traverse_depth_first( in_root->right, in_callback, in_context, in_method, in_depth + 1 ); 1549 | if ( ! err ) err = (*in_callback)( in_root, in_context, in_depth ); 1550 | } break; 1551 | 1552 | default: err = 0; break; 1553 | } 1554 | 1555 | return err; 1556 | } 1557 | 1558 | 1559 | #if 0 1560 | #pragma mark - 1561 | #endif 1562 | 1563 | 1564 | int boyer_moore_init( char *in_pattern, long in_pat_len, long in_alphabet_size ) { 1565 | long i, j, k, *backup, err = 0; 1566 | 1567 | s_pattern = in_pattern; 1568 | s_pat_len = in_pat_len; 1569 | 1570 | if ( ( s_bc_table = (long *) malloc( in_alphabet_size * sizeof(long) ) ) == nil ) mem_err(); 1571 | 1572 | if ( ! err ) { 1573 | for ( i = 0; i < in_alphabet_size; ++i ) s_bc_table[ i ] = in_pat_len; 1574 | for ( i = 0; i < in_pat_len - 1; ++i ) s_bc_table[ (uint8_t) in_pattern[ i ] ] = in_pat_len - i - 1; 1575 | 1576 | if ( ( s_gs_table = (long *) malloc( 2 * ( in_pat_len + 1 ) * sizeof(long) ) ) == nil ) mem_err(); 1577 | } 1578 | 1579 | if ( ! err ) { 1580 | backup = s_gs_table + in_pat_len + 1; 1581 | 1582 | for ( i = 1; i <= in_pat_len; ++i ) s_gs_table[ i ] = 2 * in_pat_len - i; 1583 | for ( i = in_pat_len, j = in_pat_len + 1; i; --i, --j ) { 1584 | backup[ i ] = j; 1585 | 1586 | while ( j <= in_pat_len && in_pattern[ i - 1 ] != in_pattern[ j - 1 ] ) { 1587 | if ( s_gs_table[ j ] > in_pat_len - i ) s_gs_table[ j ] = in_pat_len - i; 1588 | j = backup[ j ]; 1589 | } 1590 | } 1591 | for ( i = 1; i <= j; ++i ) if ( s_gs_table[ i ] > in_pat_len + j - i ) s_gs_table[ i ] = in_pat_len + j - i; 1592 | 1593 | k = backup[ j ]; 1594 | 1595 | for ( ; j <= in_pat_len; k = backup[ k ] ) { 1596 | for ( ; j <= k; ++j ) if ( s_gs_table[ j ] >= k - j + in_pat_len ) s_gs_table[ j ] = k - j + in_pat_len; 1597 | } 1598 | } 1599 | 1600 | return err; 1601 | } 1602 | 1603 | 1604 | void boyer_moore_done() { 1605 | if ( s_bc_table ) { free( s_bc_table ); s_bc_table = nil; } 1606 | if ( s_gs_table ) { free( s_gs_table ); s_gs_table = nil; } 1607 | } 1608 | 1609 | 1610 | char *boyer_moore_search( char *in_text, long in_text_len ) { 1611 | long i, j, k, l; 1612 | 1613 | for ( i = j = s_pat_len - 1; j < in_text_len && i >= 0; ) { 1614 | if ( in_text[ j ] == s_pattern[ i ] ) { --i; --j; } 1615 | else { 1616 | k = s_gs_table[ i + 1 ]; 1617 | l = s_bc_table[ (uint8_t) in_text[ j ] ]; 1618 | 1619 | j += max( k, l ); 1620 | 1621 | i = s_pat_len - 1; 1622 | } 1623 | } 1624 | 1625 | return i < 0 ? in_text + j + 1 : nil; 1626 | } 1627 | 1628 | 1629 | #if 0 1630 | #pragma mark - 1631 | #endif 1632 | 1633 | 1634 | int extract_file( int in_xiso, dir_node *in_file, modes in_mode , char* path) { 1635 | int err = 0; 1636 | bool warn = false; 1637 | uint32_t i, size, read_size, totalsize = 0, totalpercent = 0; 1638 | int out; 1639 | 1640 | if ( s_remove_systemupdate && strstr( path, s_systemupdate ) ){ 1641 | if ( ! err && lseek( in_xiso, (xoff_t) in_file->start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek, SEEK_SET ) == -1 ) seek_err(); 1642 | } 1643 | else { 1644 | if ( in_mode == k_extract ) { 1645 | if ( ( out = open( in_file->filename, WRITEFLAGS, 0644 ) ) == -1 ) open_err( in_file->filename ); 1646 | } else err = 1; 1647 | 1648 | if ( ! err && lseek( in_xiso, (xoff_t) in_file->start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek, SEEK_SET ) == -1 ) seek_err(); 1649 | 1650 | if ( ! err ) { 1651 | if (in_file->file_size == 0) { 1652 | exiso_log("%s%s%s (0 bytes) [100%%]%s\r", in_mode == k_extract ? "extracting " : "", path, in_file->filename, ""); 1653 | } 1654 | else { 1655 | i = 0; 1656 | size = min(in_file->file_size, READWRITE_BUFFER_SIZE); 1657 | do { 1658 | if ((int)(read_size = read(in_xiso, s_copy_buffer, size)) < 0) { 1659 | read_err(); 1660 | break; 1661 | } 1662 | if (in_mode == k_extract && read_size != 0) { 1663 | if (write(out, s_copy_buffer, read_size) != (int)read_size) { 1664 | write_err(); 1665 | break; 1666 | } 1667 | } 1668 | totalsize += read_size; 1669 | totalpercent = (totalsize * 100.0) / in_file->file_size; 1670 | exiso_log("%s%s%s (%u bytes) [%u%%]%s\r", in_mode == k_extract ? "extracting " : "", path, in_file->filename, in_file->file_size, totalpercent, ""); 1671 | 1672 | i += read_size; 1673 | size = min(in_file->file_size - i, READWRITE_BUFFER_SIZE); 1674 | } while (i < in_file->file_size && read_size > 0); 1675 | if (!err && i < in_file->file_size) { 1676 | exiso_log("\nWARNING: File %s is truncated. Reported size: %u bytes, read size: %u bytes!", in_file->filename, in_file->file_size, i); 1677 | in_file->file_size = i; 1678 | } 1679 | } 1680 | if (in_mode == k_extract) close(out); 1681 | } 1682 | } 1683 | 1684 | if ( ! err ) exiso_log( "\n" ); 1685 | 1686 | return err; 1687 | } 1688 | 1689 | 1690 | int free_dir_node_avl( void *in_dir_node_avl, void *in_context, long in_depth ) { 1691 | dir_node_avl *avl = (dir_node_avl *) in_dir_node_avl; 1692 | 1693 | if ( avl->subdirectory && avl->subdirectory != EMPTY_SUBDIRECTORY ) avl_traverse_depth_first( avl->subdirectory, free_dir_node_avl, nil, k_postfix, 0 ); 1694 | 1695 | free( avl->filename ); 1696 | free( avl ); 1697 | 1698 | return 0; 1699 | } 1700 | 1701 | 1702 | int write_tree( dir_node_avl *in_avl, write_tree_context *in_context, int in_depth ) { 1703 | xoff_t pos; 1704 | write_tree_context context; 1705 | int err = 0, pad; 1706 | char sector[ XISO_SECTOR_SIZE ]; 1707 | 1708 | if ( in_avl->subdirectory ) { 1709 | if ( in_context->path ) { if ( asprintf( &context.path, "%s%s%c", in_context->path, in_avl->filename, PATH_CHAR ) == -1 ) mem_err(); } 1710 | else { if ( asprintf( &context.path, "%c", PATH_CHAR ) == -1 ) mem_err(); } 1711 | 1712 | if ( ! err ) { 1713 | exiso_log( "adding %s (0 bytes) [OK]\n", context.path ); 1714 | 1715 | if ( in_avl->subdirectory != EMPTY_SUBDIRECTORY ) { 1716 | context.xiso = in_context->xiso; 1717 | context.from = in_context->from; 1718 | context.progress = in_context->progress; 1719 | context.final_bytes = in_context->final_bytes; 1720 | 1721 | if ( in_context->from == -1 ) { 1722 | if ( chdir( in_avl->filename ) == -1 ) chdir_err( in_avl->filename ); 1723 | } 1724 | 1725 | if ( ! err ) err = avl_traverse_depth_first( in_avl->subdirectory, (traversal_callback) write_file, &context, k_prefix, 0 ); 1726 | if ( ! err ) err = avl_traverse_depth_first( in_avl->subdirectory, (traversal_callback) write_tree, &context, k_prefix, 0 ); 1727 | 1728 | if (!err && lseek(in_context->xiso, (xoff_t)in_avl->start_sector * XISO_SECTOR_SIZE, SEEK_SET) == -1) seek_err(); 1729 | if (!err) err = avl_traverse_depth_first(in_avl->subdirectory, (traversal_callback)write_directory, (void*)in_context->xiso, k_prefix, 0); 1730 | if (!err && (pos = lseek(in_context->xiso, 0, SEEK_CUR)) == -1) seek_err(); 1731 | if (!err && (pad = (int)((XISO_SECTOR_SIZE - (pos % XISO_SECTOR_SIZE)) % XISO_SECTOR_SIZE))) { 1732 | memset(sector, XISO_PAD_BYTE, pad); 1733 | if (write(in_context->xiso, sector, pad) != pad) write_err(); 1734 | } 1735 | 1736 | if ( ! err && in_context->from == -1 ) { 1737 | if ( chdir( ".." ) == -1 ) chdir_err( ".." ); 1738 | } 1739 | 1740 | if ( context.path ) free( context.path ); 1741 | } else { 1742 | memset(sector, XISO_PAD_BYTE, XISO_SECTOR_SIZE); 1743 | if ((pos = lseek(in_context->xiso, in_avl->start_sector * XISO_SECTOR_SIZE, SEEK_SET)) == -1) seek_err(); 1744 | if (!err && write(in_context->xiso, sector, XISO_SECTOR_SIZE) != XISO_SECTOR_SIZE) write_err(); 1745 | } 1746 | } 1747 | } 1748 | 1749 | return err; 1750 | } 1751 | 1752 | 1753 | int write_file( dir_node_avl *in_avl, write_tree_context *in_context, int in_depth ) { 1754 | char *buf, *p; 1755 | uint32_t bytes, n, size; 1756 | int err = 0, fd = -1, i; 1757 | size_t len; 1758 | 1759 | if ( ! in_avl->subdirectory ) { 1760 | if ( lseek( in_context->xiso, (xoff_t) in_avl->start_sector * XISO_SECTOR_SIZE, SEEK_SET ) == -1 ) seek_err(); 1761 | 1762 | if ( ! err && ( buf = (char *) malloc( ( size = max( XISO_SECTOR_SIZE, READWRITE_BUFFER_SIZE ) ) + 1 ) ) == nil ) mem_err(); 1763 | if ( ! err ) { 1764 | if ( in_context->from == -1 ) { 1765 | if ( ( fd = open( in_avl->filename, READFLAGS, 0 ) ) == -1 ) open_err( in_avl->filename ); 1766 | } else { 1767 | if ( lseek( fd = in_context->from, (xoff_t) in_avl->old_start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek, SEEK_SET ) == -1 ) seek_err(); 1768 | } 1769 | } 1770 | 1771 | if ( ! err ) { 1772 | exiso_log( "adding %s%s (%u bytes) ", in_context->path, in_avl->filename, in_avl->file_size ); flush(); 1773 | 1774 | i = 0; 1775 | bytes = in_avl->file_size; 1776 | do { 1777 | if ((int)(n = read(fd, buf + i, min(bytes, size - i))) < 0) { 1778 | read_err(); 1779 | break; 1780 | } 1781 | if (n == 0) { 1782 | if (i) { 1783 | if (write(in_context->xiso, buf, i) != i) { 1784 | write_err(); 1785 | break; 1786 | } 1787 | } 1788 | break; 1789 | } 1790 | bytes -= n; 1791 | if (s_media_enable && (len = strlen(in_avl->filename)) >= 4 && strcasecmp(&in_avl->filename[len - 4], ".xbe") == 0) { 1792 | for (buf[n += i] = 0, p = buf; (p = boyer_moore_search(p, n - (p - buf))) != nil; p += XISO_MEDIA_ENABLE_LENGTH) p[XISO_MEDIA_ENABLE_BYTE_POS] = XISO_MEDIA_ENABLE_BYTE; 1793 | if (bytes) { 1794 | i = XISO_MEDIA_ENABLE_LENGTH - 1; 1795 | if (write(in_context->xiso, buf, n - i) != (int)n - i) { 1796 | write_err(); 1797 | break; 1798 | } 1799 | memcpy(buf, &buf[n - i], i); 1800 | } 1801 | else { 1802 | if (write(in_context->xiso, buf, n + i) != (int)n + i) { 1803 | write_err(); 1804 | break; 1805 | } 1806 | } 1807 | } 1808 | else { 1809 | if (write(in_context->xiso, buf, n + i) != (int)n + i) { 1810 | write_err(); 1811 | break; 1812 | } 1813 | } 1814 | } while (bytes); 1815 | i = in_avl->file_size; 1816 | in_avl->file_size -= bytes; 1817 | 1818 | if (!err && (bytes = (XISO_SECTOR_SIZE - (in_avl->file_size % XISO_SECTOR_SIZE)) % XISO_SECTOR_SIZE)) { 1819 | memset(buf, XISO_PAD_BYTE, bytes); 1820 | if (write(in_context->xiso, buf, bytes) != (int)bytes) write_err(); 1821 | } 1822 | exiso_log(err ? "failed\n" : "[OK]\n"); 1823 | 1824 | if (!err && i != in_avl->file_size) { 1825 | exiso_log("WARNING: File %s is truncated. Reported size: %u bytes, wrote size: %u bytes!\n", in_avl->filename, i, in_avl->file_size); 1826 | } 1827 | 1828 | if (!err) { 1829 | ++s_total_files; 1830 | s_total_bytes += in_avl->file_size; 1831 | if (in_context->progress) (*in_context->progress)(s_total_bytes, in_context->final_bytes); 1832 | } 1833 | } 1834 | 1835 | if ( in_context->from == -1 && fd != -1 ) close( fd ); 1836 | if ( buf ) free( buf ); 1837 | } 1838 | 1839 | return err; 1840 | } 1841 | 1842 | 1843 | int write_directory( dir_node_avl *in_avl, int in_xiso, int in_depth ) { 1844 | xoff_t pos; 1845 | int err = 0, pad; 1846 | uint16_t l_offset, r_offset; 1847 | uint32_t file_size = in_avl->file_size + (in_avl->subdirectory ? (XISO_SECTOR_SIZE - (in_avl->file_size % XISO_SECTOR_SIZE)) % XISO_SECTOR_SIZE : 0); 1848 | char length = (char) strlen( in_avl->filename ), attributes = in_avl->subdirectory ? XISO_ATTRIBUTE_DIR : XISO_ATTRIBUTE_ARC, sector[ XISO_SECTOR_SIZE ]; 1849 | 1850 | little32( in_avl->file_size ); 1851 | little32( in_avl->start_sector ); 1852 | 1853 | l_offset = (uint16_t) (in_avl->left ? in_avl->left->offset / XISO_DWORD_SIZE : 0); 1854 | r_offset = (uint16_t) (in_avl->right ? in_avl->right->offset / XISO_DWORD_SIZE : 0); 1855 | 1856 | little16( l_offset ); 1857 | little16( r_offset ); 1858 | 1859 | memset( sector, XISO_PAD_BYTE, XISO_SECTOR_SIZE ); 1860 | 1861 | if ( ( pos = lseek( in_xiso, 0, SEEK_CUR ) ) == -1 ) seek_err(); 1862 | if ( ! err && ( pad = (int) ( (xoff_t) in_avl->offset + in_avl->dir_start - pos ) ) && write( in_xiso, sector, pad ) != pad ) write_err(); 1863 | if ( ! err && write( in_xiso, &l_offset, XISO_TABLE_OFFSET_SIZE ) != XISO_TABLE_OFFSET_SIZE ) write_err(); 1864 | if ( ! err && write( in_xiso, &r_offset, XISO_TABLE_OFFSET_SIZE ) != XISO_TABLE_OFFSET_SIZE ) write_err(); 1865 | if ( ! err && write( in_xiso, &in_avl->start_sector, XISO_SECTOR_OFFSET_SIZE ) != XISO_SECTOR_OFFSET_SIZE ) write_err(); 1866 | if ( ! err && write( in_xiso, &file_size, XISO_FILESIZE_SIZE ) != XISO_FILESIZE_SIZE ) write_err(); 1867 | if ( ! err && write( in_xiso, &attributes, XISO_ATTRIBUTES_SIZE ) != XISO_ATTRIBUTES_SIZE ) write_err(); 1868 | if ( ! err && write( in_xiso, &length, XISO_FILENAME_LENGTH_SIZE ) != XISO_FILENAME_LENGTH_SIZE ) write_err(); 1869 | if ( ! err && write( in_xiso, in_avl->filename, length ) != length ) write_err(); 1870 | 1871 | little32( in_avl->start_sector ); 1872 | little32( in_avl->file_size ); 1873 | 1874 | return err; 1875 | } 1876 | 1877 | 1878 | int calculate_directory_offsets( dir_node_avl *in_avl, uint32_t *io_current_sector, int in_depth ) { 1879 | wdsafp_context context; 1880 | 1881 | if ( in_avl->subdirectory ) { 1882 | if (in_avl->subdirectory == EMPTY_SUBDIRECTORY) { 1883 | in_avl->start_sector = *io_current_sector; 1884 | *io_current_sector += 1; 1885 | } 1886 | else { 1887 | context.current_sector = io_current_sector; 1888 | context.dir_start = (xoff_t) ( in_avl->start_sector = *io_current_sector ) * XISO_SECTOR_SIZE; 1889 | 1890 | *io_current_sector += n_sectors( in_avl->file_size ); 1891 | 1892 | avl_traverse_depth_first( in_avl->subdirectory, (traversal_callback) write_dir_start_and_file_positions, &context, k_prefix, 0 ); 1893 | avl_traverse_depth_first( in_avl->subdirectory, (traversal_callback) calculate_directory_offsets, io_current_sector, k_prefix, 0 ); 1894 | } 1895 | } 1896 | 1897 | return 0; 1898 | } 1899 | 1900 | 1901 | int write_dir_start_and_file_positions( dir_node_avl *in_avl, wdsafp_context *io_context, int in_depth ) { 1902 | in_avl->dir_start = io_context->dir_start; 1903 | 1904 | if ( ! in_avl->subdirectory ) { 1905 | in_avl->start_sector = *io_context->current_sector; 1906 | *io_context->current_sector += n_sectors( in_avl->file_size ); 1907 | } 1908 | 1909 | return 0; 1910 | } 1911 | 1912 | 1913 | int calculate_total_files_and_bytes( dir_node_avl *in_avl, void *in_context, int in_depth ) { 1914 | if ( in_avl->subdirectory && in_avl->subdirectory != EMPTY_SUBDIRECTORY ) { 1915 | avl_traverse_depth_first( in_avl->subdirectory, (traversal_callback) calculate_total_files_and_bytes, nil, k_prefix, 0 ); 1916 | } else { 1917 | ++s_total_files; 1918 | s_total_bytes += in_avl->file_size; 1919 | } 1920 | 1921 | return 0; 1922 | } 1923 | 1924 | 1925 | int calculate_directory_requirements( dir_node_avl *in_avl, void *in_context, int in_depth ) { 1926 | if ( in_avl->subdirectory ) { 1927 | if (in_avl->subdirectory != EMPTY_SUBDIRECTORY) { 1928 | avl_traverse_depth_first(in_avl->subdirectory, (traversal_callback)calculate_directory_size, &in_avl->file_size, k_prefix, 0); 1929 | avl_traverse_depth_first(in_avl->subdirectory, (traversal_callback)calculate_directory_requirements, in_context, k_prefix, 0); 1930 | } else { 1931 | in_avl->file_size = XISO_SECTOR_SIZE; 1932 | } 1933 | } 1934 | 1935 | return 0; 1936 | } 1937 | 1938 | 1939 | int calculate_directory_size( dir_node_avl *in_avl, uint32_t *out_size, long in_depth ) { 1940 | uint32_t length; 1941 | 1942 | if ( in_depth == 0 ) *out_size = 0; 1943 | 1944 | length = XISO_FILENAME_OFFSET + strlen( in_avl->filename ); 1945 | length += ( XISO_DWORD_SIZE - ( length % XISO_DWORD_SIZE ) ) % XISO_DWORD_SIZE; 1946 | 1947 | if ( n_sectors( *out_size + length ) > n_sectors( *out_size ) ) { 1948 | *out_size += ( XISO_SECTOR_SIZE - ( *out_size % XISO_SECTOR_SIZE ) ) % XISO_SECTOR_SIZE; 1949 | } 1950 | 1951 | in_avl->offset = *out_size; 1952 | 1953 | *out_size += length; 1954 | 1955 | return 0; 1956 | } 1957 | 1958 | 1959 | int generate_avl_tree_local( dir_node_avl **out_root, int *io_n ) { 1960 | struct dirent *p; 1961 | struct stat sb; 1962 | dir_node_avl *avl; 1963 | DIR *dir = nil; 1964 | int err = 0, i, j; 1965 | bool empty_dir = true; 1966 | 1967 | if ( ( dir = opendir( "." ) ) == nil ) mem_err(); 1968 | 1969 | while ( ! err && ( p = readdir( dir ) ) != nil ) { 1970 | if ( ! strcmp( p->d_name, "." ) || ! strcmp( p->d_name, ".." ) ) continue; 1971 | 1972 | for ( i = *io_n; i; --i ) exiso_log( "\b" ); 1973 | exiso_log( "%s", p->d_name ); 1974 | for ( j = i = (int) strlen( p->d_name ); j < *io_n; ++j ) exiso_log( " " ); 1975 | for ( j = i; j < *io_n; ++j ) exiso_log( "\b" ); 1976 | *io_n = i; 1977 | flush(); 1978 | 1979 | if ( ( avl = (dir_node_avl *) malloc( sizeof(dir_node_avl) ) ) == nil ) mem_err(); 1980 | if ( ! err ) { 1981 | memset( avl, 0, sizeof(dir_node_avl) ); 1982 | if ( ( avl->filename = strdup( p->d_name ) ) == nil ) mem_err(); 1983 | } 1984 | if ( ! err && stat( avl->filename, &sb ) == -1 ) read_err(); 1985 | if ( ! err ) { 1986 | if ( S_ISDIR( sb.st_mode ) ) { 1987 | empty_dir = false; 1988 | 1989 | if ( chdir( avl->filename ) == -1 ) chdir_err( avl->filename ); 1990 | 1991 | if ( ! err ) err = generate_avl_tree_local( &avl->subdirectory, io_n ); 1992 | if ( ! err && chdir( ".." ) == -1 ) chdir_err( ".." ); 1993 | } else if ( S_ISREG( sb.st_mode ) ) { 1994 | empty_dir = false; 1995 | if ( sb.st_size > ULONG_MAX ) { 1996 | log_err( __FILE__, __LINE__, "file %s is too large for xiso, skipping...\n", avl->filename ); 1997 | free( avl->filename ); 1998 | free( avl ); 1999 | continue; 2000 | } 2001 | s_total_bytes += avl->file_size = (uint32_t) sb.st_size; 2002 | ++s_total_files; 2003 | } else { 2004 | free( avl->filename ); 2005 | free( avl ); 2006 | continue; 2007 | } 2008 | } 2009 | if ( ! err ) { 2010 | if ( avl_insert( out_root, avl ) == k_avl_error ) misc_err( "error inserting file %s into tree (duplicate filename?)\n", avl->filename, 0, 0 ); 2011 | } else { 2012 | if ( avl ) { 2013 | if ( avl->filename ) free( avl->filename ); 2014 | free( avl ); 2015 | } 2016 | } 2017 | } 2018 | 2019 | if ( empty_dir ) *out_root = EMPTY_SUBDIRECTORY; 2020 | 2021 | if ( dir ) closedir( dir ); 2022 | 2023 | return err; 2024 | } 2025 | 2026 | 2027 | FILE_TIME *alloc_filetime_now( void ) { 2028 | FILE_TIME *ft; 2029 | double tmp; 2030 | time_t now; 2031 | int err = 0; 2032 | 2033 | if ( ( ft = (FILE_TIME *) malloc( sizeof(struct FILE_TIME) ) ) == nil ) mem_err(); 2034 | if ( ! err && ( now = time( nil ) ) == -1 ) unknown_err(); 2035 | if ( ! err ) { 2036 | tmp = ( (double) now + ( 369.0 * 365.25 * 24 * 60 * 60 - ( 3.0 * 24 * 60 * 60 + 6.0 * 60 * 60 ) ) ) * 1.0e7; 2037 | 2038 | ft->h = (uint32_t) ( tmp * ( 1.0 / ( 4.0 * (double) ( 1 << 30 ) ) ) ); 2039 | ft->l = (uint32_t) ( tmp - ( (double) ft->h ) * 4.0 * (double) ( 1 << 30 ) ); 2040 | 2041 | little32( ft->h ); // convert to little endian here because this is a PC only struct and we won't read it anyway 2042 | little32( ft->l ); 2043 | } else if ( ft ) { 2044 | free( ft ); 2045 | ft = nil; 2046 | } 2047 | 2048 | return ft; 2049 | } 2050 | 2051 | // Found the CD-ROM layout in ECMA-119. Now burning software should correctly 2052 | // detect the format of the xiso and burn it correctly without the user having 2053 | // to specify sector sizes and so on. in 10.29.04 2054 | 2055 | #define ECMA_119_DATA_AREA_START 0x8000 2056 | #define ECMA_119_VOLUME_SPACE_SIZE ( ECMA_119_DATA_AREA_START + 80 ) 2057 | #define ECMA_119_VOLUME_SET_SIZE ( ECMA_119_DATA_AREA_START + 120 ) 2058 | #define ECMA_119_VOLUME_SET_IDENTIFIER ( ECMA_119_DATA_AREA_START + 190 ) 2059 | #define ECMA_119_VOLUME_CREATION_DATE ( ECMA_119_DATA_AREA_START + 813 ) 2060 | 2061 | 2062 | // write_volume_descriptors() assumes that the iso file block from offset 2063 | // 0x8000 to 0x8808 has been zeroed prior to entry. 2064 | 2065 | int write_volume_descriptors( int in_xiso, uint32_t in_total_sectors ) { 2066 | int big, err = 0, little; 2067 | char date[] = "0000000000000000"; 2068 | char spaces[ ECMA_119_VOLUME_CREATION_DATE - ECMA_119_VOLUME_SET_IDENTIFIER ]; 2069 | 2070 | big = little = in_total_sectors; 2071 | 2072 | big32( big ); 2073 | little32( little ); 2074 | 2075 | memset( spaces, 0x20, sizeof(spaces) ); 2076 | 2077 | if ( lseek( in_xiso, ECMA_119_DATA_AREA_START, SEEK_SET ) == -1 ) seek_err(); 2078 | if ( ! err && write( in_xiso, "\x01" "CD001\x01", 7 ) == -1 ) write_err(); 2079 | if ( ! err && lseek( in_xiso, ECMA_119_VOLUME_SPACE_SIZE, SEEK_SET ) == -1 ) seek_err(); 2080 | if ( ! err && write( in_xiso, &little, 4 ) == -1 ) write_err(); 2081 | if ( ! err && write( in_xiso, &big, 4 ) == -1 ) write_err(); 2082 | if ( ! err && lseek( in_xiso, ECMA_119_VOLUME_SET_SIZE, SEEK_SET ) == -1 ) seek_err(); 2083 | if ( ! err && write( in_xiso, "\x01\x00\x00\x01\x01\x00\x00\x01\x00\x08\x08\x00", 12 ) == -1 ) write_err(); 2084 | if ( ! err && lseek( in_xiso, ECMA_119_VOLUME_SET_IDENTIFIER, SEEK_SET ) == -1 ) seek_err(); 2085 | if ( ! err && write( in_xiso, spaces, sizeof(spaces) ) == -1 ) write_err(); 2086 | if ( ! err && write( in_xiso, date, sizeof(date) ) == -1 ) write_err(); 2087 | if ( ! err && write( in_xiso, date, sizeof(date) ) == -1 ) write_err(); 2088 | if ( ! err && write( in_xiso, date, sizeof(date) ) == -1 ) write_err(); 2089 | if ( ! err && write( in_xiso, date, sizeof(date) ) == -1 ) write_err(); 2090 | if ( ! err && write( in_xiso, "\x01", 1 ) == -1 ) write_err(); 2091 | if ( ! err && lseek( in_xiso, ECMA_119_DATA_AREA_START + XISO_SECTOR_SIZE, SEEK_SET ) == -1 ) seek_err(); 2092 | if ( ! err && write( in_xiso, "\xff" "CD001\x01", 7 ) == -1 ) write_err(); 2093 | 2094 | return err; 2095 | } 2096 | 2097 | 2098 | #if DEBUG 2099 | 2100 | void write_sector( int in_xiso, xoff_t in_start, char *in_name, char *in_extension ) { 2101 | ssize_t wrote; 2102 | xoff_t curpos; 2103 | int fp = -1, err = 0; 2104 | char *cwd, *sect = nil, buf[ 256 ]; 2105 | 2106 | if ( ( cwd = getcwd( nil, 0 ) ) == nil ) mem_err(); 2107 | if ( ! err && chdir( DEBUG_DUMP_DIRECTORY ) == -1 ) chdir_err( DEBUG_DUMP_DIRECTORY ); 2108 | 2109 | sprintf( buf, "%llu.%s.%s", in_start, in_name, in_extension ? in_extension : "" ); 2110 | 2111 | if ( ! err && ( fp = open( buf, WRITEFLAGS, 0644 ) ) == -1 ) open_err( buf ); 2112 | if ( ! err && ( curpos = lseek( in_xiso, 0, SEEK_CUR ) ) == -1 ) seek_err(); 2113 | if ( ! err && lseek( in_xiso, in_start, SEEK_SET ) == -1 ) seek_err(); 2114 | 2115 | if ( ! err && ( sect = (char *) malloc( XISO_SECTOR_SIZE ) ) == nil ) mem_err(); 2116 | 2117 | if ( ! err && read( in_xiso, sect, XISO_SECTOR_SIZE ) != XISO_SECTOR_SIZE ) read_err(); 2118 | if ( ! err && ( wrote = write( fp, sect, XISO_SECTOR_SIZE ) ) != XISO_SECTOR_SIZE ) write_err(); 2119 | 2120 | if ( ! err && lseek( in_xiso, curpos, SEEK_SET ) == -1 ) seek_err(); 2121 | 2122 | if ( sect ) free( sect ); 2123 | if ( fp != -1 ) close( fp ); 2124 | 2125 | if ( cwd ) { 2126 | if ( chdir( cwd ) == -1 ) chdir_err( cwd ); 2127 | free( cwd ); 2128 | } 2129 | } 2130 | 2131 | #endif 2132 | --------------------------------------------------------------------------------